【问题标题】:Piping `cd` or `popd` output prevents changing directories?管道`cd`或`popd`输出防止改变目录?
【发布时间】:2014-04-02 23:24:01
【问题描述】:

我了解,由于| 为管道之后 的命令启动了一个新进程,因此cmd | cd newdir 形式的任何shell 命令(其中cmd 不会更改当前工作目录)将保持原始进程的工作目录不变。 (更不用说这有点傻,因为cd 不从标准输入读取输入。)

但是,在我的机器上(CentOS 6 机器,使用bashkshzsh),似乎以下命令无法更改目录: p>

cd newdir | cat

(请忽略在这里将输出通过管道传输到 cat 是多么愚蠢;我只是想举一个简单的例子。)

这是为什么?有没有办法解决这个问题?具体来说,我正在尝试编写一个使用 popd 但捕获输出、丢弃标准输出并重新输出标准错误的别名。

(出于好奇,这是我当前的非工作别名:popd 2>&1 >/dev/null | toerr && lsd。这里,toerr 只是捕获标准输入,将其输出到标准错误,并返回读取/打印的行数。lsd 是一个目录名称和内容打印机,只有在popd成功时才应该执行。我将stderr发送到stdout、管道它、捕获它并在stderr上重新输出的原因只是为了让它着色红色使用stderred,因为我的shell会话没有加载LD_PRELOAD,所以像popd这样的Bash内置函数不会得到红色的stderr。)

【问题讨论】:

  • 回应谁投票决定关闭它,因为它是题外话:首先,这个问题既适用于 shell 脚本,也适用于交互式 shell 会话;其次,问题出现在编写别名的上下文中,别名(尤其是在这种情况下,涉及到一些相对复杂的逻辑)本质上是一个小程序;
  • 第三,关于 SuperUser 上的进程和子进程的主题问题倾向于更多地关注用户与正在运行的进程的交互(查看/杀死作业、生成进程等),而这个问题涉及技术行为|,这对我来说更像是一个编程问题。

标签: shell unix process pipe cd


【解决方案1】:

bashdashash 中,管道中的每个命令都在子shell 中运行。

zshkshbashshopt -s lastpipe 中,除了管道中的最后一个命令之外,所有命令都在子shell 中运行。

由于cd——以及变量、shell选项、ulimits和新文件描述符——只影响当前进程,它们的效果不会影响父shell。

例子:

# Doesn't change directory
cd foo | cat
pwd

# Doesn't set $bar on default bash (but does on zsh and ksh)
echo foo | read bar
echo "$bar"

# Doesn't change the ulimit
ulimit -c 10000 2>&1 | grep "not permitted"
ulimit -c

这同样适用于生成子外壳的其他事物。以下都不会改变目录:

# Command expansion creates a subshell
echo $(cd foo); pwd

# ( .. ) creates a subshell
( cd foo ); pwd

# Backgrounding a process creates a subshell
cd foo & pwd

要修复它,您必须重写代码以在主 shell 进程中运行任何影响环境的东西。

在您的特定情况下,您可以考虑使用进程替换:

popd > /dev/null 2>   >(toerr) && lsd

这还有一个额外的好处,那就是只在popd 成功时运行lsd,而不是像您的版本那样在toerr 成功时运行。

【讨论】:

  • 所以基本上我把它倒过来了,因为我认为如果管道中的一个命令在当前进程中,first 命令就是 不是在子shell中,而是实际上是当前进程中执行的last命令?
  • 无论如何,感谢您的替代建议,以及其他小修复(尽管我不确定为什么 2>&12> 行为不同)。一个小的修正,不过——你的版本使用进程替换,而不是命令替换。
  • 这是最后一个(如果有的话),是的。 2>&1 > /dev/null | toerr2> >(toerr)的区别主要在于避开管道。他们最终都将命令的标准错误发送到toerr 的标准输入。
  • 是的,我看到了,但实际上我的意思是我没有立即看到2>&12> 之间的区别(我在添加&1 后尝试了您的示例)。不过我现在明白了——我想我只是忘记了>(cmd)2> 之后就像一个文件名。
猜你喜欢
  • 2019-02-14
  • 1970-01-01
  • 2011-06-05
  • 1970-01-01
  • 1970-01-01
  • 2011-09-09
  • 2021-11-30
  • 2021-07-14
  • 2019-11-02
相关资源
最近更新 更多