I have a file whose content is the output of dirs -v, i.e. pathnames to directories:

 0  ~/program_files/OS/dirs/recentVisitedDirs
 1  ~/cs/security/computer security/OS security/user management/programs

How would you reproduce the stack of directories, i.e. fetch the pathname for each directory, and apply pushd to it, without modifying the content of the file?

Here is a script which I wrote for that purpose:

while read line; do
    pathname="$(awk '{ $1=""; print}'<<< $line )"
    pushd "$path"
done < "$1"

But when I run

$ myscript ./myfile
/home/t/bin/myscript: line 13: pushd:  /home/t/program_files/OS/dirs/recentVisitedDirs: No such file or directory
/home/t/bin/myscript: line 13: pushd:  /home/t/cs/security/computer security/OS security/user management/programs: No such file or directory

Why do I get the errors?



The issue in your script is that your awk program sets the first field to the empty string. By doing so you do not delete it, and therefore you get a space (the default output field separator) as the first character in your shell variable path. pushd can't find the directories you're feeding it due to the space at the start of the names, and that's where you get the errors from.

Instead, you may do something like

sed 's/^[[:blank:]]*[0-9]*[[:blank:]]*//' <"$1" |
while IFS= read -r pathname; do
    pushd "${pathname/#\~\//$HOME/}"

The sed removes any initial blanks and numbers from the start of the line before read reads it.

As discussed in comments: The substitution ${pathname/#\~\//$HOME/} would only replace the tilde if it occurs together with / at the very start of $pathname. To handle a lone ~, you may have to add a separate substitution, or something like

case "$pathname" in
    "~")   pathname=$HOME ;;
    "~/"*) pathname=${pathname/#\~/$HOME} ;;
pushd "$pathname"

If the input file was created using dirs -l -p, then the loop would be even simpler:

while IFS= read -r pathname; do
    pushd "$pathname"
done <"$1"
  • Thanks. Is pushd "${pathname/#\~/$HOME}" better than pushd "${pathname/\~/$HOME}"? I just realized that ~ may be a valid character in a filename, and ~ as tilde expansion must be the first nonblank character in a pathname.
    – Tim
    May 5 '18 at 10:42
  • @Tim Yes, using /# rather than just # would be safer as that would only match the tilde at the very start of the value of pathname. If you want to be extra certain to match the correct thing, you would probably have to match the / after the tilde as well: ${pathname/#\~\//$HOME/}. I will modify my answer to do it this way.
    – Kusalananda
    May 5 '18 at 11:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.