第一个脚本:
$ { mkdir dir; cd dir; pwd; } | cat; pwd;
./dir
.
第二个脚本:
$ { mkdir dir; cd dir; pwd; }; pwd;
./dir
./dir
为什么这个| cat
对当前目录有影响?以及如何解决?我需要第一个脚本完全像第二个脚本一样工作。我不希望cat
将当前目录更改回.
。
答案 0 :(得分:11)
引用manual:
管道中的每个命令都在它自己的子shell中执行(参见 Command Execution Environment)。
{}
{ list; }
在花括号之间放置一个命令列表会导致列表成为 在当前shell上下文中执行。没有创建子shell。
答案 1 :(得分:2)
并不是管道返回到目录,而是您已经使第一个命令(分号之前)仅适用于cat
命令。您实际上是在管道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
cat
。当这两个进程中的第一个完成并收集stdout
时,它的子进程退出,就像cd
从未发生过一样,因为cd
只影响它运行的进程的目录in。pwd
从未实际更改$PWD
,它只打印stdin
上提供的内容。
要解决此问题(假设我了解您要执行的操作),我会将其更改为:
{ mkdir dir; cd dir; pwd; }; pwd; cd -
答案 2 :(得分:2)
当你跑步时:
{ mkdir -p dir; cd dir; pwd; } | cat; pwd
OR
{ mkdir -p dir; cd dir; pwd; } | date
OR
{ mkdir -p dir; cd dir; pwd; } | ls
您正在子shell中的管道LHS上运行一组命令,因此在两个命令(LHS和RHS)完成后,change dir
不会反映在当前shell 中。< / p>
但是当你跑步时:
{ mkdir -p dir; cd dir; pwd; }; pwd;
之间没有管道,因此大括号内的所有命令和大括号外的pwd
都在当前shell本身中运行,因此您将更改目录。
PS:另请注意此行:
( mkdir -p dir; cd dir; pwd; )
也不会更改当前shell中的当前目录,因为方括号内的命令在子shell中执行,而花括号仅用于分组。
答案 3 :(得分:1)
关于变量的管道命令的行为是实现定义的。
您碰巧使用bash
选择将每个组件放在后台shell中,以便在主shell中丢失cd
效果。
如果您运行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
答案 4 :(得分:0)
管道不会重置当前工作目录。管道在管道的每一侧以bash形成子壳。子shell也不会重置当前工作目录。子shell包含对环境本身的更改,并且不会将它们传播到父shell。
在你的第一个剧本中:
$ { mkdir dir; cd dir; pwd; } | cat; pwd;
cd
在管道的左子shell内执行。工作目录仅在子shell中更改。父shell的工作目录不会更改。第一个pwd
在与cd
相同的子shell中执行,因此它会读取已更改的工作目录。最后的pwd
在父shell中执行,因此它会读取未更改的父shell的工作目录。
在你的第二个剧本中:
$ { mkdir dir; cd dir; pwd; }; pwd;
没有创建子shell。花括号不会创建子壳。 cd
在父shell中执行,两个pwd
都读取已更改的工作目录。
有关更多信息和解释,请阅读关于:
的精细bash手册答案 5 :(得分:0)
虽然手册说:
{ 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).
{ }
本身的命令不会放在子shell上,但它本身的更高的上下文就是它仍然是相同的原因。
正在运行( )
和{ }
的测试将是相同的:
# echo "$BASHPID"
6582
# ( ps -p "$BASHPID" -o ppid= ) | cat
6582
# { ps -p "$BASHPID" -o ppid=; } | cat
6582
两者都将父进程作为调用shell发送。