Using pushd and popd in shell scripts

Created: Sun Mar 24 18:10:14 CET 2019

Last mod­i­fied: Sun Mar 24 19:34:41 CET 2019


While au­tomat­ing some te­dious task, you might have writ­ten scripts that would of­ten be­gin like this:

cd $PATH_TO_MY_BLOG

and that would of­ten end like this:

cd $OLDPWD # or even ~ or $HOME

I made such scripts to au­to­mat­i­cally up­load my posts to Neocities without al­ways chang­ing di­rec­tory man­u­ally. (The scripts that man­age my blog are in the same di­rec­tory as my posts; not in ~/bin)

The is­sue is that $OLDPWD is not avail­able every­where. For ex­am­ple, it does ex­ist in Bash but is ref­er­ence as $owd in the C Shell (tcsh included).

Also, if your script calls cd sev­eral times or run other scripts which can fail be­fore chang­ing to the old di­rec­tory, you can get unexpected re­sults.

Here is where pushd and popd come in. They are al­most al­ways shell builtins, just like cd(1).

pushd will push the cur­rent di­rec­tory on the di­rec­tory stack and will run cd(1) on its ar­gu­ment.

popd will run cd(1) on the last di­rec­tory that have been pushed and remove it from the stack.

Here is an ex­am­ple case from my own work­flow:

pushd $PATH_TO_MY_BLOG
./custom_scripts/build_last
popd

The build_last script re­quires the cur­rent di­rec­tory to be $PATH_TO_MY_BLOG. Since I usu­ally edit posts from $PATH_TO_MY_BLOG using custom_scripts/edit_post, I don’t re­ally need that one.

Except I do.

Since I’ve learned about pushd and popd I started rewrit­ing all of my aliases.

Basically, this cou­ple of builtins al­lows you to al­ways re­turn to the directory you called the alias from. Now I can synclast and editlast from any place.

But what if your script calls an­other?

Since di­rec­tory are pushed onto a stack, in a FILO (First In Last Out) fash­ion, it means that popd will undo the most re­cent pushd. It means that if a script A that uses this trick calls a script B that also uses the same trick, it will work.

It all works well pro­vided your script does­n’t ter­mi­nate be­fore popd has been called.

I rec­om­mend set­ting up a trap (Bash fea­ture) to trans­par­ently run popd in case of fail­ure or just to leave it as it is and de­bug your script from the di­rec­tory it failed in.

For fur­ther ref­er­ence, I copy-pasted the rel­e­vant parts of csh(1) in a text file.

Don’t hes­i­tate to share ad­vices or sim­i­lar tricks in the com­ment section right be­low.

source code