7

第一个脚本:

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

第二个脚本:

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

为什么这| cat对当前目录有影响?以及如何解决?我需要第一个脚本与第二个脚本完全相同。我不想cat将当前目录改回..

4

6 回答 6

11

引用手册

管道中的每个命令都在其自己的子 shell 中执行(请参阅 命令执行环境)。

另请参阅分组命令

{}

   { list; }

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

于 2013-08-29T14:30:04.137 回答
2

不是管道返回目录,而是您使第一个命令(分号之前)仅适用于该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; }然后将其发送stdoutstdinof cat。当这两个进程中的第一个完成并stdout收集时,它的子进程退出并且就像cd从未发生过一样,因为cd它只影响它正在运行的进程的目录。pwd从未实际更改过$PWD,它只打印提供的内容stdin.

要解决此问题(假设我了解您要执行的操作),我会将其更改为:

{ mkdir dir; cd dir; pwd; }; pwd; cd -

于 2013-08-29T14:32:55.450 回答
2

当你运行时:

{ 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 中执行,而花括号仅用于分组。

于 2013-08-29T14:34:49.733 回答
1

流水线命令关于变量的行为是实现定义的。

您碰巧使用bashwhich 选择将每个组件放在后台 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
于 2013-08-29T14:30:13.153 回答
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).

命令{ }本身不会放在子外壳上,但它本身的更高上下文将是它仍然相同的原因。

作为测试运行( ){ }将是相同的:

# echo "$BASHPID"
6582
# ( ps -p "$BASHPID" -o ppid= ) | cat
 6582
# { ps -p "$BASHPID" -o ppid=; } | cat
 6582

两者都将父进程作为调用外壳发送。

于 2013-08-29T15:19:38.370 回答
0

管道不会重置当前工作目录。管道创建子壳,在 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 手册:

于 2013-08-29T14:27:53.437 回答