Shell 快速指南

███████╗██╗  ██╗███████╗██╗     ██╗                           
██╔════╝██║  ██║██╔════╝██║     ██║                           
███████╗███████║█████╗  ██║     ██║                           
╚════██║██╔══██║██╔══╝  ██║     ██║                           
███████║██║  ██║███████╗███████╗███████╗

概述

什么是 shell

Shell 是一個用 C 語言編寫的程序,它是用戶使用 Linux 的橋梁。

Shell 既是一種命令語言,又是一種程序設計語言。

Shell 是指一種應用程序,這個應用程序提供了一個界面,用戶通過這個界面訪問 Linux 內核的服務。

Ken Thompson 的 sh 是第一種 Unix Shell,Windows Explorer 是一個典型的圖形界面 Shell。

什么是 shell 腳本

Shell 腳本(shell script),是一種為 shell 編寫的腳本程序,一般文件后綴為?.sh

業界所說的 shell 通常都是指 shell 腳本,但 shell 和 shell script 是兩個不同的概念。

Shell 環境

Shell 編程跟 java、php 編程一樣,只要有一個能編寫代碼的文本編輯器和一個能解釋執行的腳本解釋器就可以了。

Shell 的解釋器種類眾多,常見的有:

  • sh?- 即 Bourne Shell。sh 是 Unix 標準默認的 shell。
  • bash?- 即 Bourne Again Shell。bash 是 Linux 標準默認的 shell。
  • fish?- 智能和用戶友好的命令行 shell。
  • xiki?- 使 shell 控制臺更友好,更強大。
  • zsh?- 功能強大的 shell 與腳本語言。

指定腳本解釋器

在 shell 腳本,#!?告訴系統其后路徑所指定的程序即是解釋此腳本文件的 Shell 解釋器。#!?被稱作shebang(也稱為 Hashbang )。

所以,你應該會在 shell 中,見到諸如以下的注釋:

  • 指定 sh 解釋器
#!/bin/sh
  • 指定 bash 解釋器
#!/bin/bash

注意

上面的指定解釋器的方式是比較常見的,但有時候,你可能也會看到下面的方式:

#!/usr/bin/env bash

這樣做的好處是,系統會自動在?PATH?環境變量中查找你指定的程序(本例中的bash)。相比第一種寫法,你應該盡量用這種寫法,因為程序的路徑是不確定的。這樣寫還有一個好處,操作系統的PATH變量有可能被配置為指向程序的另一個版本。比如,安裝完新版本的bash,我們可能將其路徑添加到PATH中,來“隱藏”老版本。如果直接用#!/bin/bash,那么系統會選擇老版本的bash來執行腳本,如果用#!/usr/bin/env bash,則會使用新版本。

模式

shell 有交互和非交互兩種模式。

交互模式

簡單來說,你可以將 shell 的交互模式理解為執行命令行。

看到形如下面的東西,說明shell處于交互模式下:

user@host:~$

接著,便可以輸入一系列 Linux 命令,比如?lsgrepcdmkdirrm?等等。

非交互模式

簡單來說,你可以將 shell 的非交互模式理解為執行 shell 腳本。

在非交互模式下,shell 從文件或者管道中讀取命令并執行。

當 shell 解釋器執行完文件中的最后一個命令,shell 進程終止,并回到父進程。

可以使用下面的命令讓shell以非交互模式運行:

sh /path/to/script.sh
bash /path/to/script.sh

上面的例子中,script.sh是一個包含shell解釋器可以識別并執行的命令的普通文本文件,shbash是shell解釋器程序。你可以使用任何喜歡的編輯器創建script.sh(vim,nano,Sublime Text, Atom等等)。

除此之外,你還可以通過chmod命令給文件添加可執行的權限,來直接執行腳本文件:

chmod +x /path/to/script.sh #使腳本具有執行權限
/path/to/test.sh

這種方式要求腳本文件的第一行必須指明運行該腳本的程序,比如:

#!/bin/bash
echo "Hello, world!"

上面的例子中,我們使用了一個很有用的命令echo來輸出字符串到屏幕上。

Shell 編程

由于 bash 是 Linux 標準默認的 shell,可以說 bash 是 shell 編程的基礎。

所以,下面將全部基于 bash 來講解 shell 編程。

此外,本篇章主要介紹的是 shell 編程的語法,對于 linux 指令不做任何介紹。

解釋器

前面雖然兩次提到了#!?,但是本著重要的事情說三遍的精神,這里再強調一遍:

在 shell 腳本,#!?告訴系統其后路徑所指定的程序即是解釋此腳本文件的 Shell 解釋器。#!?被稱作shebang(也稱為 Hashbang )。

#!?決定了腳本可以像一個獨立的可執行文件一樣執行,而不用在終端之前輸入sh,?bash,?python,?php等。

示例:

# 以下兩種方式都可以指定 shell 解釋器為 bash,第二種方式更好
#!/bin/bash
#!/usr/bin/env bash

注釋

shell 語法支持注釋。注釋是特殊的語句,會被 shell 解釋器忽略。它們以?#?開頭,到行尾結束。

示例:

#!/bin/bash
### This script will print your username.
whoami

Tip: 用注釋來說明你的腳本是干什么的,以及為什么這樣寫。

變量

跟許多程序設計語言一樣,你可以在 bash 中創建變量。

Bash 中沒有數據類型,bash 中的變量可以保存一個數字、一個字符、一個字符串等等。同時無需提前聲明變量,給變量賦值會直接創建變量。

你可以創建三種變量:局部變量環境變量以及作為位置參數的變量。

局部變量

局部變量是僅在某個腳本內部有效的變量。它們不能被其他的程序和腳本訪問。

局部變量可以用?=?聲明(作為一種約定,變量名、=、變量的值之間不應該有空格),其值可以$?訪問到。

示例:

username="zhangpeng"  ### 聲明變量
echo $username          ### 輸出變量的值
unset username          ### 刪除變量

可以用?local?關鍵字聲明屬于某個函數的局部變量。這樣聲明的變量會在函數結束時消失。

local local_var="I'm a local value"

環境變量

環境變量是對當前 shell 會話內所有的程序或腳本都可見的變量。

創建它們跟創建局部變量類似,但使用的是?export?關鍵字。

export global_var="I'm a global value"

常見的環境變量:

變量描述
$HOME當前用戶的用戶目錄
$PATH用分號分隔的目錄列表,shell會到這些目錄中查找命令
$PWD當前工作目錄
$RANDOM0到32767之間的整數
$UID數值類型,當前用戶的用戶ID
$PS1主要系統輸入提示符
$PS2次要系統輸入提示符

這里?有一張更全面的 Bash 環境變量列表。

位置參數

位置參數是在調用一個函數并傳給它參數時創建的變量。

位置參數變量表:

變量描述
$0腳本名稱
$1 … $9第1個到第9個參數列表
${10} … ${N}第10個到N個參數列表
$*?or?$@ 除了$0外的所有位置參數
$#不包括$0在內的位置參數的個數
$FUNCNAME函數名稱(僅在函數內部有值)

示例:

在下面的例子中,位置參數為:$0='./script.sh'$1='foo'$2='bar'

$ ./script.sh foo bar

變量可以有默認值。我們可以用如下語法來指定默認值:

### 如果變量為空,賦給他們默認值
: ${VAR:='default'}
: ${1:='first'}
echo "\$1 : " $1
: ${2:='second'}
echo "\$2 : " $2### 或者
FOO=${FOO:-'default'}

Shell擴展

擴展?發生在一行命令被分成一個個的?記號(tokens)?之后。換言之,擴展是一種執行數學運算的機制,還可以用來保存命令的執行結果,等等。

感興趣的話可以閱讀關于shell擴展的更多細節。

大括號擴展

大括號擴展讓生成任意的字符串成為可能。它跟?文件名擴展?很類似,舉個例子:

echo beg{i,a,u}n ### begin began begun

大括號擴展還可以用來創建一個可被循環迭代的區間。

echo {0..5} ### 0 1 2 3 4 5
echo {00..8..2} ### 00 02 04 06 08

命令置換

命令置換允許我們對一個命令求值,并將其值置換到另一個命令或者變量賦值表達式中。當一個命令被```$()`包圍時,命令置換將會執行。舉個例子:

now=`date +%T`
### or
now=$(date +%T)echo $now ### 19:08:26

算數擴展

在bash中,執行算數運算是非常方便的。算數表達式必須包在$(( ))中。算數擴展的格式為:

result=$(( ((10 + 5*3) - 7) / 2 ))
echo $result ### 9

在算數表達式中,使用變量無需帶上$前綴:

x=4
y=7
echo $(( x + y ))     ### 11
echo $(( ++x + y++ )) ### 12
echo $(( x + y ))     ### 13

單引號和雙引號

單引號和雙引號之間有很重要的區別。在雙引號中,變量引用或者命令置換是會被展開的。在單引號中是不會的。舉個例子:

echo "Your home: $HOME" ### Your home: /Users/<username>
echo 'Your home: $HOME' ### Your home: $HOME

當局部變量和環境變量包含空格時,它們在引號中的擴展要格外注意。隨便舉個例子,假如我們用echo來輸出用戶的輸入:

INPUT="A string  with   strange    whitespace."
echo $INPUT   ### A string with strange whitespace.
echo "$INPUT" ### A string  with   strange    whitespace.

調用第一個echo時給了它5個單獨的參數 ——?$INPUT?被分成了單獨的詞,echo在每個詞之間打印了一個空格。第二種情況,調用echo時只給了它一個參數(整個$INPUT的值,包括其中的空格)。

來看一個更嚴肅的例子:

FILE="Favorite Things.txt"
cat $FILE   ### 嘗試輸出兩個文件: `Favorite` 和 `Things.txt`
cat "$FILE" ### 輸出一個文件: `Favorite Things.txt`

盡管這個問題可以通過把FILE重命名成Favorite-Things.txt來解決,但是,假如這個值來自某個環境變量,來自一個位置參數,或者來自其它命令(find,?cat, 等等)呢。因此,如果輸入?可能?包含空格,務必要用引號把表達式包起來。

數組

跟其它程序設計語言一樣,bash中的數組變量給了你引用多個值的能力。在bash中,數組下標也是從0開始,也就是說,第一個元素的下標是0。

跟數組打交道時,要注意一個特殊的環境變量IFSIFS,全稱?Input Field Separator,保存了數組中元素的分隔符。它的默認值是一個空格IFS=' '

創建數組

在 bash 中有好幾種方法創建一個數組

array[0] = val
array[1] = val
array[2] = val
array=([2]=val [0]=val [1]=val)
array=(val val val)

獲取數組元素

  • 獲取數組的單個元素:
echo ${array[1]}
  • 獲取數組的所有元素:
echo ${array[*]}
echo ${array[@]}

上面兩行有很重要(也很微妙)的區別,假設某數組元素中包含空格:

colors[0]=Red
colors[1]="Dark Green"
colors[2]=Blue

為了將數組中每個元素單獨一行輸出,我們用內建的printf命令:

printf "+ %s\n" ${colors[*]}# 輸出:
# + Red
# + Dark
# + Green
# + Blue

為什么Desertfig各占了一行?嘗試用引號包起來:

printf "+ %s\n" "${colors[*]}"# 輸出:
# + Red Dark Green Blue

現在所有的元素都跑去了一行 —— 這不是我們想要的!為了解決這個痛點,${colors[@]}閃亮登場:

printf "+ %s\n" "${colors[@]}"# 輸出:
+ Red
+ Dark Green
+ Blue

在引號內,${colors[@]}將數組中的每個元素擴展為一個單獨的參數;數組元素中的空格得以保留。

  • 訪問數組的部分元素:
echo ${array[@]:0:2}

在上面的例子中,${array[@]}?擴展為整個數組,:0:2取出了數組中從0開始,長度為2的元素。

獲取數組長度

echo ${#array[*]}

向數組中添加元素

向數組中添加元素也非常簡單:

colors=(Yellow "${colors[@]}" Pink Black)
echo ${colors[@]}# 輸出:
# Yellow Red Dark Green Blue Pink Black

上面的例子中,${colors[@]}?擴展為整個數組,并被置換到復合賦值語句中,接著,對數組colors的賦值覆蓋了它原來的值。

從數組中刪除元素

unset命令來從數組中刪除一個元素:

unset colors[0]
echo ${colors[@]}# 輸出:
# Red Dark Green Blue Pink Black

運算符

算術運算符

下表列出了常用的算術運算符,假定變量 a 為 10,變量 b 為 20:

運算符說明舉例
+加法 expr $a + $b?結果為 30。
-減法 expr $a - $b?結果為 -10。
*乘法 expr $a \* $b?結果為 200。
/除法 expr $b / $a?結果為 2。
%取余 expr $b % $a?結果為 0。
=賦值 a=$b?將把變量 b 的值賦給 a。
==相等。用于比較兩個數字,相同則返回 true。 [ $a == $b ]?返回 false。
!=不相等。用于比較兩個數字,不相同則返回 true。 [ $a != $b ]?返回 true。

注意:條件表達式要放在方括號之間,并且要有空格,例如:?[a==a==b]?是錯誤的,必須寫成?[?a==a==b ]

示例:

a=10
b=20echo "a=$a, b=$b"val=`expr $a + $b`
echo "a + b : $val"val=`expr $a - $b`
echo "a - b : $val"val=`expr $a \* $b`
echo "a * b : $val"val=`expr $b / $a`
echo "b / a : $val"val=`expr $b % $a`
echo "b % a : $val"if [ $a == $b ]
thenecho "a 等于 b"
fi
if [ $a != $b ]
thenecho "a 不等于 b"
fi

關系運算符

關系運算符只支持數字,不支持字符串,除非字符串的值是數字。

下表列出了常用的關系運算符,假定變量 a 為 10,變量 b 為 20:

運算符說明舉例
-eq檢測兩個數是否相等,相等返回 true。 [ $a -eq $b ]返回 false。
-ne檢測兩個數是否相等,不相等返回 true。 [ $a -ne $b ]?返回 true。
-gt檢測左邊的數是否大于右邊的,如果是,則返回 true。 [ $a -gt $b ]?返回 false。
-lt檢測左邊的數是否小于右邊的,如果是,則返回 true。 [ $a -lt $b ]?返回 true。
-ge檢測左邊的數是否大于等于右邊的,如果是,則返回 true。 [ $a -ge $b ]?返回 false。
-le檢測左邊的數是否小于等于右邊的,如果是,則返回 true。 [ $a -le $b ]返回 true。

示例:

a=10
b=20if [ $a -eq $b ]
thenecho "$a -eq $b : a 等于 b"
elseecho "$a -eq $b: a 不等于 b"
fi
if [ $a -ne $b ]
thenecho "$a -ne $b: a 不等于 b"
elseecho "$a -ne $b : a 等于 b"
fi
if [ $a -gt $b ]
thenecho "$a -gt $b: a 大于 b"
elseecho "$a -gt $b: a 不大于 b"
fi
if [ $a -lt $b ]
thenecho "$a -lt $b: a 小于 b"
elseecho "$a -lt $b: a 不小于 b"
fi
if [ $a -ge $b ]
thenecho "$a -ge $b: a 大于或等于 b"
elseecho "$a -ge $b: a 小于 b"
fi
if [ $a -le $b ]
thenecho "$a -le $b: a 小于或等于 b"
elseecho "$a -le $b: a 大于 b"
fi

布爾運算符

下表列出了常用的布爾運算符,假定變量 a 為 10,變量 b 為 20:

運算符說明舉例
!非運算,表達式為 true 則返回 false,否則返回 true。 [ ! false ]?返回 true。
-o或運算,有一個表達式為 true 則返回 true。 [ $a -lt 20 -o $b -gt 100 ]?返回 true。
-a與運算,兩個表達式都為 true 才返回 true。 [ $a -lt 20 -a $b -gt 100 ]?返回 false。

示例:

a=10
b=20echo "a=$a, b=$b"if [ $a != $b ]
thenecho "$a != $b : a 不等于 b"
elseecho "$a != $b: a 等于 b"
fi
if [ $a -lt 100 -a $b -gt 15 ]
thenecho "$a 小于 100 且 $b 大于 15 : 返回 true"
elseecho "$a 小于 100 且 $b 大于 15 : 返回 false"
fi
if [ $a -lt 100 -o $b -gt 100 ]
thenecho "$a 小于 100 或 $b 大于 100 : 返回 true"
elseecho "$a 小于 100 或 $b 大于 100 : 返回 false"
fi
if [ $a -lt 5 -o $b -gt 100 ]
thenecho "$a 小于 5 或 $b 大于 100 : 返回 true"
elseecho "$a 小于 5 或 $b 大于 100 : 返回 false"
fi

邏輯運算符

以下介紹 Shell 的邏輯運算符,假定變量 a 為 10,變量 b 為 20:

運算符說明舉例
&&邏輯的 AND [[ $a -lt 100 && $b -gt 100 ]]?返回 false
||邏輯的 OR [[ $a -lt 100 || $b -gt 100 ]]?返回 true

示例:

a=10
b=20echo "a=$a, b=$b"if [[ $a -lt 100 && $b -gt 100 ]]
thenecho "返回 true"
elseecho "返回 false"
fiif [[ $a -lt 100 || $b -gt 100 ]]
thenecho "返回 true"
elseecho "返回 false"
fi

字符串運算符

下表列出了常用的字符串運算符,假定變量 a 為 "abc",變量 b 為 "efg":

運算符說明舉例
=檢測兩個字符串是否相等,相等返回 true。 [ $a = $b ]?返回 false。
!=檢測兩個字符串是否相等,不相等返回 true。 [ $a != $b ]?返回 true。
-z檢測字符串長度是否為0,為0返回 true。 [ -z $a ]?返回 false。
-n檢測字符串長度是否為0,不為0返回 true。 [ -n $a ]?返回 true。
str檢測字符串是否為空,不為空返回 true。 [ $a ]?返回 true。

示例:

a="abc"
b="efg"echo "a=$a, b=$b"if [ $a = $b ]
thenecho "$a = $b : a 等于 b"
elseecho "$a = $b: a 不等于 b"
fi
if [ $a != $b ]
thenecho "$a != $b : a 不等于 b"
elseecho "$a != $b: a 等于 b"
fi
if [ -z $a ]
thenecho "-z $a : 字符串長度為 0"
elseecho "-z $a : 字符串長度不為 0"
fi
if [ -n $a ]
thenecho "-n $a : 字符串長度不為 0"
elseecho "-n $a : 字符串長度為 0"
fi
if [ $a ]
thenecho "$a : 字符串不為空"
elseecho "$a : 字符串為空"
fi

文件測試運算符

文件測試運算符用于檢測 Unix 文件的各種屬性。

屬性檢測描述如下:

操作符說明舉例
-b file檢測文件是否是塊設備文件,如果是,則返回 true。 [ -b $file ]?返回 false。
-c file檢測文件是否是字符設備文件,如果是,則返回 true。 [ -c $file ]?返回 false。
-d file檢測文件是否是目錄,如果是,則返回 true。 [ -d $file ]?返回 false。
-f file檢測文件是否是普通文件(既不是目錄,也不是設備文件),如果是,則返回 true。 [ -f $file ]?返回 true。
-g file檢測文件是否設置了 SGID 位,如果是,則返回 true。 [ -g $file ]?返回 false。
-k file檢測文件是否設置了粘著位(Sticky Bit),如果是,則返回 true。 [ -k $file ]返回 false。
-p file檢測文件是否是有名管道,如果是,則返回 true。 [ -p $file ]?返回 false。
-u file檢測文件是否設置了 SUID 位,如果是,則返回 true。 [ -u $file ]?返回 false。
-r file檢測文件是否可讀,如果是,則返回 true。 [ -r $file ]?返回 true。
-w file檢測文件是否可寫,如果是,則返回 true。 [ -w $file ]?返回 true。
-x file檢測文件是否可執行,如果是,則返回 true。 [ -x $file ]?返回 true。
-s file檢測文件是否為空(文件大小是否大于0),不為空返回 true。 [ -s $file ]?返回 true。
-e file檢測文件(包括目錄)是否存在,如果是,則返回 true。 [ -e $file ]?返回 true。

示例:

變量 file 表示文件"/var/www/runoob/test.sh",它的大小為100字節,具有 rwx 權限。下面的代碼,將檢測該文件的各種屬性:

file="./operatorDemo.sh"
if [ -r $file ]
thenecho "文件可讀"
elseecho "文件不可讀"
fi
if [ -w $file ]
thenecho "文件可寫"
elseecho "文件不可寫"
fi
if [ -x $file ]
thenecho "文件可執行"
elseecho "文件不可執行"
fi
if [ -f $file ]
thenecho "文件為普通文件"
elseecho "文件為特殊文件"
fi
if [ -d $file ]
thenecho "文件是個目錄"
elseecho "文件不是個目錄"
fi
if [ -s $file ]
thenecho "文件不為空"
elseecho "文件為空"
fi
if [ -e $file ]
thenecho "文件存在"
elseecho "文件不存在"
fi

語句

條件語句

跟其它程序設計語言一樣,Bash中的條件語句讓我們可以決定一個操作是否被執行。結果取決于一個包在[[ ]]里的表達式。

條件表達式可以包含&&||運算符,分別對應??和??。除此之外還有很多有用的表達式。

共有兩個不同的條件表達式:ifcase

基元和組合表達式

[[ ]]sh中是[ ])包起來的表達式被稱作?檢測命令?或?基元。這些表達式幫助我們檢測一個條件的結果。在下面的表里,為了兼容sh,我們用的是[ ]。這里可以找到有關bash中單雙中括號區別的答案。

使用if

if在使用上跟其它語言相同。如果中括號里的表達式為真,那么thenfi之間的代碼會被執行。fi標志著條件代碼塊的結束。

### 寫成一行
if [[ 1 -eq 1 ]]; then echo "true"; fi### 寫成多行
if [[ 1 -eq 1 ]]; thenecho "true"
fi

同樣,我們可以使用if..else語句,例如:

### 寫成一行
if [[ 2 -ne 1 ]]; then echo "true"; else echo "false"; fi### 寫成多行
if [[ 2 -ne 1 ]]; thenecho "true"
elseecho "false"
fi

有些時候,if..else不能滿足我們的要求。別忘了if..elif..else,使用起來也很方便。

示例:

if [[ `uname` == "Adam" ]]; thenecho "Do not eat an apple!"
elif [[ `uname` == "Eva" ]]; thenecho "Do not take an apple!"
elseecho "Apples are delicious!"
fi
使用case

如果你需要面對很多情況,分別要采取不同的措施,那么使用case會比嵌套的if更有用。使用case來解決復雜的條件判斷,看起來像下面這樣:

echo "input param: " $1case $1 in"jpg" | "jpeg")echo "It's image with jpeg extension.";;"png")echo "It's image with png extension.";;"gif")echo "Oh, it's a giphy!";;*)echo "Woops! It's not image!";;
esac

每種情況都是匹配了某個模式的表達式。|用來分割多個模式,)用來結束一個模式序列。第一個匹配上的模式對應的命令將會被執行。*代表任何不匹配以上給定模式的模式。命令塊兒之間要用;;分隔。

循環語句

循環其實不足為奇。跟其它程序設計語言一樣,bash中的循環也是只要控制條件為真就一直迭代執行的代碼塊。

Bash中有四種循環:forwhileuntilselect

for循環

for與它在C語言中的姊妹非常像。看起來是這樣:

for arg in elem1 elem2 ... elemN
do### 語句
done

在每次循環的過程中,arg依次被賦值為從elem1elemN。這些值還可以是通配符或者大括號擴展。

當然,我們還可以把for循環寫在一行,但這要求do之前要有一個分號,就像下面這樣:

for i in {1..5}; do echo $i; done

還有,如果你覺得for..in..do對你來說有點奇怪,那么你也可以像C語言那樣使用for,比如:

for (( i = 0; i < 10; i++ )); doecho $i
done

當我們想對一個目錄下的所有文件做同樣的操作時,for就很方便了。舉個例子,如果我們想把所有的.bash文件移動到script文件夾中,并給它們可執行權限,我們的腳本可以這樣寫:

#!/bin/bash

for FILE in $HOME/*.bash; domv "$FILE" "${HOME}/scripts"chmod +x "${HOME}/scripts/${FILE}"
done
while循環

while循環檢測一個條件,只要這個條件為?,就執行一段命令。被檢測的條件跟if..then中使用的基元并無二異。因此一個while循環看起來會是這樣:

while [[ condition ]]
do### 語句
done

for循環一樣,如果我們把do和被檢測的條件寫到一行,那么必須要在do之前加一個分號。

比如下面這個例子:

#!/bin/bash

### 0到9之間每個數的平方
x=0
while [[ $x -lt 10 ]]; do ### x小于10echo $(( x * x ))x=$(( x + 1 )) ### x加1
done
until循環

until循環跟while循環正好相反。它跟while一樣也需要檢測一個測試條件,但不同的是,只要該條件為??就一直執行循環:

until [[ condition ]]; do### 語句
done
select循環

select循環幫助我們組織一個用戶菜單。它的語法幾乎跟for循環一致:

select answer in elem1 elem2 ... elemN
do### 語句
done

select會打印elem1..elemN以及它們的序列號到屏幕上,之后會提示用戶輸入。通常看到的是$?PS3變量)。用戶的選擇結果會被保存到answer中。如果answer是一個在1..N之間的數字,那么語句會被執行,緊接著會進行下一次迭代 —— 如果不想這樣的話我們可以使用break語句。

一個可能的實例可能會是這樣:

#!/bin/bash

PS3="Choose the package manager: "
select ITEM in bower npm gem pip
doecho -n "Enter the package name: " && read PACKAGEcase $ITEM inbower) bower install $PACKAGE ;;npm)   npm   install $PACKAGE ;;gem)   gem   install $PACKAGE ;;pip)   pip   install $PACKAGE ;;esacbreak ### 避免無限循環
done

這個例子,先詢問用戶他想使用什么包管理器。接著,又詢問了想安裝什么包,最后執行安裝操作。

運行這個腳本,會得到如下輸出:

$ ./my_script
1) bower
2) npm
3) gem
4) pip
Choose the package manager: 2
Enter the package name: bash-handbook
<installing bash-handbook>
break 和 continue

如果想提前結束一個循環或跳過某次循環執行,可以使用 shell 的breakcontinue語句來實現。它們可以在任何循環中使用。

break語句用來提前結束當前循環。

continue語句用來跳過某次迭代。

for (( i = 0; i < 10; i++ )); doif [[ $(( i % 2 )) -eq 0 ]]; then continue; fiecho $i
done

運行上面的例子,會打印出所有0到9之間的奇數。

函數

在腳本中,我們可以定義并調用函數。跟其它程序設計語言類似,函數是一個代碼塊,但有所不同。

bash 中,函數是一個命令序列,這個命令序列組織在某個名字下面,即?函數名?。調用函數跟其它語言一樣,寫下函數名字,函數就會被?調用

我們可以這樣聲明函數:

my_func () {### 語句
}my_func ### 調用 my_func

我們必須在調用前聲明函數。

函數可以接收參數并返回結果 —— 返回值。參數,在函數內部,跟非交互式下的腳本參數處理方式相同 —— 使用位置參數。返回值可以使用return命令?返回?。

下面這個函數接收一個名字參數,返回0,表示成功執行。

### 帶參數的函數
greeting () {if [[ -n $1 ]]; thenecho "Hello, $1!"elseecho "Hello, unknown!"fireturn 0
}greeting Denys  ### Hello, Denys!
greeting        ### Hello, stranger!

我們之前已經介紹過返回值。不帶任何參數的return會返回最后一個執行的命令的返回值。上面的例子,return 0會返回一個成功表示執行的值,0

另外,還有幾個特殊字符用來處理參數:

參數處理說明
$#傳遞到腳本的參數個數
$*以一個單字符串顯示所有向腳本傳遞的參數
$$腳本運行的當前進程ID號
$!后臺運行的最后一個進程的ID號
$@與$*相同,但是使用時加引號,并在引號中返回每個參數。
$-顯示Shell使用的當前選項,與set命令功能相同。
$?顯示最后命令的退出狀態。0表示沒有錯誤,其他任何值表明有錯誤。

流和重定向

Bash有很強大的工具來處理程序之間的協同工作。使用流,我們能將一個程序的輸出發送到另一個程序或文件,因此,我們能方便地記錄日志或做一些其它我們想做的事。

管道給了我們創建傳送帶的機會,控制程序的執行成為可能。

學習如何使用這些強大的、高級的工具是非常非常重要的。

輸入、輸出流

Bash接收輸入,并以字符序列或?字符流?的形式產生輸出。這些流能被重定向到文件或另一個流中。

有三個文件描述符:

代碼描述符描述
0stdin標準輸入
1stdout標準輸出
2stderr標準錯誤輸出

重定向

重定向讓我們可以控制一個命令的輸入來自哪里,輸出結果到什么地方。這些運算符在控制流的重定向時會被用到:

OperatorDescription
>重定向輸出
&>重定向輸出和錯誤輸出
&>>以附加的形式重定向輸出和錯誤輸出
<重定向輸入
<< Here文檔?語法
<<<Here字符串

以下是一些使用重定向的例子:

### ls的結果將會被寫到list.txt中
ls -l > list.txt### 將輸出附加到list.txt中
ls -a >> list.txt### 所有的錯誤信息會被寫到errors.txt中
grep da * 2> errors.txt### 從errors.txt中讀取輸入
less < errors.txt

/dev/null?文件

如果希望執行某個命令,但又不希望在屏幕上顯示輸出結果,那么可以將輸出重定向到 /dev/null:

$ command > /dev/null

/dev/null 是一個特殊的文件,寫入到它的內容都會被丟棄;如果嘗試從該文件讀取內容,那么什么也讀不到。但是 /dev/null 文件非常有用,將命令的輸出重定向到它,會起到"禁止輸出"的效果。

如果希望屏蔽 stdout 和 stderr,可以這樣寫:

$ command > /dev/null 2>&1

Debugging

shell提供了用于debugging腳本的工具。如果我們想以debug模式運行某腳本,可以在其shebang中使用一個特殊的選項:

#!/bin/bash options

options是一些可以改變shell行為的選項。下表是一些可能對你有用的選項:

ShortNameDescription
-fnoglob禁止文件名展開(globbing)
-iinteractive讓腳本以?交互?模式運行
-nnoexec讀取命令,但不執行(語法檢查)
-t執行完第一條命令后退出
-vverbose在執行每條命令前,向stderr輸出該命令
-xxtrace在執行每條命令前,向stderr輸出該命令以及該命令的擴展參數

舉個例子,如果我們在腳本中指定了-x例如:

#!/bin/bash -xfor (( i = 0; i < 3; i++ )); doecho $i
done

這會向stdout打印出變量的值和一些其它有用的信息:

$ ./my_script
+ (( i = 0 ))
+ (( i < 3 ))
+ echo 0
0
+ (( i++  ))
+ (( i < 3 ))
+ echo 1
1
+ (( i++  ))
+ (( i < 3 ))
+ echo 2
2
+ (( i++  ))
+ (( i < 3 ))

有時我們需要debug腳本的一部分。這種情況下,使用set命令會很方便。這個命令可以啟用或禁用選項。使用-啟用選項,+禁用選項:

#!/bin/bash

echo "xtrace is turned off"
set -x
echo "xtrace is enabled"
set +x
echo "xtrace is turned off again"


本文轉自靜默虛空博客園博客,原文鏈接:http://www.cnblogs.com/jingmoxukong/p/7867397.html,如需轉載請自行聯系原作者

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/279455.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/279455.shtml
英文地址,請注明出處:http://en.pswp.cn/news/279455.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

UmiJS CDN 部署之 publicPath

為什么80%的碼農都做不了架構師&#xff1f;>>> 靜態資源在非根目錄或 cdn 這時&#xff0c;就需要配置 publicPath。至于 publicPath 是啥&#xff1f;具體看 webpack 文檔&#xff0c;把他指向靜態資源&#xff08;js、css、圖片、字體等&#xff09;所在的路徑。…

check corners_免費下載:將Mac樣式的Hot Corners添加到Windows 10

check cornersWindows: Move your mouse to any corner to quickly see all your open windows, your desktop, or even start your screen saver. Windows&#xff1a;將鼠標移到任意角落可快速查看所有打開的窗口&#xff0c;桌面&#xff0c;甚至啟動屏幕保護程序。 It’s c…

centos

1. ssh 登錄 centos 服務器 引用: https://www.cnblogs.com/loseself/p/9599745.html 2. ssh 登錄 失敗: WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! 引用: https://blog.csdn.net/xlgen157387/article/details/52669709 3. 編譯安裝 git 引用: https://git-scm.com/bo…

網易前端面試題總結,你見過幾個?

劃重點 很幸運地能收到網易的面試通知&#xff0c;就毫不猶豫去面試了~三點的面試&#xff0c;因為從來沒去過那個中關村西北旺區&#xff0c;吃完飯早早就去了,想象中那里應該是繁華的地方&#xff0c;到了發現都在建設中&#xff0c;很多還在建設中&#xff0c;看到了…

webpack 配置詳解

最近在學習webpack&#xff0c;邊學邊練習&#xff0c;下面是對一些應用到的屬性、插件記錄分享出來&#xff0c;也方便以后查找與復習&#xff0c;過程中碰到了一些坑&#xff0c;在注釋中有說明&#xff1a; const path require(path) const webpack require("webpack…

連續對焦 auto對焦_如何在Windows 10上使用對焦輔助(請勿打擾模式)

連續對焦 auto對焦Windows 10’s “Focus Assist” feature is a “Do Not Disturb” mode that hides notifications. Windows automatically activates it when you’re playing PC games or mirroring your display—and you can have Windows automatically activate it on…

小米品牌

對于現階段的小米公司來說&#xff0c;他們的策略是行走在鋼絲上的。為什么這么說呢&#xff1f;原因是營銷策略的優勢和劣勢都很容易被人翻轉。換句話來說就是優勢的穩定性不夠&#xff0c;劣勢的方面又很容易崩盤&#xff0c;這兩個方向的絲毫失誤都會帶給小米公司嚴重的打擊…

wepy學習筆記之環境搭建

寫了近兩年小程序了&#xff0c;越來越發現原生小程序有太多雞肋的地方。所以今天準備嘗試一下wepy&#xff0c;正好最近手上有個外包&#xff0c;可以拿來練手。如果可以的話&#xff0c;或許會出一系列wepy相關的文章&#xff08;偏實戰&#xff09;&#xff0c;歡迎大佬們指…

山東青島市南區:創建物聯網 信息化管理涉案財物

近日,山東省青島市市南區檢察院自主設計研發“物聯網”涉案財物管理系統,使涉案財物管理工作步入了信息化管理模式。 涉案財物管理工作是案件辦理過程中一個相當重要的環節,而以往主要靠人工進行涉案財物管理,工作繁瑣,業務量大,存在查找困難、堆放混亂等問題。庫存現狀不能及時…

spotify能免費下歌嗎_Spotify免費版與高級版:值得升級嗎?

spotify能免費下歌嗎Spotify offers two tiers: a free, ad-supported plan and a $9.99 per month Premium plan. But what are the differences between the two and is it worth upgrading? Let’s find out. Spotify提供兩個等級&#xff1a;免費的廣告支持計劃和每月9.99…

BZOJ4012 [HNOI2015]開店

BZOJ4012 [HNOI2015]開店 這道題因為太多人拿這個題卡$BZOJ$&#xff0c;于是成了權限題。。。 本蒟蒻表示沒錢氪金。。。 無奈&#xff0c;拿出了洛谷&#xff1a;P3241 [HNOI2015]開店 還有$LOJ$&#xff1a;#2116. 「HNOI2015」開店 這里附上洛谷的題面&#xff1a; 題目描述…

ElasticSearch實戰-入門

1.概述 今天接著《ElasticSearch實戰&#xff0d;日志監控平臺》一文來給大家分享后續的學習&#xff0c;在《ElasticSearch實戰&#xff0d;日志監控平臺》中給大家介紹一個日志監控平臺的架構方案&#xff0c;接下來給大家分享如何去搭建部署這樣一個平臺&#xff0c;給大家做…

如何解決90%的報表設計難題?300張報表模板任君挑選

下載ActiveReport最新試用版 大數據時代&#xff0c;數據價值愈發彰顯&#xff0c;數據分析正在成為影響業務決策的關鍵因素。其中&#xff0c;數據分析的結果以報表的形式呈現給用戶&#xff0c;究竟什么樣的報表設計才能真正讓用戶滿意&#xff0c;如何保證用戶在復雜的數據…

macos 版本_如何檢查您使用的macOS版本

macos 版本Apple releases new versions of the macOS operating system about once per year. Here’s how to check which release of the macOS operating system is installed on your MacBook, iMac, Mac Mini, or Mac Pro. 蘋果大約每年發布一次新版本的macOS操作系統。 …

luogu 1484\1792 種樹 奇怪的貪心可反悔

1484 種樹 此版本是線性的&#xff0c;那么根據鏈表維護即可&#xff1b; 構建新點&#xff0c;點的左右分別是原整個區間的前驅及后繼&#xff0c;再正常維護即可 注意兩個版本的維護有所不同 第二個版本的維護直接將左右兩點刪除 1792 種樹2 此版本是環 1484 #include<bi…

第十四周作業

2019春第二次課程設計實驗報告 一.實驗項目 貪吃蛇游戲 二.實驗功能描述&#xff1a; 存儲數據&#xff0c;實現wasd控制蛇方向&#xff0c;吃到食物就增加長度&#xff0c;最后按長度算分數&#xff0c;撞到障礙物則死亡&#xff0c;計算積分 三.項目模板結構介紹&#xff1a;…

java語言不用擔心內存嗎_不用擔心智能手機的電池,只需使用它

java語言不用擔心內存嗎When you’re trying to get the most life out of your device, it’s easy to overthink batteries. Don’t. Plug in your devices when possible, carry a battery pack with you, and get on with your life. 當您試圖充分利用設備的使用壽命時&…

asp.net core結合NLog搭建ELK實時日志分析平臺

0、整體架構 整體架構目錄&#xff1a;ASP.NET Core分布式項目實戰-目錄 一、介紹ELK 1、說明&#xff08;此篇ELK采用rpm的方式安裝在服務器上&#xff09;-牛刀小試 承接上一篇文章的內容準備部署ELK來展示asp.net core 的數據。目前此篇文章只用到單臺服務器&#xff0c;等下…

Rhel7 設置目錄權限,acl權限

Rhel7 設置目錄權限&#xff0c;acl權限 改變用戶和組的所屬 Getfacl 取得 Setfacl設置 [rootdesktop0 tmp]# setfacl -m u:natasha:rw fstab [rootdesktop0 tmp]# setfacl -m u:harry:- fstab [rootdesktop0 tmp]# setfacl -m o::r fstab [rootdesktop0 tmp]# getfacl fstab #…

IT兄弟連 JavaWeb教程 AJAX定義以及解決的問題

2019獨角獸企業重金招聘Python工程師標準>>> Ajax是"Asynchronous JavaScript And XML"的縮寫(即&#xff1a;異步的JavaScript和XML)&#xff0c;是一種實現無頁面刷新獲取服務器數據的混合技術,Ajax這個概念的最早提出者是Jesse James Garrett。我們知道…