[英]Why pipe resets current working directory?
第一个脚本:
$ { mkdir dir; cd dir; pwd; } | cat; pwd;
./dir
.
第二个脚本:
$ { mkdir dir; cd dir; pwd; }; pwd;
./dir
./dir
为什么这个| cat
| cat
对当前目录有影响吗? 以及如何解决? 我需要第一个脚本完全像第二个脚本一样工作。 我不希望cat
将我当前的目录更改回.
。
这不是管道返回到目录,而是你已经使第一个命令(在分号之前)仅适用于cat
命令。 你实际上是管道mkdir
的子进程的输出, cd
和pwd
转到cat
进程。
例如: { mkdir dir; cd dir; pwd; } | cat; pwd;
{ mkdir dir; cd dir; pwd; } | cat; pwd;
首先扩展到两个过程:1) { mkdir dir; cd dir; pwd; } | cat;
{ mkdir dir; cd dir; pwd; } | cat;
和2) pwd
第一个过程扩展为两个过程, { mkdir dir; cd dir; pwd; }
{ mkdir dir; cd dir; pwd; }
{ mkdir dir; cd dir; pwd; }
,然后将其stdout
到stdin
的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
您正在子shell中的管道LHS上运行命令组,因此在完成两个命令(LHS和RHS)后, change dir
不会反映在当前shell中 。
但是当你运行时:
{ mkdir -p dir; cd dir; pwd; }; pwd;
之间没有管道,因此花括号内的所有命令和花括号外的pwd
都在当前shell中运行,因此您将获得更改的目录。
PS:还要注意这一行:
( mkdir -p dir; cd dir; pwd; )
也不会更改当前shell中的当前目录,因为方括号内的命令在子shell中执行,而花括号仅用于分组。
关于变量的管道命令的行为是实现定义的。
您碰巧使用bash
选择将每个组件放在后台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
管道不会重置当前工作目录。 管道在管道的每一侧以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手册:
虽然手册说:
{ 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上,但它本身的更高上下文将是它仍然是相同的原因。
作为测试,running ( )
和{ }
将是相同的:
# echo "$BASHPID"
6582
# ( ps -p "$BASHPID" -o ppid= ) | cat
6582
# { ps -p "$BASHPID" -o ppid=; } | cat
6582
两者都将父进程作为调用shell发送。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.