首页 | 论坛 | Linux | 博客 | 下载 | 人才 | 培训 | WIKI | 手册 | 图书 | 搜索  
ChinaUnix.net » Shell » shell 十三問?

2004-1-13 03:37 網中人
shell 十三問?

8) $(( )) 與 $( ) 還有${ } 差在哪?

我們上一章介紹了 ( ) 與 { } 的不同,這次讓我們擴展一下,看看更多的變化:$( ) 與 ${ } 又是啥玩意兒呢?

在 bash shell 中,$( ) 與 ` ` (反引號) 都是用來做命令替換用(command substitution)的。
所謂的命令替換與我們第五章學過的變量替換差不多,都是用來重組命令行:
* 完成引號裡的命令行,然後將其結果替換出來,再重組命令行。
例如:
[code]$ echo the last sunday is $(date -d "last sunday" +%Y-%m-%d)[/code]
如此便可方便得到上一星期天的日期了... ^_^

在操作上,用 $( ) 或 ` ` 都無所謂,只是我"個人"比較喜歡用 $( ) ,理由是:

1,  ` ` 很容易與 ' ' ( 單引號)搞混亂,尤其對初學者來說。
有時在一些奇怪的字形顯示中,兩種符號是一模一樣的(直豎兩點)。
當然了,有經驗的朋友還是一眼就能分辯兩者。只是,若能更好的避免混亂,又何樂不為呢? ^_^

2, 在多層次的復合替換中,` ` 須要額外的跳脫( \` )處理,而 $( ) 則比較直觀。例如:
這是錯的:
[code]command1 `command2 `command3` `[/code]
原本的意圖是要在 command2 `command3` 先將 command3 提換出來給 command 2 處理,
然後再將結果傳給 command1 `command2 ...` 來處理。
然而,真正的結果在命令行中卻是分成了 `command2 ` 與 `` 兩段。
正確的輸入應該如下:
[code]command1 `command2 \`command3\` `[/code]

要不然,換成 $( ) 就沒問題了:
[code]command1 $(command2 $(command3))[/code]
只要你喜歡,做多少層的替換都沒問題啦~~~  ^_^

不過,$( ) 並不是沒有斃端的...
首先,` ` 基本上可用在全部的 unix shell 中使用,若寫成 shell script ,其移植性比較高。
而 $( ) 並不見的每一種 shell 都能使用,我只能跟你說,若你用 bash2 的話,肯定沒問題...  ^_^

接下來,再讓我們看 ${ } 吧... 它其實就是用來作變量替換用的啦。
一般情況下,$var 與 ${var} 並沒有啥不一樣。
但是用 ${ } 會比較精確的界定變量名稱的範圍,比方說:
[code]$ A=B
$ echo $AB

[/code]
原本是打算先將 $A 的結果替換出來,然後再補一個 B 字母於其後,
但在命令行上,真正的結果卻是只會提換變量名稱為 AB 的值出來...
若使用 ${ } 就沒問題了:
[code]$ echo ${A}B
BB[/code]

不過,假如你只看到 ${ } 只能用來界定變量名稱的話,那你就實在太小看 bash 了﹗
有興趣的話,你可先參考一下 cu 本版的精華文章:
[url]http://www.chinaunix.net/forum/viewtopic.php?t=201843[/url]

為了完整起見,我這裡再用一些例子加以說明 ${ } 的一些特異功能:
假設我們定義了一個變量為:
file=/dir1/dir2/dir3/my.file.txt
我們可以用 ${ } 分別替換獲得不同的值:
${file#*/}:拿掉第一條 / 及其左邊的字串:dir1/dir2/dir3/my.file.txt
${file##*/}:拿掉最後一條 / 及其左邊的字串:my.file.txt
${file#*.}:拿掉第一個 .  及其左邊的字串:file.txt
${file##*.}:拿掉最後一個 .  及其左邊的字串:txt
${file%/*}:拿掉最後條 / 及其右邊的字串:/dir1/dir2/dir3
${file%%/*}:拿掉第一條 / 及其右邊的字串:(空值)
${file%.*}:拿掉最後一個 .  及其右邊的字串:/dir1/dir2/dir3/my.file
${file%%.*}:拿掉第一個 .  及其右邊的字串:/dir1/dir2/dir3/my
記憶的方法為:
[list]# 是去掉左邊(在鑑盤上 # 在 $ 之左邊)
% 是去掉右邊(在鑑盤上 % 在 $ 之右邊)
單一符號是最小匹配﹔兩個符號是最大匹配。[/list]
${file:0:5}:提取最左邊的 5 個字節:/dir1
${file:5:5}:提取第 5 個字節右邊的連續 5 個字節:/dir2

我們也可以對變量值裡的字串作替換:
${file/dir/path}:將第一個 dir 提換為 path:/path1/dir2/dir3/my.file.txt
${file//dir/path}:將全部 dir 提換為 path:/path1/path2/path3/my.file.txt

利用 ${ } 還可針對不同的變數狀態賦值(沒設定、空值、非空值):
${file-my.file.txt} :假如 $file 沒有設定,則使用 my.file.txt 作傳回值。(空值及非空值時不作處理)
${file:-my.file.txt} :假如 $file 沒有設定或為空值,則使用 my.file.txt 作傳回值。 (非空值時不作處理)
${file+my.file.txt} :假如 $file 設為空值或非空值,均使用 my.file.txt 作傳回值。(沒設定時不作處理)
${file:+my.file.txt} :若 $file 為非空值,則使用 my.file.txt 作傳回值。 (沒設定及空值時不作處理)
${file=my.file.txt} :若 $file 沒設定,則使用 my.file.txt 作傳回值,同時將 $file 賦值為 my.file.txt 。 (空值及非空值時不作處理)
${file:=my.file.txt} :若 $file 沒設定或為空值,則使用 my.file.txt 作傳回值,同時將 $file 賦值為 my.file.txt 。 (非空值時不作處理)
${file?my.file.txt} :若 $file 沒設定,則將 my.file.txt 輸出至 STDERR。 (空值及非空值時不作處理)
${file:?my.file.txt} :若 $file 沒設定或為空值,則將 my.file.txt 輸出至 STDERR。 (非空值時不作處理)

tips:
以上的理解在於, 你一定要分清楚 unset 與 null 及 non-null 這三種賦值狀態.
一般而言, : 與 null 有關, 若不帶 : 的話, null 不受影響, 若帶 : 則連 null 也受影響.


還有哦,${#var} 可計算出變量值的長度:
${#file} 可得到 27 ,因為 /dir1/dir2/dir3/my.file.txt 剛好是 27 個字節...

接下來,再為大家介稍一下 bash 的組數(array)處理方法。
一般而言,A="a b c def" 這樣的變量只是將 $A 替換為一個單一的字串,
但是改為 A=(a b c def) ,則是將 $A 定義為組數...
bash 的組數替換方法可參考如下方法:
[code]${A[@]} 或 ${A[*]} [/code]可得到 a b c def (全部組數)
[code]${A[0]} [/code] 可得到 a (第一個組數),${A[1]} 則為第二個組數...
[code]${#A[@]} 或 ${#A[*]}  [/code]可得到 4 (全部組數數量)
[code]${#A[0]} [/code] 可得到 1 (即第一個組數(a)的長度),${#A[3]} 可得到 3 (第四個組數(def)的長度)
[code]A[3]=xyz  [/code]則是將第四個組數重新定義為 xyz ...

諸如此類的....
能夠善用 bash 的 $( ) 與 ${ } 可大大提高及簡化 shell 在變量上的處理能力哦~~~  ^_^

好了,最後為大家介紹 $(( )) 的用途吧:它是用來作整數運算的。
在 bash 中,$(( )) 的整數運算符號大致有這些:
+ - * / :分別為 "加、減、乘、除"。
% :餘數運算
& | ^ !:分別為 "AND、OR、XOR、NOT" 運算。

例:
[code]$ a=5; b=7; c=2
$ echo $(( a+b*c ))
19
$ echo $(( (a+b)/c ))
6
$ echo $(( (a*b)%c))
1[/code]

在 $(( )) 中的變量名稱,可於其前面加 $ 符號來替換,也可以不用,如:
$(( $a + $b * $c)) 也可得到 19 的結果

此外,$(( )) 還可作不同進位(如二進位、八進位、十六進位)作運算呢,只是,輸出結果皆為十進位而已:
echo $((16#2a)) 結果為 42 (16進位轉十進位)
以一個實用的例子來看看吧:
假如當前的  umask 是 022 ,那麼新建文件的權限即為:
[code]$ umask 022
$ echo "obase=8;$(( 8#666 & (8#777 ^ 8#$(umask)) ))" | bc
644[/code]

事實上,單純用 (( )) 也可重定義變量值,或作 testing:
a=5; ((a++)) 可將 $a 重定義為 6
a=5; ((a--)) 則為 a=4
a=5; b=7; ((a < b)) 會得到  0 (true) 的返回值。
常見的用於 (( )) 的測試符號有如下這些:
[list]<:小於
>:大於
<=:小於或等於
>=:大於或等於
==:等於
!=:不等於[/list]
不過,使用 (( )) 作整數測試時,請不要跟 [ ] 的整數測試搞混亂了。(更多的測試我將於第十章為大家介紹)

怎樣?好玩吧..  ^_^  okay,這次暫時說這麼多...
上面的介紹,並沒有詳列每一種可用的狀態,更多的,就請讀者參考手冊文件囉...

[[i] 本帖最后由 網中人 于 2008-12-3 02:39 编辑 [/i]]

2004-1-13 17:09 nkliyong
shell 十三問?

好久没来,各位公历日见深厚,佩服,惭愧。
顶一把。

2004-1-14 16:50 沁园春
shell 十三問?

好人啊,支持

2004-1-14 19:42 qyfeng2002
shell 十三問?

好贴,请继续。。。

2004-1-15 02:22 網中人
shell 十三問?

9) $@ 與 $* 差在哪?

要說 $@ 與 $* 之前,需得先從 shell script 的 positional parameter 談起...
我們都已經知道變量(variable)是如何定義及替換的,這個不用再多講了。
但是,我們還需要知道有些變量是 shell 內定的,且其名稱是我們不能隨意修改的,
其中就有 positional parameter 在內。

在 shell script 中,我們可用 $0, $1, $2, $3 ... 這樣的變量分別提取命令行中的如下部份:
[code]script_name parameter1 parameter2 parameter3 ...[/code]
我們很容易就能猜出 $0 就是代表 shell script 名稱(路逕)本身,而 $1 就是其後的第一個參數,如此類推....
須得留意的是 IFS 的作用,也就是,若 IFS 被 quoting 處理後,那麼 positional parameter 也會改變。
如下例:
[code]my.sh p1 "p2 p3" p4[/code]
由於在 p2 與 p3 之間的空白鍵被 soft quote 所關閉了,因此 my.sh 中的 $2 是 "p2 p3" 而 $3 則是 p4 ...

還記得前兩章我們提到 fucntion 時,我不是說過它是 script 中的 script 嗎?  ^_^
是的,function 一樣可以讀取自己的(有別於 script 的) postitional parameter ,惟一例外的是 $0 而已。
舉例而言:假設 my.sh 裡有一個 fucntion 叫 my_fun , 若在 script 中跑 my_fun fp1 fp2 fp3 ,
那麼,function 內的 $0 是 my.sh ,而 $1 則是 fp1 而非 p1 了...

不如寫個簡單的 my.sh script  看看吧:
[code]#!/bin/bash

my_fun() {
    echo '$0 inside function is '$0
    echo '$1 inside function is '$1
    echo '$2 inside function is '$2
}

echo '$0 outside function is '$0
echo '$1 outside function is '$1
echo '$2 outside function is '$2

my_fun fp1 "fp2 fp3"[/code]
然後在 command line 中跑一下 script 就知道了:
[code]chmod +x my.sh
./my.sh p1 "p2 p3"
$0 outside function is ./my.sh
$1 outside function is p1
$2 outside function is p2 p3
$0 inside function is ./my.sh
$1 inside function is fp1
$2 inside function is fp2 fp3[/code]

然而,在使用 positional parameter 的時候,我們要注意一些陷阱哦:
* $10 不是替換第 10 個參數,而是替換第一個參數($1)然後再補一個 0 於其後﹗
也就是,my.sh one two three four five six seven eigth nine ten 這樣的 command line ,
my.sh 裡的 $10 不是 ten 而是 one0 哦... 小心小心﹗
要抓到 ten 的話,有兩種方法:

方法一是使用我們上一章介紹的 ${ } ,也就是用 ${10} 即可。

方法二,就是 shift 了。
用通俗的說法來說,所謂的 shift 就是取消 positional parameter 中最左邊的參數( $0 不受影響)。
其預設值為 1 ,也就是 shift 或 shift 1  都是取消 $1 ,而原本的 $2 則變成 $1、$3 變成 $2 ...
若 shift 3 則是取消前面三個參數,也就是原本的 $4 將變成 $1 ...
那,親愛的讀者,你說要 shift 掉多少個參數,才可用 $1 取得 ${10} 呢? ^_^

okay,當我們對 positional parameter 有了基本概念之後,那再讓我們看看其他相關變量吧。
首先是 $# :它可抓出 positional parameter 的數量。
以前面的 my.sh p1 "p2 p3" 為例:
由於 p2 與 p3 之間的 IFS 是在 soft quote 中,因此 $# 可得到 2 的值。
但如果 p2 與 p3 沒有置於 quoting 中話,那 $# 就可得到 3 的值了。
同樣的道理在 function 中也是一樣的...

因此,我們常在 shell script 裡用如下方法測試 script 是否有讀進參數:
[code][ $# = 0 ][/code]
假如為 0 ,那就表示 script 沒有參數,否則就是有帶參數...

接下來就是 $@ 與 $* :
精確來講,兩者只有在 soft quote 中才有差異,否則,都表示"全部參數"( $0 除外)。
舉例來說好了:
若在 command line 上跑 my.sh p1 "p2 p3" p4 的話,
不管是 $@ 還是 $* ,都可得到 p1 p2 p3 p4 就是了。
但是,如果置於 soft quote 中的話:
"$@" 則可得到 "p1" "p2 p3" "p4" 這三個不同的詞段(word)﹔
"$*" 則可得到 "p1 p2 p3 p4" 這一整串單一的詞段。

我們可修改一下前面的 my.sh ,使之內容如下:
[code]#!/bin/bash

my_fun() {
    echo "$#"
}

echo 'the number of parameter in "$@" is '$(my_fun "$@")
echo 'the number of parameter in "$*" is '$(my_fun "$*")[/code]

然後再執行 ./my.sh p1 "p2 p3" p4 就知道 $@ 與 $* 差在哪了 ...    ^_^

2004-1-16 03:48 網中人
shell 十三問?

10) && 與 || 差在哪?

好不容易,進入兩位數的章節了... 一路走來,很辛苦吧?也很快樂吧?  ^_^

在解答本章題目之前,先讓我們了解一個概念:return value ﹗
我們在 shell 下跑的每一個 command 或 function ,在結束的時候都會傳回父行程一個值,稱為 return value 。
在 shell command line 中可用 $? 這個變量得到最"新"的一個 return value ,也就是剛結束的那個行程傳回的值。
Return Value(RV) 的取值為 0-255 之間,由程式(或 script)的作者自行定議:
* 若在 script 裡,用 exit RV 來指定其值,若沒指定,在結束時以最後一道命令之 RV 為值。
* 若在 function 裡,則用 return RV 來代替 exit RV 即可。

Return Value 的作用,是用來判斷行程的退出狀態(exit status),只有兩種:
* 0 的話為"真"( true )
* 非  0 的話為"假"( false )

舉個例子來說明好了:
假設當前目錄內有一份 my.file 的文件,而 no.file 是不存在的:
[code]$ touch my.file
$ ls my.file
$ echo $?        # first echo
0
$ ls no.file
ls: no.file: No such file or directory
$ echo $?        # second echo
1
$ echo $?        # third echo
0[/code]
上例的第一個 echo 是關於 ls my.file 的 RV ,可得到 0 的值,因此為 true ﹔
第二個 echo 是關於 ls no.file 的 RV ,則得到非 0  的值,因此為 false ﹔
第三個 echo 是關於第二個 echo $? 的 RV ,為 0 的值,因此也為 true 。

請記住:每一個 command 在結束時都會送回 return value 的﹗不管你跑甚麼樣的命令...
然而,有一個命令卻是"專門"用來測試某一條件而送出 return value 以供 true 或 false 的判斷,
它就是 test 命令了﹗
若你用的是 bash ,請在 command line 下打 man test 或 man bash 來了解這個 test 的用法。
這是你可用作參考的最精確的文件了,要是聽別人說的,僅作參考就好...
下面我只簡單作一些輔助說明,其餘的一律以 man 為準:

首先,test 的表示式我們稱為 expression ,其命令格式有兩種:
[code]test expression
or:
[ expression ][/code]
(請務必注意 [ ] 之間的空白鍵﹗)
用哪一種格式沒所謂,都是一樣的效果。(我個人比較喜歡後者...)

其次,bash 的 test 目前支援的測試對像只有三種:
* string:字串,也就是純文字。
* integer:整數( 0 或正整數,不含負數或小數點)。
* file:文件。
請初學者一定要搞清楚這三者的差異,因為 test 所用的 expression 是不一樣的。
以 A=123 這個變量為例:
* [ "$A" = 123 ]:是字串的測試,以測試 $A 是否為 1、2、3 這三個連續的"文字"。
* [ "$A" -eq 123 ]:是整數的測試,以測試 $A 是否等於"一百二十三"。
* [ -e "$A" ]:是關於文件的測試,以測試 123 這份"文件"是否存在。

第三,當 expression 測試為"真"時,test 就送回 0 (true) 的 return value ,否則送出非 0 (false)。
若在 expression 之前加上一個 " ! "(感嘆號),則是當 expression 為"假時" 才送出 0 ,否則送出非 0 。
同時,test 也允許多重的覆合測試:
* expression1 -a expression2 :當兩個 exrepssion 都為 true ,才送出 0 ,否則送出非 0 。
* expression1 -o expression2 :只需其中一個 exrepssion 為 true ,就送出 0 ,只有兩者都為 false 才送出非 0 。
例如:
[code][ -d "$file" -a -x "$file" ][/code]
是表示當 $file 是一個目錄、且同時具有 x 權限時,test 才會為 true 。

第四,在 command line 中使用 test 時,請別忘記命令行的"重組"特性,
也就是在碰到 meta 時會先處理 meta 再重新組建命令行。(這個特性我在第二及第四章都曾反覆強調過)
比方說,若 test 碰到變量或命令替換時,若不能滿足 expression 格式時,將會得到語法錯誤的結果。
舉例來說好了:
關於 [ string1 = string2 ] 這個 test 格式,
在 = 號兩邊必須要有字串,其中包括空(null)字串(可用 soft quote  或 hard quote 取得)。
假如 $A 目前沒有定義,或被定議為空字串的話,那如下的寫法將會失敗:
[code]$ unset A
$ [ $A = abc ]
[: =: unary operator expected[/code]
這是因為命令行碰到  $ 這個 meta 時,會替換 $A 的值,然後再重組命令行,那就變成了:
[ = abc ]
如此一來 = 號左邊就沒有字串存在了,因此造成 test 的語法錯誤﹗
但是,下面這個寫法則是成立的:
[code]$ [ "$A" = abc ]
$ echo $?
1[/code]
這是因為在命令行重組後的結果為:
[ "" = abc ]
由於 = 左邊我們用 soft quote 得到一個空字串,而讓 test 語法得以通過...

讀者諸君請務必留意這些細節哦,因為稍一不慎,將會導至 test 的結果變了個樣﹗
若您對 test 還不是很有經驗的話,那在使用 test 時不妨先採用如下這一個"法則":
* 假如在 test 中碰到變量替換,用 soft quote 是最保險的﹗
若你對 quoting 不熟的話,請重新溫習第四章的內容吧...  ^_^

okay,關於更多的 test 用法,老話一句:請看 man page 吧﹗  ^_^

雖然洋洋灑灑講了一大堆,或許你還在嘀咕.... 那... 那個 return value 有啥用啊?﹗
問得好﹗
告訴你:return value 的作用可大了﹗若你想讓你的 shell 變"聰明"的話,就全靠它了:
* 有了 return value,我們可以讓 shell 跟據不同的狀態做不同的時情...

這時候,才讓我來揭曉本章的答案吧~~~  ^_^
&& 與 || 都是用來"組建"多個 command line 用的:
* command1 && command2 :其意思是 command2 只有在 RV 為 0 (true) 的條件下執行。
* command1 || command2 :其意思是 command2 只有在 RV 為非 0 (false) 的條件下執行。
來,以例子來說好了:
[code]$ A=123
$ [ -n "$A" ] && echo "yes! it's ture."
yes! it's ture.
$ unset A
$ [ -n "$A" ] && echo "yes! it's ture."
$ [ -n "$A" ] || echo "no, it's NOT ture."
no, it's NOT ture.[/code]
(註:[ -n string ] 是測試 string 長度大於 0 則為 true 。)
上例的第一個 && 命令行之所以會執行其右邊的 echo 命令,是因為上一個 test 送回了 0 的 RV 值﹔
但第二次就不會執行,因為 test 送回非 0 的結果...
同理,|| 右邊的 echo 會被執行,卻正是因為左邊的 test 送回非 0 所引起的。

事實上,我們在同一命令行中,可用多個 && 或 || 來組建呢:
[code]$ A=123
$ [ -n "$A" ] && echo "yes! it's ture." || echo "no, it's NOT ture."
yes! it's ture.
$ unset A
$ [ -n "$A" ] && echo "yes! it's ture." || echo "no, it's NOT ture."
no, it's NOT ture.[/code]
怎樣,從這一刻開始,你是否覺得我們的 shell 是"很聰明"的呢?  ^_^

好了,最後,佈置一道習題給大家做做看、、、
下面的判斷是:當 $A 被賦與值時,再看是否小於 100 ,否則送出 too big! :
[code]$ A=123
$ [ -n "$A" ] && [ "$A" -lt 100 ] || echo 'too big!'
too big![/code]
若我將 A 取消,照理說,應該不會送文字才對啊(因為第一個條件就不成立了)...
[code]$ unset A
$ [ -n "$A" ] && [ "$A" -lt 100 ] || echo 'too big!'
too big![/code]
為何上面的結果也可得到呢?
又,如何解決之呢?
(提示:修改方法很多,其中一種方法可利用第七章介紹過的 command group ...)

快﹗告我我答案﹗其餘免談....

[[i] 本帖最后由 網中人 于 2008-10-30 02:21 编辑 [/i]]

2004-1-16 14:44 網中人
shell 十三問?

11) > 與 < 差在哪?

這次的題目之前我在 CU 的 shell 版已說明過了:
(原貼連結在論壇改版後已經失效)
這次我就不重寫了,將貼子的內容"抄"下來就是了...

--------------
11.1
談到 I/O redirection ,不妨先讓我們認識一下 File Descriptor (FD) 。

程式的運算,在大部份情況下都是進行數據(data)的處理,
這些數據從哪讀進?又,送出到哪裡呢?
這就是 file descriptor (FD) 的功用了。

在 shell 程式中,最常使用的 FD 大概有三個,分別為:
0: Standard Input (STDIN)
1: Standard Output (STDOUT)
2: Standard Error Output (STDERR)

在標準情況下,這些 FD 分別跟如下設備(device)關聯:
stdin(0): keyboard
stdout(1): monitor
stderr(2): monitor

我們可以用如下下命令測試一下:
[code]$ mail -s test root
this is a test mail.
please skip.
^d (同時按 crtl 跟 d 鍵) [/code]
很明顯,mail 程式所讀進的數據,就是從 stdin 也就是 keyboard 讀進的。
不過,不見得每個程式的 stdin 都跟 mail 一樣從 keyboard 讀進,
因為程式作者可以從檔案參數讀進 stdin ,如:
[code]$ cat /etc/passwd [/code]
但,要是 cat 之後沒有檔案參數則又如何呢?
哦,請您自己玩玩看囉.... ^_^
[code]$ cat [/code]

(請留意數據輸出到哪裡去了,最後別忘了按 ^d 離開...)

至於 stdout 與 stderr ,嗯... 等我有空再續吧... ^_^
還是,有哪位前輩要來玩接龍呢?

--------------
11.2
沿文再續,書接上一回... ^_^

相信,經過上一個練習後,你對 stdin 與 stdout 應該不難理解吧?
然後,讓我們繼續看 stderr 好了。
事實上,stderr 沒甚麼難理解的:說穿了就是"錯誤信息"要往哪邊送而已...
比方說,若讀進的檔案參數是不存在的,那我們在 monitor 上就看到了:
[code]$ ls no.such.file
ls: no.such.file: No such file or directory [/code]

若,一個命令同時產生 stdout 與 stderr 呢?
那還不簡單,都送到 monitor 來就好了:
[code]$ touch my.file
$ ls my.file no.such.file
ls: no.such.file: No such file or directory
my.file [/code]

okay,至此,關於 FD 及其名稱、還有相關聯的設備,相信你已經沒問題了吧?
那好,接下來讓我們看看如何改變這些 FD 的預設數據通道,
我們可用 < 來改變讀進的數據通道(stdin),使之從指定的檔案讀進。
我們可用 > 來改變送出的數據通道(stdout, stderr),使之輸出到指定的檔案。

比方說:
[code]$ cat < my.file [/code]
就是從 my.file 讀進數據
[code]$ mail -s test root < /etc/passwd [/code]
則是從 /etc/passwd 讀進...
這樣一來,stdin 將不再是從 keyboard 讀進,而是從檔案讀進了...
嚴格來說,< 符號之前需要指定一個 FD 的(之間不能有空白),
但因為 0 是 < 的預設值,因此 < 與 0< 是一樣的﹗

okay,這個好理解吧?
那,要是用兩個 << 又是啥呢?
這是所謂的 HERE Document ,它可以讓我們輸入一段文本,直到讀到 << 後指定的字串。
比方說:
[code]$ cat <<FINISH
first line here
second line there
third line nowhere
FINISH [/code]
這樣的話,cat 會讀進 3 行句子,而無需從 keyboard 讀進數據且要等 ^d 結束輸入。

至於 > 又如何呢?
且聽下回分解....

--------------
11.3
okay,又到講古時間~~~

當你搞懂了 0< 原來就是改變 stdin 的數據輸入通道之後,相信要理解如下兩個 redirection 就不難了:
* 1>
* 2>
前者是改變 stdout 的數據輸出通道,後者是改變 stderr 的數據輸出通道。
兩者都是將原本要送出到 monitor 的數據轉向輸出到指定檔案去。
由於 1 是 > 的預設值,因此,1> 與 > 是相同的,都是改變 stdout 。

用上次的 ls 例子來說明一下好了:

[code]$ ls my.file no.such.file 1>file.out
ls: no.such.file: No such file or directory [/code]
這樣 monitor 就只剩下 stderr 而已。因為 stdout 給寫進 file.out 去了。

[code]$ ls my.file no.such.file 2>file.err
my.file [/code]
這樣 monitor 就只剩下 stdout ,因為 stderr 寫進了 file.err 。

[code]$ ls my.file no.such.file 1>file.out 2>file.err [/code]
這樣 monitor 就啥也沒有,因為 stdout 與 stderr 都給轉到檔案去了...

呵~~~ 看來要理解 > 一點也不難啦﹗是不?沒騙你吧? ^_^
不過,有些地方還是要注意一下的。

首先,是同時寫入的問題。比方如下這個例子:

[code]$ ls my.file no.such.file 1>file.both 2>file.both [/code]
假如 stdout(1) 與 stderr(2) 都同時在寫入 file.both 的話,
則是採取“覆蓋”方式:後來寫入的覆蓋前面的。
讓我們假設一個 stdout 與 stderr 同時寫入 file.out 的情形好了:
* 首先 stdout 寫入10個字元
* 然後 stderr 寫入 6 個字元
那麼,這時候原本 stdout 輸出的 10 個字元就被 stderr 覆蓋掉了。

那,如何解決呢?所謂山不轉路轉、路不轉人轉嘛,
我們可以換一個思維:將 stderr 導進 stdout 或將 stdout 導進 sterr ,而不是大家在搶同一份檔案,不就行了﹗
bingo﹗就是這樣啦:
* 2>&1 就是將 stderr 併進 stdout 作輸出
* 1>&2 或 >&2 就是將 stdout 併進 stderr 作輸出
於是,前面的錯誤操作可以改為:
[code]$ ls my.file no.such.file 1>file.both 2>&1

$ ls my.file no.such.file 2>file.both >&2 [/code]

這樣,不就皆大歡喜了嗎? 呵~~~ ^_^

不過,光解決了同時寫入的問題還不夠,我們還有其他技巧需要了解的。
故事還沒結束,別走開﹗廣告後,我們再回來...﹗


--------------
11.4
okay,這次不講 I/O Redirction ,講佛吧...
(有沒搞錯?﹗網中人是否頭殼燒壞了?...) 嘻~~~ ^_^

學佛的最高境界,就是"四大皆空"。至於是空哪四大塊?我也不知,因為我還沒到那境界...
但這個"空"字,卻非常值得我們返複把玩的:
--- 色即是空、空即是色﹗
好了,施主要是能夠領會"空"的禪意,那離修成正果不遠矣~~~

在 Linux 檔案系統裡,有個設備檔位於 /dev/null 。
許多人都問過我那是甚麼玩意兒?我跟你說好了:那就是"空"啦﹗
沒錯﹗空空如也的空就是 null 了.... 請問施主是否忽然有所頓誤了呢?然則恭喜了~~~ ^_^

這個 null 在 I/O Redirection 中可有用得很呢:
* 若將 FD1 跟 FD2 轉到 /dev/null 去,就可將 stdout 與 stderr 弄不見掉。
* 若將 FD0 接到 /dev/null 來,那就是讀進 nothing 。
比方說,當我們在執行一個程式時,畫面會同時送出 stdout 跟 stderr ,
假如你不想看到 stderr (也不想存到檔案去),那可以:
[code]$ ls my.file no.such.file 2>/dev/null
my.file[/code]

若要相反:只想看到 stderr 呢?還不簡單﹗將 stdout 弄到 null 就行:
[code]$ ls my.file no.such.file >/dev/null
ls: no.such.file: No such file or directory [/code]

那接下來,假如單純只跑程式,不想看到任何輸出結果呢?
哦,這裡留了一手上次節目沒講的法子,專門贈予有緣人﹗... ^_^
除了用 >/dev/null 2>&1 之外,你還可以如此:
[code]$ ls my.file no.such.file &>/dev/null [/code]
(提示:將 &> 換成 >& 也行啦~~! )

okay?講完佛,接下來,再讓我們看看如下情況:
[code]$ echo "1" > file.out
$ cat file.out
1
$ echo "2" > file.out
$ cat file.out
2 [/code]
看來,我們在重導 stdout 或 stderr 進一份檔案時,似乎永遠只獲得最後一次導入的結果。
那,之前的內容呢?
呵~~~ 要解決這個問提很簡單啦,將 > 換成 >> 就好:
[code]$ echo "3" >> file.out
$ cat file.out
2
3 [/code]
如此一來,被重導的目標檔案之內容並不會失去,而新的內容則一直增加在最後面去。
easy ? 呵 ... ^_^

但,只要你再一次用回單一的 > 來重導的話,那麼,舊的內容還是會被"洗"掉的﹗
這時,你要如何避免呢?
----備份﹗ yes ,我聽到了﹗不過.... 還有更好的嗎?
既然與施主這麼有緣份,老納就送你一個錦囊妙法吧:
[code]$ set -o noclobber
$ echo "4" > file.out
-bash: file: cannot overwrite existing file [/code]

那,要如何取消這個"限制"呢?
哦,將 set -o 換成 set +o 就行:
[code]$ set +o noclobber
$ echo "5" > file.out
$ cat file.out
5 [/code]

再問:那... 有辦法不取消而又"臨時"蓋寫目標檔案嗎?
哦,佛曰:不可告也﹗
啊~~~ 開玩笑的、開玩笑的啦~~~ ^_^ 唉,早就料到人心是不足的了﹗
[code]$ set -o noclobber
$ echo "6" >| file.out
$ cat file.out
6 [/code]
留意到沒有:在 > 後面再加個" | "就好(注意: > 與 | 之間不能有空白哦)....

呼.... (深呼吸吐納一下吧)~~~ ^_^
再來還有一個難題要你去參透的呢:
[code]$ echo "some text here" > file
$ cat < file
some text here
$ cat < file > file.bak
$ cat < file.bak
some text here
$ cat < file > file
$ cat < file [/code]

嗯?﹗注意到沒有?﹗﹗
---- 怎麼最後那個 cat 命令看到的 file 竟是空的?﹗
why? why? why?

同學們:下節課不要遲到囉~~~!


--------------
11.5
噹噹噹~~~ 上課囉~~~ ^_^

前面提到:$ cat < file > file 之後原本有內容的檔案結果卻被洗掉了﹗
要理解這一現像其實不難,這只是 priority 的問題而已:
* 在 IO Redirection 中,stdout 與 stderr 的管道會先準備好,才會從 stdin 讀進資料。
也就是說,在上例中,> file 會先將 file 清空,然後才讀進 < file ,
但這時候檔案已經被清空了,因此就變成讀不進任何資料了...

哦~~~ 原來如此~~~~ ^_^
那... 如下兩例又如何呢?
[code]$ cat <> file
$ cat < file >> file [/code]

嗯... 同學們,這兩個答案就當練習題囉,下節課之前請交作業﹗

好了,I/O Redirection 也快講完了,sorry,因為我也只知道這麼多而已啦~~~ 嘻~~ ^_^
不過,還有一樣東東是一定要講的,各位觀眾(請自行配樂~!#@!$%) :
---- 就是 pipe line 也﹗

談到 pipe line ,我相信不少人都不會陌生:
我們在很多 command line 上常看到的" | "符號就是 pipe line 了。
不過,究竟 pipe line 是甚麼東東呢?
別急別急... 先查一下英漢字典,看看 pipe 是甚麼意思?
沒錯﹗它就是"水管"的意思...
那麼,你能想像一下水管是怎麼一根接著一根的嗎?
又,每根水管之間的 input 跟 output 又如何呢?
嗯??
靈光一閃:原來 pipe line 的 I/O 跟水管的 I/O 是一模一樣的:
* 上一個命令的 stdout 接到下一個命令的 stdin 去了﹗
的確如此... 不管在 command line 上你使用了多少個 pipe line ,
前後兩個 command 的 I/O 都是彼此連接的﹗(恭喜:你終於開竅了﹗ ^_^ )

不過... 然而... 但是... ... stderr 呢?
好問題﹗不過也容易理解:
* 若水管漏水怎麼辦?
也就是說:在 pipe line 之間,前一個命令的 stderr 是不會接進下一命令的 stdin 的,
其輸出,若不用 2> 導到 file 去的話,它還是送到監視器上面來﹗
這點請你在 pipe line 運用上務必要注意的。

那,或許你又會問:
* 有辦法將 stderr 也餵進下一個命令的 stdin 去嗎?
(貪得無厭的家夥﹗)
方法當然是有,而且你早已學過了﹗ ^_^
我提示一下就好:
* 請問你如何將 stderr 合併進 stdout 一同輸出呢?
若你答不出來,下課之後再來問我吧... (如果你臉皮真夠厚的話...)

或許,你仍意尤未盡﹗或許,你曾經碰到過下面的問題:
* 在 cm1 | cm2 | cm3 ... 這段 pipe line 中,若要將 cm2 的結果存到某一檔案呢?

若你寫成 cm1 | cm2 > file | cm3 的話,
那你肯定會發現 cm3 的 stdin 是空的﹗(當然啦,你都將水管接到別的水池了﹗)
聰明的你或許會如此解決:
[code]cm1 | cm2 > file ; cm3 < file [/code]
是的,你的確可以這樣做,但最大的壞處是:這樣一來,file I/O 會變雙倍﹗
在 command 執行的整個過程中,file I/O 是最常見的最大效能殺手。
凡是有經驗的 shell 操作者,都會盡量避免或降低 file I/O 的頻率。

那,上面問題還有更好方法嗎?
有的,那就是 tee 命令了。
* 所謂 tee 命令是在不影響原本 I/O 的情況下,將 stdout 複製一份到檔案去。
因此,上面的命令行可以如此打:
[code]cm1 | cm2 | tee file | cm3 [/code]
在預設上,tee 會改寫目標檔案,若你要改為增加內容的話,那可用 -a 參數達成。

基本上,pipe line 的應用在 shell 操作上是非常廣泛的,尤其是在 text filtering 方面,
凡舉 cat, more, head, tail, wc, expand, tr, grep, sed, awk, ... 等等文字處理工具,
搭配起 pipe line 來使用,你會驚覺 command line 原來是活得如此精彩的﹗
常讓人有"眾裡尋他千百度,驀然回首,那人卻在燈火闌珊處﹗"之感... ^_^

....

好了,關於 I/O Redirection 的介紹就到此告一段落。
若日後有空的話,再為大家介紹其它在 shell 上好玩的東西﹗bye... ^_^

[[i] 本帖最后由 網中人 于 2008-10-30 02:24 编辑 [/i]]

2004-1-17 17:36 tacy_lee
shell 十三問?

谢谢您的贴子,受益良多!

2004-1-19 15:28 fengjianheng
shell 十三問?

[quote][i]原帖由 "網中人" 发表:[/i]

嗯,這個前面已經討論過了,我不再重複了...[/quote]

能解释一下下面命令中的{}含义吗?

find . -size +3000k -exec ls -ld {} \;

2004-1-19 16:29 網中人
shell 十三問?

這是 find 用的,而不是 shell ...

它用在 find 的 -exec 或 -ok 處理,用在其後 command line 中,依次代入 find 找到的結果。

請 man find 及 man xargs (xargs 之 -i)...

2004-1-23 15:12 calltomxy
shell 十三問?

看来台湾人的解释更加简洁,易懂 :)

2004-1-30 12:57 admirer
shell 十三問?

一口气又从头到尾看了一边,深有感触!
强烈推荐喜欢shell的朋友多多研读,仔细品味!

2004-1-31 17:20 網中人
shell 十三問?

12) 你要 if 還是 case 呢?

放了一個愉快的春節假期,人也變得懶懶散散的... 只是,答應了大家的作業,還是要堅持完成就是了~~~

還記得我們在第 10 章所介紹的 return value 嗎?
是的,接下來介紹的內容與之有關,若你的記憶也被假期的歡樂時光所抵消掉的話,
那,建議您還是先回去溫習溫習再回來...

若你記得  return value ,我想你也應該記得了 && 與 || 是甚麼意思吧?
用這兩個符號再配搭 command group 的話,我們可讓 shell script 變得更加聰明哦。
比方說:
[code]comd1 && {
    comd2
    comd3
    :
} || {
    comd4
    comd5
}[/code]
意思是說:
[list]假如 comd1 的 return value 為 true 的話,
然則執行 comd2 與 comd3 ,
否則執行 comd4 與 comd5 。[/list]

事實上,我們在寫 shell script 的時候,經常需要用到這樣那樣的條件以作出不同的處理動作。
用 && 與 || 的確可以達成條件執行的效果,然而,從"人類語言"上來理解,卻不是那麼直觀。
更多時候,我們還是喜歡用 if .... then ... else ... 這樣的 keyword 來表達條件執行。
在 bash shell 中,我們可以如此修改上一段代碼:
[code]if comd1
then
    comd2
    comd3
else
    comd4
    comd5
fi[/code]
這也是我們在 shell script 中最常用到的 if 判斷式:
[list]只要 if 後面的 command line 返回 true 的 return value (我們最常用 test 命令來送出 return value),
然則就執行 then 後面的命令,否則執行  else 後的命令﹔fi 則是用來結束判斷式的 keyword 。[/list]

在 if 判斷式中,else 部份可以不用,但 then 是必需的。
(若 then 後不想跑任何 command ,可用" : " 這個 null command 代替)。
當然,then 或 else 後面,也可以再使用更進一層的條件判斷式,這在 shell script 設計上很常見。
若有多項條件需要"依序"進行判斷的話,那我們則可使用 elif 這樣的 keyword :
[code]if comd1; then
    comd2
elif comd3; then
    comd4
else
    comd5
fi[/code]
意思是說:
[list]若 comd1 為 true ,然則執行 comd2 ﹔
否則再測試 comd3 ,然則執行 comd4 ﹔
倘若 comd1 與 comd3 均不成立,那就執行 comd5 。[/list]

if 判斷式的例子很常見,你可從很多 shell script 中看得到,我這裡就不再舉例子了...

接下來要為大家介紹的是 case 判斷式。
雖然 if 判斷式已可應付大部份的條件執行了,然而,在某些場合中,卻不夠靈活,
尤其是在 string 式樣的判斷上,比方如下:
[code]QQ () {
    echo -n "Do you want to continue? (Yes/No): "
    read YN
    if [ "$YN" = Y -o "$YN" = y -o "$YN" = "Yes" -o "$YN" = "yes" -o "$YN" = "YES" ]
    then
        QQ
    else
        exit 0
    fi
}
QQ[/code]
從例中,我們看得出來,最麻煩的部份是在於判斷 YN 的值可能有好幾種式樣。
聰明的你或許會如此修改:
[code]...
if echo "$YN" | grep -q '^[Yy]\([Ee][Ss]\)*$'
...[/code]
也就是用 Regular Expression 來簡化代碼。(我們有機會再來介紹 RE)
只是... 是否有其它更方便的方法呢?
有的,就是用 case 判斷式即可:
[code]QQ () {
    echo -n "Do you want to continue? (Yes/No): "
    read YN
   case "$YN" in
        [Yy]|[Yy][Ee][Ss])
            QQ
            ;;
        *)
            exit 0
            ;;
    esac
}
QQ[/code]
我們常用 case 的判斷式來判斷某一變量在不同的值(通常是 string)時作出不同的處理,
比方說,判斷 script 參數以執行不同的命令。
若你有興趣、且用 Linux 系統的話,不妨挖一挖 /etc/init.d/* 裡那堆 script 中的  case 用法。
如下就是一例:
[code]case "$1" in
  start)
        start
        ;;
  stop)
        stop
        ;;
  status)
        rhstatus
        ;;
  restart|reload)
        restart
        ;;
  condrestart)
        [ -f /var/lock/subsys/syslog ] && restart || :
        ;;
  *)
        echo $"Usage: $0 {start|stop|status|restart|condrestart}"
        exit 1
esac[/code]
(若你對 positional parameter 的印像已經模糊了,請重看第 9 章吧。)

okay,十三問還剩一問而已,過幾天再來搞定之....  ^_^

[[i] 本帖最后由 網中人 于 2008-10-30 02:25 编辑 [/i]]

2004-2-1 15:10 sqwen
shell 十三問?

讲的很透彻,support!

2004-2-2 14:59 liu_xy
shell 十三問?

向他们学习

2004-2-3 17:27 網中人
shell 十三問?

13) for what?  while 與 until 差在哪?

終於,來到 shell 十三問的最後一問了...  長長吐一口氣~~~~

最後要介紹的是 shell script 設計中常見的"循環"(loop)。
所謂的 loop 就是 script 中的一段在一定條件下反覆執行的代碼。
bash shell  中常用的 loop 有如下三種:
* for
* while
* until

for loop 是從一個清單列表中讀進變量值,並"依次"的循環執行 do 到 done 之間的命令行。
例:
[code]for var in one two three four five
do
    echo -----------
    echo '$var is '$var
    echo
done[/code]
上例的執行結果將會是:
[list]1) for 會定義一個叫 var 的變量,其值依次是 one two three four five 。
2) 因為有 5 個變量值,因此 do 與 done 之間的命令行會被循環執行 5 次。
3) 每次循環均用 echo 產生三行句子。
     而第二行中不在 hard quote 之內的 $var 會依次被替換為 one two three four five 。
4) 當最後一個變量值處理完畢,循環結束。[/list]

我們不難看出,在  for loop 中,變量值的多寡,決定循環的次數。
然而,變量在循環中是否使用則不一定,得視設計需求而定。
倘若 for loop 沒有使用 in 這個 keyword 來指定變量值清單的話,其值將從 $@ (或 $* )中繼承:
[code]for var; do
    ....
done[/code]
(若你忘記了 positional parameter ,請溫習第 9 章...)

for loop 用於處理"清單"(list)項目非常方便,
其清單除了可明確指定或從 positional parameter 取得之外,
也可從變量替換或命令替換取得... (再一次提醒:別忘了命令行的"重組"特性﹗)
然而,對於一些"累計變化"的項目(如整數加減),for 亦能處理:
[code]for ((i=1;i<=10;i++))
do
   echo "num is $i"
done[/code]

除了 for loop ,上面的例子我們也可改用  while loop 來做到:
[code]num=1
while [ "$num" -le 10 ]; do
    echo "num is $num"
    num=$(($num + 1))
done[/code]

while loop 的原理與 for loop 稍有不同:
它不是逐次處理清單中的變量值,而是取決於 while 後面的命令行之 return value :
* 若為 ture ,則執行 do 與 done 之間的命令,然後重新判斷 while 後的 return value 。
* 若為 false ,則不再執行 do 與 done 之間的命令而結束循環。

分析上例:
[list]1) 在 while 之前,定義變量 num=1 。
2) 然後測試(test) $num 是否小於或等於 10 。
3) 結果為 true ,於是執行 echo 並將 num 的值加一。
4) 再作第二輪測試,此時 num 的值為 1+1=2 ,依然小於或等於 10,因此為  true ,繼續循環。
5) 直到 num 為 10+1=11 時,測試才會失敗... 於是結束循環。[/list]

我們不難發現:
* 若 while 的測試結果永遠為 true 的話,那循環將一直永久執行下去:
[code]while :; do
    echo looping...
done[/code]
上例的" : "是 bash 的 null command ,不做任何動作,除了送回 true 的 return value 。
因此這個循環不會結束,稱作死循環。
死循環的產生有可能是故意設計的(如跑 daemon),也可能是設計錯誤。
若要結束死尋環,可透過 signal 來終止(如按下 ctrl-c )。
(關於 process 與 signal ,等日後有機會再補充,十三問暫時略過。)

一旦你能夠理解 while loop 的話,那,就能理解 until loop :
* 與 while 相反,until 是在 return value 為 false 時進入循環,否則結束。
因此,前面的例子我們也可以輕鬆的用  until 來寫:
[code]num=1
until [ ! "$num" -le 10 ]; do
    echo "num is $num"
    num=$(($num + 1))
done[/code]
或是:
[code]num=1
until [ "$num" -gt 10 ]; do
    echo "num is $num"
    num=$(($num + 1))
done[/code]

okay ,關於 bash 的三個常用的 loop 暫時介紹到這裡。
在結束本章之前,再跟大家補充兩個與 loop 有關的命令:
*  break
* continue
這兩個命令常用在複合式循環裡,也就是在 do ... done 之間又有更進一層的 loop ,
當然,用在單一循環中也未嘗不可啦...  ^_^

break 是用來打斷循環,也就是"強迫結束" 循環。
若 break 後面指定一個數值 n 的話,則"從裡向外"打斷第 n 個循環,
預設值為 break 1 ,也就是打斷當前的循環。
在使用 break 時需要注意的是, 它與 return 及 exit 是不同的:
* break 是結束 loop
* return 是結束 function
* exit 是結束 script/shell

而 continue 則與 break 相反:強迫進入下一次循環動作。
若你理解不來的話,那你可簡單的看成:在 continue 到 done 之間的句子略過而返回循環頂端...
與 break 相同的是:continue 後面也可指定一個數值 n ,以決定繼續哪一層(從裡向外計算)的循環,
預設值為 continue 1 ,也就是繼續當前的循環。

在 shell script 設計中,若能善用 loop ,將能大幅度提高 script 在複雜條件下的處理能力。
請多加練習吧....


-----------

好了,該是到了結束的時候了。
婆婆媽媽的跟大家囉唆了一堆關於 shell 的基礎概念,
目的不是要告訴大家"答案",而是要帶給大家"啟發"...
在日後關於 shell 的討論中,我或許會經常用"鏈接"方式指引回來十三問中的內容,
以便我們在進行技術探討時彼此能有一些討論基礎,而不至於各說各話、徒費時力。
但,更希望十三問能帶給你更多的思考與樂趣,至為重要的是透過實作來加深理解。

是的,我很重視"實作"與"獨立思考"這兩項學習要素,若你能夠掌握其中真義,那請容我說聲:
--- 恭喜﹗十三問你沒白看了﹗  ^_^


p.s.
至於補充問題部份,我暫時不寫了。而是希望:
1) 大家擴充題目。
2) 一起來寫心得。

Good luck and happy studying!

[[i] 本帖最后由 網中人 于 2008-10-30 02:26 编辑 [/i]]

2004-2-5 14:54 sqwen
shell 十三問?

good article & thx a lot

2004-2-5 22:38 朽木可雕
shell 十三問?

真是太好了,十分精辟。只是我有一问。
for ((i=1;i<=10;i++))
do
   echo "num is $i"
done
这个例子好像不行啊。我用bash和sh执行都不行。
用bash的错误提示是:
mfor: line 1: syntax error near unexpected token `(('
mfor: line 1: `for ((i=1;i<=10;i++)) '
用sh的错误提示是:
mfor: syntax error at line 1: `(' unexpected
还请指点一下。

2004-2-6 00:01 網中人
shell 十三問?

請問你是用 bash 嗎?
若你寫成 script 的話,確定第一行有定義 interpreter :
#!/bin/bash

2004-2-6 12:24 朽木可雕
shell 十三問?

[quote][i]原帖由 "網中人" 发表:[/i]
請問你是用 bash 嗎?
若你寫成 script 的話,確定第一行有定義 interpreter :
#!/bin/bash[/quote]
谢谢,已经解决了,原来是掉了一个!号。 不好意思。

页: 1 2 3 [4] 5 6 7 8 9 10 11 12 13
查看完整版本: shell 十三問?