第一个脚本:
$ { mkdir dir; cd dir; pwd; } | cat; pwd;
./dir
.
第二个脚本:
$ { mkdir dir; cd dir; pwd; }; pwd;
./dir
./dir
为什么这| cat
对当前目录有影响?以及如何解决?我需要第一个脚本与第二个脚本完全相同。我不想cat
将当前目录改回.
.
不是管道返回目录,而是您使第一个命令(分号之前)仅适用于该cat
命令。您实际上是在管道传输 and 的子流程的输出,mkdir
然后cd
转到pwd
流程cat
。
例如:
{ mkdir dir; cd dir; pwd; } | cat; pwd;
首先扩展为两个过程:1){ mkdir dir; cd dir; pwd; } | cat;
和2)pwd
第一个进程扩展为两个进程,{ mkdir dir; cd dir; pwd; }
然后将其发送stdout
到stdin
of cat
。当这两个进程中的第一个完成并stdout
收集时,它的子进程退出并且就像cd
从未发生过一样,因为cd
它只影响它正在运行的进程的目录。pwd
从未实际更改过$PWD
,它只打印提供的内容stdin
.
要解决此问题(假设我了解您要执行的操作),我会将其更改为:
{ mkdir dir; cd dir; pwd; }; pwd; cd -
当你运行时:
{ mkdir -p dir; cd dir; pwd; } | cat; pwd
或者
{ mkdir -p dir; cd dir; pwd; } | date
或者
{ mkdir -p dir; cd dir; pwd; } | ls
您正在子外壳中的管道 LHS 上运行一组命令,因此在两个命令(LHS 和 RHS)完成后change dir
不会反映在当前外壳中。
但是,当您运行时:
{ mkdir -p dir; cd dir; pwd; }; pwd;
两者之间没有管道,因此大括号内和大括号pwd
外的所有命令都在当前 shell 本身中运行,因此您会更改目录。
PS:还要注意这一行:
( mkdir -p dir; cd dir; pwd; )
也不会更改当前 shell 中的当前目录,因为方括号内的命令在子 shell 中执行,而花括号仅用于分组。
流水线命令关于变量的行为是实现定义的。
您碰巧使用bash
which 选择将每个组件放在后台 shell 中,因此cd
效果在主 shell 中丢失。
如果您运行ksh
了选择将管道的最后一个元素保留在当前 shell 中的运行,并且应该将其cd
放在最后一个语句中,那么行为将是您所期望的。
$ bash
$ { mkdir -p dir; cd dir; pwd; } | { cat; mkdir -p dir; cd dir; pwd ; } ; pwd;
/tmp/dir
/tmp/dir
/tmp
$ ksh
$ { mkdir -p dir; cd dir; pwd; } | { cat; mkdir -p dir; cd dir; pwd ; } ; pwd;
/tmp/dir
/tmp/dir
/tmp/dir
虽然手册说:
{ list; }
Placing a list of commands between curly braces causes the list to be executed in the current shell context. No subshell is created.
将其放置在管道上仍会将其放置在子外壳上。
{ list; } | something
自从
Each command in a pipeline is executed in its own subshell (see Command Execution Environment).
命令{ }
本身不会放在子外壳上,但它本身的更高上下文将是它仍然相同的原因。
作为测试运行( )
,{ }
将是相同的:
# echo "$BASHPID"
6582
# ( ps -p "$BASHPID" -o ppid= ) | cat
6582
# { ps -p "$BASHPID" -o ppid=; } | cat
6582
两者都将父进程作为调用外壳发送。
管道不会重置当前工作目录。管道创建子壳,在 bash 中为管道的每一侧创建一个子壳。子shell 也不会重置当前工作目录。子shell 本身包含对环境的更改,并且不会将它们传播到父shell。
在您的第一个脚本中:
$ { mkdir dir; cd dir; pwd; } | cat; pwd;
在管道的cd
左子壳内执行。工作目录仅在该子外壳中更改。父 shell 的工作目录没有改变。第一个pwd
在与 相同的子shell 中执行,cd
因此它读取更改的工作目录。最后的pwd
是在父 shell 中执行的,因此它会读取未更改的父 shell 的工作目录。
在您的第二个脚本中:
$ { mkdir dir; cd dir; pwd; }; pwd;
没有创建子shell。花括号不会创建子外壳。在cd
父 shell 中执行,并且两者都pwd
读取更改的工作目录。
有关更多信息和解释,请阅读有关以下内容的精美 bash 手册: