0

I have a file /tmp/pathnames where each line is a pathname to a dir.

/tmp/my dir1
/tmp/dir2
/tmp/dir3

I want to apply the command pushd to each line in the file, so that I can have those pathnames in the dir stack of the current shell.

Here is a bash script for that purpose:

#! /bin/bash

cat /tmp/pathnames | while read pathname; do
    pushd "$pathname"
done

But it doesn't work as I expected, i.e. no stack of dirs is created for my current shell.

Even if I run the commands in the script in the shell directly with source, it still does not work.

I wonder why and how to solve the problem?

Thanks.

marked as duplicate by Barmar, Rui F Ribeiro, jimmij, mdpc, cas Jan 13 '18 at 4:09

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

  • 1
    Possibly related: Why is the array empty after the while loop? – steeldriver Jan 12 '18 at 16:24
  • It's the same reason that variable changes aren't preserved when you pipe to a while read loop, and the same solution fixes it. – Barmar Jan 12 '18 at 16:52
  • Giles' solution in the link is to include the command after the whole pipeline inside the last part of the pipeline, while in my script there is no statement after the whole pipeline. @Barmar – Tim Jan 12 '18 at 16:56
  • You don't need a pipeline at all. Just use file redirection: < /tmp/pathnames – Barmar Jan 12 '18 at 16:57
  • Run the script directly will not change the dirs stack of the current shell. Is it possible to run the script without source it in the current shell? @Barmar – Tim Jan 12 '18 at 17:00
2

This is essentially the same issue as Why is the array empty after the while loop? i.e. that (regardless of whether you source it or not), the pushd is occurring in a different subshell.

Contrast

$ cat pathnames | while read pathname; do pushd "$pathname"; dirs -v; done
/tmp/my dir1 ~
 0  /tmp/my dir1
 1  ~
/tmp/dir2 /tmp/my dir1 ~
 0  /tmp/dir2
 1  /tmp/my dir1
 2  ~
/tmp/dir3 /tmp/dir2 /tmp/my dir1 ~
 0  /tmp/dir3
 1  /tmp/dir2
 2  /tmp/my dir1
 3  ~
$
$ dirs -v
 0  ~

with

$ while read pathname; do pushd "$pathname"; dirs -v; done < pathnames
/tmp/my dir1 ~
 0  /tmp/my dir1
 1  ~
/tmp/dir2 /tmp/my dir1 ~
 0  /tmp/dir2
 1  /tmp/my dir1
 2  ~
/tmp/dir3 /tmp/dir2 /tmp/my dir1 ~
 0  /tmp/dir3
 1  /tmp/dir2
 2  /tmp/my dir1
 3  ~
$
$ dirs -v
 0  /tmp/dir3
 1  /tmp/dir2
 2  /tmp/my dir1
 3  ~
$

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