如何一次性推送一系列目录?

如何一次性推送一系列目录?

foo我位于包含子目录的目录中bar1 bar2bar3仅此而已。

我想在一个命令中执行pushdfoobar1bar2bar3但是我遇到了困难:

find `pwd` | xargs pushd

回报

xargs: pushd: No such file or directory

我尝试用 while 循环来规避这个问题:

find `pwd` | while read line ; do pushd $line ; done

这给出了看起来正确的输出:

~/foo ~/foo
~/foo/bar1 ~/foo ~/foo
~/foo/bar2 ~/foo/bar1 ~/foo ~/foo
~/foo/bar3 ~/foo/bar2 ~/foo/bar1 ~/foo ~/foo

但是,之后使用dirs表明我没有向堆栈添加任何新目录:

~/foo

谁能发现我做错了什么吗?

答案1

你们非常接近。你可以做你想做的事

while read line; do pushd "$line"; done < <(find "$(pwd)" -type d)

您的命令的问题在于pushdcd必须在主(父)进程中完成才能有用,并且(根据系统的设置方式会有一些变化)管道中的命令在子进程(子进程)中运行)。  神奇地给你一个管道而不给你一个管道。< <(cmd)

这要求您运行 bash (或者可能是其他高级 shell 之一?),因为 POSIX 不支持.<(cmd)

遗憾的是,这种xargs方法注定要失败,因为pushd(like cd) 是一个 shell 内置命令(即,没有程序称为pushd),并且xargs需要外部可执行程序。您可以使用以下命令获得(几乎)看起来正确的输出:

$ find "$(pwd)" -type d | xargs -i sh -c 'pushd "$1"' sh
~/foo ~/foo
~/foo/bar1 ~/foo
~/foo/bar2 ~/foo
~/foo/bar3 ~/foo

但这是将 shell 作为外部程序执行,并对每个目录(独立)执行此操作。这有点接近:

$ find "$(pwd)" -type d | xargs sh -c 'for line do pushd "$line"; done' sh
~/foo ~/foo
~/foo/bar1 ~/foo ~/foo
~/foo/bar2 ~/foo/bar1 ~/foo ~/foo
~/foo/bar3 ~/foo/bar2 ~/foo/bar1 ~/foo ~/foo

执行单个 shell 进程,并告诉它循环遍历所有目录。正如您所看到的,这种技术类似于您的第二次尝试(以及我的答案),并且结果与您的第二次尝试相同 - 您得到一个调用pushd四次的新 shell 进程,并最终得到一个目录堆栈,即五层深(计算起始目录两次)——但是新的 shell 进程位于子进程中,并且当您看到下一个 shell 提示符时,shell进程消失了。

作为参考,请注意该命令有点类似于 我几年前给出的答案。  斯蒂芬·查泽拉斯讨论命令结构 sh -c long_complex_shell_command sh这里这里

答案2

这不是一份工作find

for subdirectory in */; do
  pushd -- "$subdirectory"
done

您的代码不起作用,因为pushd,例如cd,需要在您想要影响的 shell 环境中执行。没有可以调用pushd的外部命令xargs,因为外部命令是毫无意义的:它不会影响任何东西。管道的右侧在子 shell 中运行(在您的 shell 中,有一些 shell 在您的另一个 shell 中运行,并且您的第二次尝试将起作用);你可以看到pushd

find `pwd` | {
  while read line ; do pushd $line ; done;
  dirs
}

除非目录名称包含特殊字符,否则有效。始终在变量替换周围加上双引号,除非您知道为什么需要将它们去掉

相关内容