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.

Tim
  • 101,790
  • 1
  • 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
  • No. A child process can't change the directories of the parent process. You need to use source to make the script run in the original shell process. – Barmar Jan 12 '18 at 17:04
  • @Barmar Thanks. I put the pathname to the script file in PATH, and I want to invoke the script without specifying its pathname. If I have to run it by source, is it possible to reuse the pathname in PATH? – Tim Jan 12 '18 at 17:10
  • source searches PATH by default. If you turn off the sourcepath option it won't. – Barmar Jan 12 '18 at 17:12
  • @Barmar Is there any variable used in my original command, which can't be passed out of the while command in a subshell? I miss to see what I need to pass out. – Tim Jan 12 '18 at 18:30
  • You need to pass out the directory stack, which is per-process just like variables. – Barmar Jan 12 '18 at 19:12

1 Answers1

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  ~
$
steeldriver
  • 81,074