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;
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;
- 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。