0

I am trying to do "cd" inside "if" condition in a Bash script. It stays in the same directory after "if". So I have to do "cd" outside "if" and then use $? value in if. Is there a way to avoid using this extra step? What is the shortest possible way of doing it?

See three variants of my code:

#!/bin/bash

set +e

# If I write the following:

if ! (rm -rf sim && mkdir sim && cd sim); then
    echo $0: Cannot prepare simulation directory
    exit 1
fi

# it does not change the current directory

echo $PWD

# Also, if I write the following:

if ! rm -rf sim || ! mkdir sim || ! cd sim; then
    echo $0: Cannot prepare simulation directory
    exit 1
fi

# it also does not change the current directory

echo $PWD

# In order to change the current directory, I need to write:

rm -rf sim && mkdir sim && cd sim

if [ $? -eq 1 ]; then
    echo $0: Cannot prepare simulation directory
    exit 1
fi

# Now it prints .../sim

echo $PWD
cd ..

# Is it possible to write it without an additional line with $?

exit
  • It's the `( )`s, not the `if`. Parethesis create a subshell -- they fork a completely new process with its own variables, active directory, etc. – Charles Duffy Dec 16 '18 at 22:37
  • ...so this is basically the inverse of https://stackoverflow.com/questions/10382141/temporarily-change-current-working-directory-in-bash-to-run-a-command (where they're asking *how to do* the thing you're doing unintentionally). – Charles Duffy Dec 16 '18 at 22:38

1 Answers1

2

Parenthesis in bash create a subshell -- a fork()-ed off copy of the shell with its own environment variables, its own current directory, etc; thus, in your first attempt, the cd took effect only until the closing paren ended the subshell. (POSIX doesn't strictly require this subshell behavior, but it does require that the environment created by parenthesis have its own working directory, so cd's effects will be scoped on all standard-compliant shells, whether or not that shell actually uses a fork() here in all circumstances).

Use curly brackets, not parenthesis, for grouping when you don't want to create a subshell. That is:

if ! { rm -rf sim && mkdir sim && cd sim; }; then
    echo "$0: Cannot prepare simulation directory"
    exit 1
fi

That said, your second approach works perfectly well (though it's unwieldy to write and not a conventional idiom).

bash <<'EOF'
cd /tmp
echo "Starting in $PWD"
if ! rm -rf sim || ! mkdir sim || ! cd sim; then
    echo "$0: Cannot prepare simulation directory"
    exit 1
fi
echo "Ending in $PWD"
EOF

...properly emits:

Starting in /tmp
Ending in /tmp/sim
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • Is the fork a requirement or just an implementation detail? `bash` just says the command creates a new subshell environment, changes to which are not retained after the `(...)` completes (though the working directory is part of that environment, so this is just a small nit I'm picking). – chepner Dec 16 '18 at 23:19
  • Thank you. Is curly bracket a Bash extension? Will is work in every POSIX shell? – Yuri Panchul Dec 17 '18 at 01:11
  • Not an extension; baseline-POSIX. – Charles Duffy Dec 17 '18 at 01:24