next up previous
Next: 我要怎麼在一個 shell script 中或在背景執行  'ftp' Up: 中級的問題 Previous: 一個 process 要怎樣偵測出自己是否在背景狀態執行?

為什麼在 Bourne shell 當中,對迴圈的輸出入轉向無法達到預期的效果?

舉個例子來說好了:

		foo=bar
		while read line
		do
		# do something with $line
		    foo=bletch
		done < /etc/passwd

		echo "foo is now: $foo"
儘管 "foo=bletch" 已經設定了 foo 的值,然而在多種系統的 Bourne shell 上執行此 script 的時候仍會印出 "foo is now: bar"。為什麼呢?因為一些 歷史因素,在 Bourne shell 當中,一個控制結構(如一個迴圈,或者一個  "if" 敘述)的重導向會造出一個新的 subshell,所以啦,在此 subshell 內 所設定的變數當然不會影響目前 shell 的變數。

POSIX 1003.2 Shell and Tools Interface 的標準委員會已防止上述的問題, 也就是上述的例子在遵循 P1003.2 標準的 Bourne shells當中會印出 "foo is now: bletch"。

在一些較古老的 (以及遵循 P1003.2 標準的) Bourne shell 當中,您可以使 用以下技巧來避免重轉向的問題:

		foo=bar
		# make file descriptor 9 a duplicate of file descriptor 0 stdin);
		# then connect stdin to /etc/passwd; the original stdin is now
		# `remembered' in file descriptor 9; see dup(2) and sh(1)
		exec 9<&0 < /etc/passwd

		while read line
		do
		# do something with $line
		    foo=bletch
		done

		# make stdin a duplicate of file descriptor 9, i.e. reconnect
		# it to the original stdin; then close file descriptor 9
		exec 0<&9 9<&-
		echo "foo is now: $foo"
這樣子不管在哪種 Bourne shell 應該都會印出 "foo is now: bletch"。 接下來,看看以下這個例子:
	        foo=bar

	        echo bletch | read foo

	        echo "foo is now: $foo"
這個例子在許多 Bourne shell 內都會印出 "foo is now: bar",有些則會 印出 "foo is now: bletch"。為什麼呢?一般說來,一個 pipeline 裡面 的每一個部份都是在一個 subshell 中執行。但是有些系統的裡 pipeline 的最後一個如果是如 "read" 這類的內建指令,並不會另外造出一個  subshell。

 POSIX 1003.2 對這兩種作法並沒有硬性規定要用哪一種。所以如果想寫 一個 portable 的 shell script 不應該依賴這兩種作法其中的一種。



Tan Koan-Sin
1999-03-02