bash:Bourne Again shell,是 Linux 上的標配 shell;對于想學習 shell 的人來說,無論是新手,還是想進一步提高 shell 編程能力的高級用戶,bash 都是比較好的選擇。
Bash Reference Manual(version 4.4, Sep. 7, 2016);GNU bash;
Bash Testing: 如何測試你寫的 bash script;
Bash by example @ IBM developerWorks;
-
Linux/BSD command line wizardry:Learn to think in sed, awk, and grep.
Grep finds strings; Sed replaces strings; Awk finds columns.
Grep:查找
Sed - stream editor for filtering and transforming text 流編輯、替換
Awk:pattern scanning and processing language. 取字段 grep、awk、sed:grep, awk and sed – three VERY useful command-line utilities。
Greg's Wiki @ wooledge,有深度,是理解 shell 編程的絕佳資料;
- For learning Bash, try the BashGuide.
- 引號 Quotes,熟讀并測試!
- 命令參數 Arguments,熟讀并測試!
- Word Splitting;
- Process Management,有價值!
- 數據和安全專家 Adam Katz 在 How to get the first line of a file in a bash script? 文章中的回答盡顯對 grep、awk、sed 的嫻熟掌握。
grep
Show Lines Before and After Match via Grep: 使用 -A -B -C 可以顯示 After、Before、Both 前后各幾行文本。
Bash coding conventions @ stackoverflow;
export PS1='${PWD#"${PWD%/*/*}/"} \$ '
: 在終端提示(shell terminal prompt),顯示當前目錄的最后兩級目錄,不顯示用戶名和主機名。
標準設置:export PS1="[\u@\h \W]\\$ "
,其中\u
表示用戶,\h
表示主機
Only showing the last 2 directories in the terminal prompt?How to determine the current shell I'm working on?
ps -p $$ -ocomm=
:得知 shell 名稱;Bash POSIX Mode:盡量遵循 POSIX 標準,以便兼容更多系統;
小技巧
-
cat - > /tmp/xxx
,或者echo "$(</dev/stdin)" > /tmp/xxx
將標準輸入(屏幕輸入)直接輸出到xxx文件中。使用ctrl+d
中止輸入。How to redirect stdin to file in bash。
條件判斷
-
Introduction to if;
[ "$a" \> "$b"]
字符串比較大小;>
和<
是重定向字符,做大小比較時,要轉義。文件是否存在等。
[ -s "$filename" ]
、[ -d "$filename" ]
、[ -L "$filename" ]
、[ ! -e "$filename" ]
判斷文件、目錄、鏈接,必須雙引號括起;
[ -z "$name" ]
、[ -n "$name" ]
判斷字符串長度,必須雙引號括起; -
if TEST-COMMANDS; then CONSEQUENT-COMMANDS; fi
The TEST-COMMAND list is executed, and if its return status is zero, the CONSEQUENT-COMMANDS list is executed. The return status is the exit status of the last command executed, or zero if no condition tested true. - Testing and Branching;elif;
if [ ]; then
...
elif [ ]; then
...
else
...
fi
-
Test Constructs;
有示例,對于了解 bash 邏輯判斷非常有幫助; -
In bash, how can I check if a string begins with some value?
檢查 $HOST 是否以 node 開頭:
case $HOST in node*)
your code here
esac
- Variable Assignment(=)
- string comparison(= or ==)
- integer and string comparison;
- Simple logical operators in BASH;
- Unix Boolean Operators ( &&, -a, ||, -o );
-
$( cd "$( dirname ${0} )" && pwd )
腳本源文件目錄
Getting the source directory of a Bash script from within【@stackoverflow】;
How do I determine the location of my script? I want to read some config files from the same place【BashFAQ/028 腳本文件目錄】;
Parameter Substitution 參數替換,字符串查找、替換、截取操作;
${var%Pattern}
:Remove from$var
the shortest part of$Pattern
that matches the back end of$var
.
示例:
script_dir=$( cd ${0%/*} && pwd -P )
文件目錄【從右側開始刪除,直到遇到第一個/
:最短刪除】${0##*/}
,相當于"$(basename ${0})"
文件名【從左側開始刪除,直到最后一個/
:最長刪除】g_nap=${url##*/}; g_nap=${g_nap%%\?*}
取 url 的 path 的最右側一節;http://host:port/p1/p2/p3?query
,取到的是p3;
在 bash 歷史記錄中搜索
CTRL- r: searches in the backward direction
Bash – Using History Efficiently;Extract filename and extension in Bash;
獲取文件的擴展名和文件名:
filename=$(basename -- "$fullfile")
extension="${filename##*.}"
filename="${filename%.*}"
How to determine function name from inside a function
怎么得到 function 函數的名字;
計算賦值
i=0
i=$(expr $i + 1)
i=`expr $i + 1`
i=$(($i + 1))
i=$[$i + 1]
i=$((i + 1))
i=$[i + 1]
讀文件
-
數組
${#ArrayName[@]}
:顯示數組大小。 -
${ArrayName[@]}
數組的所有值。 -
"${ArrayName[$i]}"
取數組第i個值。 -
NEW=("${OLD1[@]}" "${OLD2[@]}")
: 將兩個數組合并生成一個新數組。 - 給定一個值,看是否在數組中存在
if [[ ! " ${PART_OPTS[*]} " =~ " ${PART} " ]]; then
echo -e "\e[41m PART:$PART 錯誤,目前僅支持 ${PART_OPTS[*]} \e[0m"; exit 99;
fi
- 一行一行讀文件
Read line by line in Bash script。 -
(${IN//;/ })
:以;
分隔的字符串 IN 轉數組
How do I split a string on a delimiter in Bash?
示例如下:
IN="bla@some.com;john@home.com"
arrIN=(${IN//;/ })
echo ${arrIN[1]}
- 分隔字符串為數組
IFS=', ' read -r -a array <<< "$string"
How to split a string into an array in Bash? - 如何取 數組索引(下標) 和 數組值
for element in "${array[@]}"
do
echo "$element"
done
for index in "${!array[@]}"
do
echo "$index ${array[index]}"
done
-
$ grep pattern file | tr '\n' ' '
把多行字符串合并成一行:How to concatenate multiple lines of output to one line? - 從文件創建數組
Creating an array from a text file in Bash
結構良好
- 在 shell 中寫結構良好的代碼:main 函數及前向聲明。
Forward function declarations in a Bash or a Shell script? - 改變輸出的文本顏色:使用
tput
命令。
How to change the output color of echo in Linux; -
Linux shell 命令顏色 \e[*m詳解;
示例:
echo -e "工程 \e[32m $xone \e[0m 成功!"
綠字
31m
紅字
32m
綠字
34m
藍字
41m
紅底白字
42m
綠底白字
43m
黃底白字
44m
藍底白字
45m
粉底白字
46m
淺藍白字 -
$LINENO
行號,shell 還有 更多特殊變量。 - expr 是一個命令;
-
Command substitution
Command substitution reassigns the output of a command.
- $(command)
- `command`
- $(...) is preferred over `...` (backticks),建議使用 $(...);
- 注意:
${}
、$()
、$[]
的用法。 - 注意某些嵌入系統要求嚴格,數字變量初值賦數字,保持可移植性;
- echo 輸出內容使用
""
雙引號括起,某些嵌入系統要求嚴格;
類似echo "who am I: $USER"
,如果沒有雙引號,$USER
輸出可能就為空;$USER
是一個內置變量; -
IFS:Internal Field Separator,Input Field Separator;
The default value of IFS is space, tab, newline. (A three-character string.)
在 shell 腳本中,一般沒有必要修改 IFS。 - Arithmetic expansion
$(( EXPRESSION ))
$[ EXPRESSION ]
- 在一行使用
&&
和;
執行多個命令語句;
Lists of Commands;list construct;
Running multiple commands in one line in shell;
&&
表示只有前一條語句執行成功,才執行下一條命令;&& operator to execute the next command only if the previous one succeeded
;
表示順序執行即可。 -
&
:后臺進程(background);
If a command is terminated by the control operator ‘&’, the shell executes the command asynchronously in a subshell. This is known as executing the command in the background. The shell does not wait for the command to finish, and the return status is 0 (true).
if [ "$EUID" -ne 0 ]; then
echo "Please run as root"
exit
fi
變量、函數和引號
- Quoting Variables;
-
quoting in shell programming is extremely important;
一定要注重使用引號;
Why a variable assignment replaces tabs with spaces?
真實答案是:It isn't the assignment that's losing the tabs, but invoking the echo command.
這篇文章對 shell 如何解析命令行講得非常好:command name 命令名,arguments 命令參數,以及 split 分割過程,簡潔清晰。
The shell parses your command-line into a command name and a list of arguments.
It uses white-space (tabs and spaces) to split the command into these parts.
Then it runs the command, passing it the argument list.
Perl one-liner 這個工具也很有用,可以分析參數。Smylers 是個人物。
- CURL escape single quote;
- How to add single quote inside of a bash variable?
-
I'm trying to put a command in a variable, but the complex cases always fail!
Variables hold data. Functions hold code. Don't put code inside variables!
BashFAQ/050:編程規則之一,記住:使用 set -x 和 set +x 調試 shell script!參見: I want a log of my script's actions; -
In a shell script: echo shell commands as they are executed
set -x
orset -o xtrace
:Options;
錯誤
If a command is not found, the child process created to execute it returns a status of 127.
If a command is found but is not executable, the return status is 126.
比如:試圖在 64 位機上 運行 ELF 32 位可執行程序,則報告如下錯誤:
./superd -V
-bash: ./superd: cannot execute binary file
echo $?
126
- reserved exit codes;
- 使用
echo $?
,顯示上一條命令的返回值。
雙引號
- Security implications of forgetting to quote a variable in bash/POSIX shells;
- 參數擴展(Parameter Expansion)、命令替換(command substitution)一定要使用雙引號引起來。
Always use double quotes around variable substitutions and command substitutions;
"$foo", "$(foo)"
討論得非常好!
命令
-
Get current users username in bash?
whoami
、$USER
查看當前用戶 - source 命令或者 . 命令
source <filename>
. <filename>
(.
命令是POSIX 標準)這里的.
和 source 一樣,都是內置命令;注意區分命令的.
和表示目錄的.
;
source 執行文件時,不要求文件有可執行屬性 +x;
source 引入(包含)的文件如何處理 arguments 的?
subshell
-
sh <filename>
,不要求 filename 有可執行權限; -
./filename
,要求 filename 有可執行權限; - 內置命令執行shell腳本文件
shell 內置命令(builtin)不會開啟 subshell。 -
command
,type
,hash
How to check if a program exists from a Bash script?
awk | gawk
- Linux 中最常用的命令之一,和 grep、sed 一樣重要。
-
BashFAQ/045: How can I ensure that only one instance of a script is running at a time (mutual exclusion, locking)?
Quick-and-dirty way to ensure only one instance of a shell script is running at a time?
shell 中如何加鎖?
killall -SIGTERM supertack
- 隨機數 How to generate random number in Bash?
- 如何計算文本行數
sed -n '$=' filename
awk 'END {print NR}' filename
grep -c '' filename
AWK - Built-in Functions @tutorialspoint.com 是一個非常好的 AWK 學習材料,值得從頭到尾讀一遍,你就是 awk 專家了;
how to use awk to manipulate text,learning-awk;
這是非常好的文章,循序漸進,容易理解和學習使用。
The basic format of an awk command is:
awk '/search_pattern/ { action_to_take_on_matches; another_action; }' file_to_parse
--field-separator 或者 -F 則指定使用什么作為分隔符
;
示例:
echo "a/b/c" | awk '{print $0}'
:$0
輸出的是原始文本a/b/c
。$1
就是空格分隔的第1個值,也是a/b/c
,$2
及以后就為空。
echo "a/b/c" | gawk -F "/" '{print $2}'
,結果顯示 b;
gawk '/^author/ { print $0 }' onefile
,找出文件 onefile 中以 author 開頭的行,并打印整行;$(awk -v s="$v" 'BEGIN {gsub("=", "", s); gsub("&", "", s); gsub(" ", "", s); print substr(s, 1, 24)}');
變量v賦值給s,替換s中的=
、&
、空格
,僅取s的前24個字符;How to print third column to last column?: 處理文件行內容,打印出第3列到最后一列的所有內容。
# 分隔符為空格
cut -d ' ' -f 3- <filename>
# 或者采用 awk
命令行參數
for last; do true; done
echo $last
-
An example of how to use getopts in bash;
在 bash 中使用 getopt,指定命令行參數。
如何異步簽出代碼并構建?
source /home/git/devops/gwph.git.hooks/www.post-receive.gulp >&- 2>&- &
- 在 post-receive 時啟動一個 shell 異步處理頁面構建;
否則 git push 等得時間太長,降低效率; - Asynchronous git hook?
- Asynchronous shell commands
- How best to include other scripts?
- How do I parse command line arguments in bash?
何為重定向?
符號 & 比較神奇,在命令后面加 & 就會在后臺運行;還可以用來重定向,原來一直對重定向模糊,今天前端構建時要在簽入代碼之后異步構建頁面,看了兩篇文章才徹底明白其原理:
-
Bash One-Liners Explained, Part III: All about redirections @ catonmat.net;
這篇文章配有大量的說明圖,一看就明白了; - What does “3>&1 1>&2 2>&3” do in a script?
-
How can I redirect and append both stdout and stderr to a file with Bash?
cmd &> file.txt
: 將標準輸出和錯誤定向到 file.txt 文件中。&>
是 bash 的語法; -
cmd 1>& abc.txt
: 將標準輸出定向到 abc.txt 文件中。 -
How to redirect output to a file and stdout?
program [arguments...] 2>&1 | tee outfile
:將 program 的 stderr 重定向到 stdout,通過|
定向到 文件 outfile 中。
if cmp a b &> /dev/null # Suppress output.
then echo "Files a and b are identical."
else echo "Files a and b differ."
fi
-
./sapiloader >/dev/null 2>&1 &
后臺運行,所有的錯誤和輸出都不再到屏幕;
In the shell, what does “ 2>&1 ” mean? - see REDIRECTION in bash man;
bash shell 特殊變量
- Special Variables;
- Special Variable Types;
- 8類特殊變量
No. | Variable | Description |
---|---|---|
1 | $0 |
The filename of the current script. |
2 | $n |
These variables correspond to the arguments with which a script was invoked. Here n is a positive decimal number corresponding to the position of an argument (the first argument is $1 , the second argument is $2 , and so on). |
3 | $# |
The number of arguments supplied to a script. |
4 | $* |
All the arguments are double quoted. If a script receives two arguments, $* is equivalent to $1 $2 . |
5 | $@ |
All the arguments are individually double quoted. If a script receives two arguments, $@ is equivalent to $1 $2 . |
6 | $? |
The exit status of the last command executed. |
7 | $$ |
The process number of the current shell. For shell scripts, this is the process ID under which they are executing. |
8 | $! |
The process number of the last background command. |
-
How do I find out what shell I am using on Linux/Unix?
echo $0
當前 shell 是什么 shell? - 在 shell 命令行下,
echo $$
即可知當前 shell 的 PID 值。 -
How to get parent PID of a given process in GNU/Linux from command line?
給定 pid,想知道其 父ID 是多少?
parent pid:cat /proc/1111/status | grep PPid
當前shell的父ID:cat /proc/$$/status | grep PPid
- The /proc Filesystem@kernel.org: 詳細了解 /proc 下的內容,很有意思。
- Understanding Exit Codes and how to use them in bash scripts;
The
exit
command in bash accepts integers from0 - 255
, in most cases0
and1
will suffice, however there are other reserved exit codes that can be used for more specific errors. The Linux Documentation Project has a pretty good table of reserved exit codes and what they are used for.
保留錯誤碼。錯誤碼在 1-255 之間。用戶使用的話,建議在 1-125 之間取值。
備注
-
source
:How to include file in a bash shell script?
source *filename* [arguments]
Read and execute commands from filename in the current shell environment and return the exit status of the last command executed from filename.
If any arguments are supplied, they become the positional parameters when filename is executed.
Otherwise the positional parameters are unchanged.
The return status is the status of the last command exited within the script (0 if no commands are executed),
and false if filename is not found or cannot be read.
字符串比較請使用一個等號=
即可(POSIX標準)
[ STRING1 == STRING2 ] True if the strings are equal. "=" may be used instead of "==" for strict POSIX compliance.
BASH 基本概念
Brace Expansion:{} 擴展;
Tilde Expansion:~ 擴展;
Variable and Parameter Expansion(PE):變量和參數擴展;
- 理解 Parameter Expansion 這個概念很重要;
- 理解:literal 和 syntactic,尤其:
空白符
、引號
、反斜線
在何時是 syntactic? - Variables are a common type of parameter.
It is vital to understand, however, that Quoting and Escaping are considered before parameter expansion happens, while Word Splitting is performed after. That means that it remains absolutely vital that we quote our parameter expansions, in case they may expand values that contain syntactical whitespace which will then in the next step be word-split.
- Command, Process, Arithmatic Substitution:命令、進程、算數替換;
- Word Splitting:分詞,Field Splitting;理解 IFS;
- Filename Generation:文件名生成;
- Shell Expansions:基礎知識,值得讀,詳細解釋了以上各種擴展和替換;
解析流程
-
arguments 命令參數分析,對 shell 和
系統調用
的關系講得清楚; - Processing the Command Line by Mark G. Sobell and Peter Seebach;
-
Command-line Processing;
Steps in Command-line Processing - How shell processes the content of command line in order to execute?
-
Command not found
error in Bash variable assignment;
eval 050 規則
- Eval command and security issues,基礎知識,值得讀;
- Execute command containing quotes from shell variable [duplicate];
- Why does shell ignore quotes in arguments passed to it through variables?這個帖子講得比較清楚;
- Execute a command stored in a variable,對 eval 有講述;
- Stéphane Chazelas;
-
How to assign space-containing values to variables in bash using eval?
A good way to work with eval is to replace it with echo for testing. echo and eval work the same.
- Unable to set variables in bash script [duplicate]:Jahid 和 Ignacio 的回答都不錯;
- Why is printf better than echo?
-
Assignment of variables with space after the (=) sign?
PWD= /bin/pwd
,PWD變量所賦值(本例是空值)只在 /bin/pwd 執行期間有效,或者說只應用于 /bin/pwd 命令;
IFS=, read -ra namesArray <<< "$names"
:IFS臨時設置為,
,只在 read 期間有效;這種用法 只對幾個命令有效,read 是其中之一;一般不要修改 IFS 變量設置;
how aboutname=hello world
:world: command not found
; - shell解析命令行的過程以及eval命令;這篇博文不錯,博主寫的大都是運維類文章;
Greg's Wiki【wooledge.org】
- 首先理解系統調用 execve
int execve(const char *filename, char *const argv[], char *const envp[]);
argv
:argument vector;
envp
:environment; - 理解 shell 如何將 命令 command 翻譯成系統調用;
- 實現業務邏輯;
Quote Guidelines
- quoting in shell programming is extremely important.
- "Quote" any arguments that contain data which also happens to be shell syntax.
- "$Quote" all parameter expansions in arguments. You never really know what a parameter might expand into; and even if you think it won't expand bytes that happen to be shell syntax, quoting will future-proof your code and make it safer and more consistent.
- Don't try to put syntactical quotes inside parameters. It doesn't work.
了解 shell
知識點
- 文件大小
ls -l filename |awk '{print $5}'
wc -c < filename
:short for word count,-c
prints the byte count. wc is a portable, POSIX solution.
du -k filename | cut -f1
-
cat <<EOF
How does “cat << EOF” work in bash?
示例:
cat <<EOF
Usage: $0 [options]
Language-agnostic unit tests for subprocesses.
Options:
-v, --verbose generate output for every individual test case
-h show brief usage information and exit
--help show this help message and exit
EOF
- 查看 bash 版本
/bin/bash --version
echo $BASH_VERSION
sudo bats 時報告sudo: bats: command not found
$ sudo bats sapiloader.bats
sudo: bats: command not found
解決方案:
The error happens because the binary you are trying to call from command line is only part of the current user's PATH variable, but not a part of root user's PATH.
$ sudo env | grep ^PATH
查看 sudo 的 PATH,果然發現不包含 /usr/local/bin,因此:將 bats 改為全路徑 /usr/local/bin/bats 就可以正常執行,但不方便,我們改造 ~/.bashrc,加一條語句,創建 sudo 別名(別名優先于命令)即可:
alias sudo='sudo -E env "PATH=$PATH"'
。
完美解決!
備注
- 錯誤
[: bad number
的問題原因是:[ $EUID -eq 0 ]
語句中,EUID 沒有賦值,在有些 shell 中 EUID 是內部變量,會自動賦值,你在 script 中使用即可,但有些就沒有賦值,所以不要使用。但從用法上可以這么解決:${EUID:-0}
,采用 Parameter Substitution default 值來避免錯誤,當 EUID 未聲明或者賦值為空時,輸出 0。