The choice of the title of your question is a bit confusing.
pushd
/popd
, a csh
feature copied by bash
and zsh
, are a way to manage a stack of remembered directories.
pushd /some/dir
pushes the current working directory onto a stack, and then changes the current working directory (and then prints /some/dir
followed by the content of that stack (space-separated).
popd
prints the content of the stack (again, space separated) and then changes to the top element of the stack and pops it from the stack.
(also beware that some directories will be represented there with their ~/x
or ~user/x
notation).
So if the stack currently has /a
and /b
, the current directory is /here
and you're running:
pushd /tmp/whatever
popd
pushd
will print /tmp/whatever /here /a /b
and popd
will output /here /a /b
, not /tmp/whatever
. That's independent of using command substitution or not. popd
cannot be used to get the path of the previous directory, and in general its output cannot be post processed (see the $dirstack
or $DIRSTACK
array of some shells though for accessing the elements of that directory stack)
Maybe you want:
pushd "$(mktemp -d)" &&
popd &&
rmdir "$OLDPWD"
Or
cd "$(mktemp -d)" &&
cd - &&
rmdir "$OLDPWD"
Though, I'd use:
tmpdir=$(mktemp -d) || exit
(
cd "$tmpdir" || exit # in a subshell
# do what you have to do in that tmpdir
)
rmdir "$tmpdir"
In any case, pushd "$(mktemp -d)"
doesn't run pushd
in a subshell. If it did, it couldn't change the working directory. That's mktemp
that runs in a subshell. Since it is a separate command, it has to run in a separate process. It writes its output on a pipe, and the shell process reads it at the other end of the pipe.
ksh93 can avoid the separate process when the command is builtin, but even there, it's still a subshell (a different working environment) which this time is emulated rather than relying on the separate environment normally provided by forking. For example, in ksh93
, a=0; echo "$(a=1; echo test)"; echo "$a"
, no fork is involved, but still echo "$a"
outputs 0
.
Here, if you want to store the output of mktemp
in a variable, at the same time as you pass it to pushd
, with zsh
, you could do:
pushd ${tmpdir::="$(mktemp -d)"}
With other Bourne-like shells:
unset tmpdir
pushd "${tmpdir=$(mktemp -d)}"
Or to use the output of $(mktemp -d)
several times without explicitly storing it in a variable, you could use zsh
anonymous functions:
(){pushd ${1?} && cd - && rmdir $1} "$(mktemp -d)"
Best Answer
The problem lies in how you're calling the
.
special builtin:In
sh
, if the argument doesn't contain any/
,.
searches for the file in$PATH
. So above, it would look forvars.sh
in$PATH
instead of the current directory as you intended.Also,
.
being a special builtin, its failure causes the shell to exit (when not interactive), so the next command (herefish
) is not executed which is why your terminal emulator window goes away without afish
prompt.That can be prevented by calling
.
ascommand .
which removes the special attribute of special builtins.Note that the behaviour of
bash
(thesh
implementation of the GNU project) is different in that regard when not in POSIX mode (when not called assh
, nor with--posix
, and when the environment doesn't containPOSIXLY_CORRECT=
norSHELLOPTS=posix
):bash
's.
doesn't cause the shell to exit upon failure and it searches for slash-less argument in the current directory if it can't find it in$PATH
.In any case, POSIX mode or not, if you want the
vars.sh
in the current directory, you need the./vars.sh
syntax. So it's