歡迎光臨
每天分享高質量文章

有關bash,我希望我能知曉的十件事

來源:Python程式員

ID:pythonbuluo

簡介

我之前的一篇文章比我預想的更受歡迎,因此我想再寫一篇文章來介紹一些不太知名的bash功能

正如之前所言,由於我覺得bash是一種要經常使用(且需理解)的技術,所以我在研究bash時寫了一本書。雖然許多人並不熟悉bash,但我覺得他們也認為非常重要便足夠令人欣喜。

1)^x^y^

我總在使用的一個小技巧。

從來沒有輸入過類似的命令?

$ grp somestring somefile
-bash: grp: command not found

哎,這個命令敲錯了,所以你要敲“↑”,然後敲”←“直到”p“,然後輸入”e”再執行。

或者這樣輸入:

$ ^rp^rep^
grep 'somestring' somefile
$

你可能需要註意的一個細節是:

$ grp rp somefile
$ ^rp^rep^
$ grep rp somefile

如果你想搜尋“rep”,那你就要深入研究man page,學會使用這個更強大的命令:

$ grp rp somefile
$ !!:gs/rp/rep
grep rep somefile
$

我不會在這裡解釋這個用法。。。

2)pushd/popd

這個在指令碼中非常好用,特別是在迴圈中

如下所示,假設你正在寫一個進入退出檔案夾的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' ...

3) shopt vs set

這兩個命令困擾了我一陣子。

兩者之間有什麼不同呢?

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選項提供了一種方式

但是我也不確定……如果你知道為什麼,請告訴我。

4)Here Docs 與 Here Strings

“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'

5)字串變數的操作

以前你可能是像下麵展示的那樣寫程式碼,用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}

6)變數的預設值

這些對寫指令碼來說非常好用。

如果你有一個沒有賦值的變數,你可以像這樣給它“賦預設值”

建立一個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}(等號,不是破折號),但是註意這不適用於指令碼或函式中的位置變數。嘗試修改上面的指令碼來看它是如何失敗的。

7)Traps

當一個訊號被送到指令碼時,內建的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’中,來達成函式的目的。

8)Shell變數

瞭解可用的標準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}

9) Extglobs

如果你真的沉迷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那樣的“合適的”程式語言。

10)關聯陣列

談到移植到其他語言,一條重要的規則是,如果我需要用到陣列,那麼我會放棄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+版本

英文原文:https://zwischenzugs.com/2018/01/21/ten-more-things-i-wish-id-known-about-bash/  
譯者:D  

《Linux雲端計算及運維架構師高薪實戰班》2018年07月16日即將開課中,120天衝擊Linux運維年薪30萬,改變速約~~~~

    *宣告:推送內容及圖片來源於網路,部分內容會有所改動,版權歸原作者所有,如來源資訊有誤或侵犯權益,請聯絡我們刪除或授權事宜。

    – END –


    更多Linux好文請點選【閱讀原文】

    ↓↓↓

    贊(0)

    分享創造快樂