About me

Thursday 5 January 2017

[Linux] bash - set指令

  
介紹

Linux 這個Unix-like的架構所使用的shell - 『Bourne shell』,底下有內建一個『set』指令,主要用來設置/取消/檢視shell的參數和變數。


語法 set [--abefhkmnptuvxBCHP] [-o option-name] [arg ...]
參數 介紹參數前要先介紹一下要如何看目前的設置: $ echo $- himBH 看到上面目前的參數是『himBH』,要開關這些參數的話就要用『-/+』,一般來說我們的認知,開的話應該配『+』,關配『-』,但是這邊剛好相反,開的話用『-』,關則用『+』,舉個例子來說,我想要關閉歷史紀錄的『!』功能,也就是『H』選項,如下: $ set +H $ echo $- himB 然後就發現H不見了。底下是所有參數的說明還有例子,注意一般來說都有兩種方式可以去設定flag,一種是直接用一個字符,另外一種則是用『-o 字串』的方式,像是底下這個case都是一樣的: #case1 $ set -a #case2 $ set -o allexport -a 或是『-o allexport』 這個flag是關於變數的輸出,例子如下: # 假設我有個script叫做test.sh,內容如下 #!/bin/bash echo $TEST #下面兩個case會呼叫到/test.sh #case 1 $ TEST="exportTest" $ ./TEST.sh #case 2 $ set -a $ echo $- ahimBh $ TEST="exportTest" $ ./TEST.sh exportTest 一般來說script要用parent bash的變數的話,正規來說會用『export』的方式,如果沒有的話就會像第一個case一樣,裡面會接不到值,但是也可以向第二個case一樣設定這個flag,裡面就會接的到。 -b 或是 『-o notify』 background job 執行結束後是否會通知,例子如下: $ echo $- himBH $ sleep 2& [1] 21790 第一個例子沒有設定這個flag,所以執行完畢之後也不會通知你,看一下第二個例子: $ set -b $ echo $- bhimBH $ sleep 2& [1] 21790 $ [1]+ Done sleep 2 第二個例子很清楚的看到job結束後,系統會發個通知。 -e 或是『-o errexit』 如果command執行完後回傳值為非0,則會馬上中斷,這個參數通常是使用在shell script裡面, 假設我有個script,內容如下: #!/bin/bash ls test1 ls test2 ls test3 執行後,結果如下: ls: cannot access 'test1': No such file or directory ls: cannot access 'test2': No such file or directory ls: cannot access 'test3': No such file or directory 三個檔案都沒有,但是如果將script改成: #!/bin/bash set -e ls test1 ls test2 ls test3 執行後結果如下: ls: cannot access 'test1': No such file or directory 因為第一個指令就已經回傳非零值了,所以系統會直接中斷。 -f 或是 『-o noglob』 這個選項是關於檔名的正規表示法,舉個簡單的例子: $ ls test* test1 test10 test2 test3 $ set -f $ echo $- fhimBH $ ls test* ls: cannot access 'test*': No such file or directory 還沒設定時使用『ls test*』確實有列出相關的檔案,但是一旦設定了『-f』以後,正規表示法就無法使用了。 -h 或是 『-o hashall』 開啟或關閉bash 的hash功能,舉例如下: #開啟hash功能 $ set -h # 關閉command是否存在的檢查 $ shopt -u checkhash # 將date這個command跟路徑/tmp/fake/date 綁在一起 $ hash -p /tmp/fake/date date $ date bash: /tmp/fake/date: No such file or directory #關閉hash功能 $ set +h $ date Wed Jan 4 12:15:15 CST 2017 第一個case因為開啟hash(預設也是開啟),然後又故意把hash table裡面的date值給改掉,所以造成找不到這個command,所以在第二個case時,將hash功能關掉以後就會直接去找PATH變數了。 -k或是『-o keyword』 這個參數其實是有點歷史因素,過渡時期下的產物,會影響到早期的parser的執行順序,但是根據POSIX的文件,因為現在parser都已經會reorder了,所以其實這個參數已經是『never nedded』。 -m 或是『-o monitor』 這個flag是控制『job control』: #取消job control的功能 $ set +m #發起一個任務 $ sleep 10& #將背景任務喚回前景 $ fg 1 -bash: fg: no job control 設定這個flag以後,如上例,發現無法切換jobs。 -n 或是『-o noexec』 只會讀取這個command,但是不會去執行它,在CMD上面沒什麼用,但是如果要用在shell script裡面的話(我覺得還是沒什麼用就對了),用法就如下: $ set -n $ ps -aux $ set +n -o 這個參數無法獨自存在,它一定要後面在加一個值,如同其他例子一樣,可以用一個字符或是『-o 值』的方式來用,但是因為其他參數我都有大概講解過了,所以這邊只介紹一下沒有的。 -o history 開啟或關閉history記錄功能。 #開啟history記錄功能 $ set -o history #關閉history記錄功能 $ set +o history -o interactive-comments 在互動模式底下是否允許註解: #case1 $ set -o interactive-comments $ #tttt #case2 set +o interactive-comments $ #tttt #tttt: command not found -o nolog 主要是預防函數的定義會寫到History裡面,但是沒試過,所以不清楚到底是什麼樣子的定義會寫到History。 -o ignoreeof 如果shell讀到EOF的話不會結束。 -o emacs 使用emacs-style的編輯介面。 -o vi 使用vi-style的編輯介面。 -o posix 將bash的行為改成POSIX的標準。 -o pipefail pipeline的退出狀態是在最後一個退出的command(成功的話就是最右邊,失敗的話就是失敗的那一個),但是如果啟用這個flag的話,退出狀態就會變成最右邊的那個command的狀態。 -p或是『-o privileged』 這個很難懂,所以先直接整個貼上: Turned on whenever the real and effective user ids do not match. Disables processing of the $ENV file and importing of shell functions. Turning this option off causes the effective uid and gid to be set to the real uid and gid. -t 或是『-o onecmd』 讀取並執行一個command以後就exit,我試過在script底下沒有用,只能用在互動式CMD底下,例子如下: $ set -t; echo "test 1" > file1 上面這個例子就跟底下這個例子一樣的功能: $ echo "test 1" > file1; exit -u 或是 『-o nounset』 這個flag主要是關於如果變數沒有值會不會報錯,例子如下: # case 1 $ echo $- himBH $ echo $TEST # case 2 $ set -u $ echo $- himuBH $ echo $TEST bash: TEST: unbound variable 同樣是沒值,第一個例子只會印出空白,但是第二個例子就會報錯。 -v 或是『-o verbose』 這個flag主要決定是否要在執行command前,先印出這條command在stand output上。 #case 1 $ ls test.sh test.sh #case 2 $ set -v $ls test.sh ls test.sh test.sh 第一個case是一般的案例,執行後只有結果,第二個case則是設定flag以後,結果在執行command之前,先印出這條command。 -x 或是 『-o xtrace』 跟『-v』有點像,執行command之前先印出來,但是重點是,印出來的是原始完整的command,並且前面加個『+』,而不是『alias』過的command。 # case 1 $ ll total 12 drwxr-xr-x 2 root root 4096 Jan 4 15:06 ./ drwxr-xr-x 13 hugh hugh 4096 Jan 4 09:14 ../ -rwxrwxrwx 1 root root 59 Jan 4 14:19 test.sh* # case2 $ set -x $ ll + ls --color=auto -alF total 12 drwxr-xr-x 2 root root 4096 Jan 4 15:06 ./ drwxr-xr-x 13 hugh hugh 4096 Jan 4 09:14 ../ -rwxrwxrwx 1 root root 59 Jan 4 14:19 test.sh* 看到第二個case,會列出『ll』原本的原始command『+ ls --color=auto -alF』。 -B 或是『-o braceexpand』 這個flag是關於括號的,舉個底下的例子來看: # case1 $ echo test{1..10} test1 test2 test3 test4 test5 test6 test7 test8 test9 test10 # case 2 $ set +B $ echo $- himH $ echo test{1..10} test{1..10} 第一個case一般來說會直接印出1~10的範圍,但是因為第二個case已經取消這個flag,所以不會自動幫你展開。 -C 或是 『-o noclobber』 如果啟用的話,既存的檔案不會因為redirection of output 『>』而複寫。 $ echo "test" > file1 $ set -C $ echo $- himBCH echo "overwrite" > file1 bash: file1: cannot overwrite existing file 因為file1一開始已經有寫值了,所以設定這個flag以後,bash就不讓你覆寫這個檔案。 -E或是『-o errtrace』 如果設定這個flag的話,ERR trap就會繼承自shell 函數。 -H 或是『-o histexpand』 是否要允許用『!』然後加history number就可以執行歷史紀錄,舉例如下: # case 1 $ history .... 1228 echo "test" echo "test" test # case 2 $ set +H $ !1228 !1228: command not found 第一個case 可以正常的執行編號第1228的command,印出test,但是第二個case取消flag以後就無法用這個功能。 -P或是『-o physical』 當執行像是『cd』這種command時,是否要允許直接用symbolic link來顯示: #case 1 root@host:/tmp$ ln -s /test folder1 root@host:/tmp$ ls -l folder1 lrwxrwxrwx 1 root root 5 一 5 12:04 folder1 -> /test root@host:/tmp$ cd folder1 root@myhost:/tmp/folder1# pwd /tmp/folder1 #case 2 root@host:/tmp$ ln -s /test folder1 root@host:/tmp$ ls -l folder1 lrwxrwxrwx 1 root root 5 一 5 12:04 folder1 -> /test root@host:/tmp$ cd folder1 root@myhost:/test# pwd /test 第一個case發現路徑雖然還是在原本的底下,但是實際上內容已經在『/test』裡面了,但是第二個case flag打開之後,整個會把路徑切換到『/test』底下。 -T或是『-o functrace』 如果設定這個flag的話,DEBUG trap就會繼承自shell函數。
判斷是否是互動模式 從bash-cookbook裡面看到,如果要判斷你的bash環境是否是互動模式(interactive shell)的話可以由『$-』裡面看看是否有『i』的存在,所謂的互動模式就是當你登入tty時,你的shell就會讀取相對應的startup檔案(像是『~/.bashrc』或是『./profile』之類的),然後顯示『prompt』,並且預設是啟動『job control』的,啟動結束後,接下來shell就等你輸入命令了,所以可想而知通常shell script裡面所提供的環境就會是『non-interactive』非互動模式,想要判斷是否是互動模式的話,可以由底下的scirpt來判斷,主要就是判斷輸出是否有『i』的存在: #!/usr/bin/env bash #from bash-cookbok case "$-" in *i*) # Code for interactive shell here echo "interactive" ;; *) # Code for non-interactive shell here echo "non interacrive" ;; esac
Ref: http://linuxcommand.org/lc3_man_pages/seth.html http://ss64.com/bash/set.html http://pubs.opengroup.org/onlinepubs/009696799/utilities/set.html http://pubs.opengroup.org/onlinepubs/009696799/utilities/sh.html http://tldp.org/LDP/abs/html/intandnonint.html bash-cookbook

No comments:

Post a Comment