为什么管道重置当前的工作目录?

时间:2013-08-29 14:25:08

标签: bash shell

第一个脚本:

$ { mkdir dir; cd dir; pwd; } | cat; pwd;
./dir
.

第二个脚本:

$ { mkdir dir; cd dir; pwd; }; pwd;
./dir
./dir

为什么这个| cat对当前目录有影响?以及如何解决?我需要第一个脚本完全像第二个脚本一样工作。我不希望cat将当前目录更改回.

6 个答案:

答案 0 :(得分:11)

引用manual

  

管道中的每个命令都在它自己的子shell中执行(参见   Command Execution Environment)。

另见Grouping Commands

  

{}

   { list; }
     

在花括号之间放置一个命令列表会导致列表成为   在当前shell上下文中执行。没有创建子shell。

答案 1 :(得分:2)

并不是管道返回到目录,而是您已经使第一个命令(分号之前)仅适用于cat命令。您实际上是在管道mkdircd以及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发送。