我以前的一篇文章比我預想的更受歡迎,所以我想再寫一篇文章來介紹一些不太知名的bash功能python
正如以前所言,因爲我以爲bash是一種要常用(且需理解)的技術,因此我在研究bash時寫了一本書。雖然許多人並不熟悉bash,但我以爲他們也認爲很是重要便足夠使人欣喜。git
我總在使用的一個小技巧。shell
歷來沒有輸入過相似的命令?編程
$ grp somestring somefile -bash: grp: command not found
哎,這個命令敲錯了,因此你要敲「↑」,而後敲」←「直到」p「,而後輸入」e"再執行。數組
或者這樣輸入:bash
$ ^rp^rep^ grep 'somestring' somefile $
你可能須要注意的一個細節是:app
$ grp rp somefile $ ^rp^rep^ $ grep rp somefile
若是你想搜索「rep」,那你就要深刻研究man page,學會使用這個更強大的命令:編程語言
$ grp rp somefile $ !!:gs/rp/rep grep rep somefile $
我不會在這裏解釋這個用法。。。ide
這個在腳本中很是好用,特別是在循環中函數
以下所示,假設你正在寫一個進入退出文件夾的for循環:
for d1 in $(ls -d */) do # Store original working directory. original_wd="$(pwd)" cd "$d1" for d2 in $(ls -d */) do pushd "$d2" # Do something popd done # Return to original working directory cd "${original_wd}" done
你能夠像這樣使用pushd棧來重寫上方代碼:
for d1 in $(ls -d *) do pushd "$d1" for d2 in $(ls -d */) do pushd "$d2" # Do something popd done popd done
它能夠追蹤記錄你切換的目錄並進行入棧或出棧
注意,當使用pushd出現錯誤時,可能會丟失棧的記錄而且popd屢次。所以你可能會想要在腳本中使用set -e(見上一篇文章)
固然也能夠用cd -,可是它不會使用棧——僅僅返回前一個目錄
cd ~ cd /tmp cd blah cd - # Back to /tmp cd - # Back to 'blah' cd - # Back to /tmp cd - # Back to 'blah' ...
這兩個命令困擾了我一陣子。
二者之間有什麼不一樣呢?
set在以前的文章已經介紹過了,而shopt看起來與之類似。只輸入shopt會顯示一系列選項:
$ shopt cdable_vars off cdspell on checkhash off checkwinsize on cmdhist on compat31 off dotglob off
我在這裏( here)找到了一些答案。
從根本上說,彷佛有一系列的bash(和其餘shells)創建在sh之上,而添加shopt命令則爲設置額外的shell選項提供了一種方式
可是我也不肯定……若是你知道爲何,請告訴我。
「Here Docs」是在shell中用一些語句建立的文件。
「訣竅」很簡單。定義一個用於結束的單詞,則在這個單詞單獨出如今一行以前的全部輸入行將構成文件。
像這樣:
$ cat > afile << SOMEENDSTRING > here is a doc > it has three lines > SOMEENDSTRING alone on a line will save the doc > SOMEENDSTRING $ cat afile here is a doc it has three lines SOMEENDSTRING alone on a line will save the doc $
注意:
· 若是結束單詞不是「單獨」出如今一行中,那它能夠構成文件
· SOMEENDSTRING一般是END,但這僅僅只是習慣
更不爲人知的是「here string」:
$ cat > asd <<< 'This file has one line'
之前你多是像下面展現的那樣寫代碼,用sed一類的工具來操做字符串:
$ VAR='HEADERMy voice is my passwordFOOTER' $ PASS="$(echo $VAR | sed 's/^HEADER(.*)FOOTER/1/')" $ echo $PASS
可是你可能不知道bash自己也是能夠的。
這意味着你能夠省去大量的sed和awk。
一種重寫上述代碼的方式以下所示:
$ VAR='HEADERMy voice is my passwordFOOTER' $ PASS="${VAR#HEADER}" $ PASS="${PASS%FOOTER}" $ echo $PASS
·#表示「從字符串開頭開始匹配並刪除所給的模式串」
·%表示「從字符串結尾開始匹配並刪除所給的模式串」
在個人電腦上,後一種方法比前一種快兩倍。而且(令我吃驚的是),他的速度跟相似功能的python腳本速度大體至關
若是你想使用通配符(見前文)模式串並採用貪婪模式,你須要雙寫:
$ VAR='HEADERMy voice is my passwordFOOTER' $ echo ${VAR##HEADER*} $ echo ${VAR%%*FOOTER}
這些對寫腳原本說很是好用。
若是你有一個沒有賦值的變量,你能夠像這樣給它「賦默認值」
建立一個default.sh文件,寫入以下內容:
#!/bin/bash FIRST_ARG="${1:-no_first_arg}" SECOND_ARG="${2:-no_second_arg}" THIRD_ARG="${3:-no_third_arg}" echo ${FIRST_ARG} echo ${SECOND_ARG} echo ${THIRD_ARG}
如今執行chmod +x default.sh並用./default.sh first second來運行腳本:
觀察第三個參數的默認值是如何被分配的,而不是前兩個。
你也能夠直接用${VAR:=defaultval}(等號,不是破折號),可是注意這不適用於腳本或函數中的位置變量。嘗試修改上面的腳原本看它是如何失敗的。
當一個信號被送到腳本時,內建的trap能夠用於「捕獲」
下面是我用在本身的chepci腳本中的一個例子:
function cleanup() { rm -rf "${BUILD_DIR}" rm -f "${LOCK_FILE}" # get rid of /tmp detritus, leaving anything accessed 2 days ago+ find "${BUILD_DIR_BASE}"/* -type d -atime +1 | rm -rf echo "cleanup done" } trap cleanup TERM INT QUIT
任何使用TERM信號的CTRL-C,CTRL-或終止程序的操做將會首先調用cleanup
注意:
·trap的邏輯可能很是棘手(例如處理信號競爭條件)
·KILL信號不能以這種方式捕獲
可是大多數狀況下,我會把它用於相似上述的‘cleanup’中,來達成函數的目的。
瞭解可用的標準shell變量是很是值得的。這些是我最喜歡的。
RANDOM
不要依賴這個來加密堆棧,但你能夠生成隨機數字,例如在腳本中建立臨時文件時:
$ echo ${RANDOM} 16313 $ # Not enough digits? $ echo ${RANDOM}${RANDOM} 113610703 $ NEWFILE=/tmp/newfile_${RANDOM} $ touch $NEWFILE
REPLY
不在須要給read一個變量名稱
$ read my input $ echo ${REPLY}
LINENO 與 SECONDS
方便調試
$ echo ${LINENO} 115 $ echo ${SECONDS}; sleep 1; echo ${SECONDS}; echo $LINENO 174380 174381 116
注意,即使使用;來隔開命令,上面的代碼也要分兩行
TMOUT
能夠用來超時讀取,在一些腳本中真的很好用
#!/bin/bash TMOUT=5 echo You have 5 seconds to respond... read echo ${REPLY:-noreply}
若是你真的沉迷bash不能自拔,那麼你可能想要加強你的通配功能。你能夠經過設置shell中的extglob選項。這是設置方法:
shopt -s extglob A="12345678901234567890" B=" ${A} "
如今來看看你是否能指出如下這些語句各自的功能:
echo "B |${B}|" echo "B#+( ) |${B#+( )}|" echo "B#?( ) |${B#?( )}|" echo "B#*( ) |${B#*( )}|" echo "B##+( )|${B##+( )}|" echo "B##*( )|${B##*( )}|" echo "B##?( )|${B##?( )}|"
雖然它可能頗有用,可是很難想象出一種你必需要用這種方式的狀況。一般你會使用一些更適合相應任務的工具(像sed)或者直接放棄bash去使用一些像python那樣的「合適的」編程語言。
談到移植到其餘語言,一條重要的規則是,若是我須要用到數組,那麼我會放棄bash,使用python(爲此我甚至建立了一個Docker Container來運行一個專門的工具)
知道讀到它我才知道,在bash中有關聯數組
如下是演示:
$ declare -A MYAA=([one]=1 [two]=2 [three]=3) $ MYAA[one]="1" $ MYAA[two]="2" $ echo $MYAA $ echo ${MYAA[one]} $ MYAA[one]="1" $ WANT=two $ echo ${MYAA[$WANT]}
注意僅適用於bash4.x+版本
本文由馬哥教育Linux學院提供支持。