[英]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.