繁体   English   中英

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

[英]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将我当前的目录更改回.

引自手册

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

另请参阅分组命令

{}

  { list; } 

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

这不是管道返回到目录,而是你已经使第一个命令(在分号之前)仅适用于cat命令。 你实际上是管道mkdir的子进程的输出, cdpwd转到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; } ,然后将其stdoutstdincat 当这两个进程中的第一个完成并收集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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM