管道`cd`或`popd`输出阻止更改目录?

时间:2014-04-02 23:24:02

标签: shell unix process pipe cd

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

但是,在我的机器上(使用bashkshzsh的CentOS 6机箱),似乎以下命令失败更改目录:

cd newdir | cat

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

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

(好奇的是,这是我当前的非工作别名:popd 2>&1 >/dev/null | toerr && lsd。这里,toerr只捕获stdin,将其输出到stderr,并返回读取/打印的行数。 lsd是一个目录名和内容打印机,只有在popd成功时才能执行。我将stderr发送到stdout,管道,捕获它并重新输出它的原因在stderr上只是使用stderred将其变为红色,因为我的shell会话未加载LD_PRELOAD,因此诸如popd之类的Bash内置函数不会获得红色的stderr。)

1 个答案:

答案 0 :(得分:2)

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

zshkshbash以及shopt -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

这样做的另一个好处是,只有在lsd成功时运行popd,而不是像您的版本那样成功toerr