Linux-Shell-CheatSheet
Shell CheatSheet
一、脚本基础
1.1 创建脚本
在创建
#!/bin/bash
示例如下:
#!/bin/bash
date
who
脚本创建完成后,还需要使用 chmod
命令赋予文件拥有者执行文件的权限:
chmod u+x test1
u 表示该文件的拥有者,g 表示同组其他用户,o 表示该组外的其他用户,a 表示所有用户;+ 表示增加权限、- 表示取消权限、= 表示设定唯一权限;x 表示可执行,r 表示可读取,w 表示可写入。
1.2 显示消息
echo This is a test
默认情况下,不需要使用引号来圈定所需要输出的字符,但如果待输出字符中含有单引号,则需要使用双引号进行圈定,反之亦然。示例如下:
echo "This is a test to see if you're paying attention"
除此之外,-n
参数,用于将文本和命令行的输出显示在同一行:
echo -n "The time and date are: "
date
输出:the time and date are: 2020年 02月 18日 星期二 10:29:28 CST
1.3 使用变量
用户变量可以是由字母、数字或下划线组成的长度不超过$
符号进行引用:
name=heibaiying
echo $name
需要注意的是使用一个已经存在的用户变量来对新的用户变量进行赋值,仍然需要使用 $
符号进行引用:
value1=10
value2=$value1 #正确
value2=value1 #错误
因为 $
符号已经用作引用变量,所以如果想在普通文本中使用它,则需要使用 \
进行转义。
在set
命令进行查看。和自定义变量一样,引用系统变量时也需要使用 $
符号。
除了可以将普通文本内容赋值给变量外,还可以将命令的输出赋值给变量,此时可以使用两种方式来进行引用:
- 使用反引号将整个命令圈起来;
- 使用
$()
格式。
示例如下:
date1=`date`
echo "1.The date and time are: " $date1
date2=$(date)
echo "2.The date and time are: " $date2
1.4 数学运算
在
var1=$[1 + 5] # 结果:6
var2=$[$var1 * 2] # 结果:12
var3=$[$var1 * ($var2 - $var1)] # 结果:36
需要注意的是
var1=100
var2=45
var3=$[$var1 / $var2]
echo The final result is $var3 # 结果:2
如果想要支持浮点数运算,可以使用内置的bc
命令即可打开该计算器:
[root@node01 ~]# bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
scale = 4
8/3
2.6666
如果想要在脚本文件中使用
variable=$(echo "options; expression" | bc)
该方式通过管道运算符将$()
格式来引用
var1=100
var2=45
var3=$(echo "scale=4; $var1 / $var2" | bc)
echo The answer for this is $var3 # 结果:2.2222
如果你要进行的运算比较复杂,需要多行书写,此时可以使用内联输入重定向:
var1=10
var2=1.5
var3=5
var4=2.5
var5=$(bc << EOF
a1 = ( $var1 * $var2)
b1 = ($var3 * $var4)
a1 + b1
EOF
) # 结果:27.5
这里使用
1.5 退出脚本
$?
来查看上一个命令的退出状态码,使用示例如下:
# date
2020年 02月 18日 星期二 11:43:42 CST
# echo $?
0
状态码 | 描述 |
---|---|
0 | 命令成功结束 |
1 | 一般性未知错误 |
2 | 不适合的 |
126 | 命令不可执行 |
127 | 没找到命令 |
128 | 无效的退出参数 |
128+x | 与 |
130 | 通过 |
255 | 范围之外的退出状态码 |
默认情况下,
exit 0
需要注意的是退出状态码的范围只能是
exit 300 #实际的退出状态码为:44
二、分支语句
2.1 if-then
if command
then
commands
fi
如果
2.2 if-then-else
if command
then
commands
else
commands
fi
如果
2.3 if-then-elif
if command1
then
commands
elif command2
then
commands
fi
2.4 test 命令
test
可以用在test
命令中条件成立,test
命令就会退出并返回退出状态码
if test condition
then
commands
fi
if [ condition ]
then
commands
fi
需要注意的是第一个方括号后和第二个方括号前必须加上一个空格,否则就会报错。常见的条件测试分为以下三种:
比较 | 描述 |
---|---|
n1 -eq n2 | 检查 |
n1 -ge n2 | 检查 |
n1 -gt n2 | 检查 |
n1 -le n2 | 检查 |
n1 -lt n2 | 检查 |
n1 -ne n2 | 检查 |
示例如下:
value1=10
value2=11
if [ $value1 -eq $value2 ]
then
echo "The values are equal"
else
echo "The values are different"
fi
比较 | 描述 |
---|---|
str1 = str2 | 检查 |
str1 != str2 | 检查 |
str1 < str2 | 检查 |
str1 > str2 | 检查 |
-n str1 | 检查 |
-z str1 | 检查 |
字符串在进行大小比较时使用的是标准的<
和 >
符号通常会被解释为输入重定向符号和输出重定向符号,因此需要使用 \
对其进行转义。
比较 | 描述 |
---|---|
-d file | 检查 |
-e file | 检查 |
-f file | 检查 |
-r file | 检查 |
-s file | 检查 |
-w file | 检查 |
-x file | 检查 |
-O file | 检查 |
-G file | 检查 |
file1 -nt file2 | 检查 |
file1 -ot file2 | 检查 |
2.5 复合条件
除了上面的单一测试外,还可以使用逻辑运算符来组合测试:
- && :等价于
and ,基本格式如下: [ condition1 ] && [ condition2 ] ; - || :等价于
or ,基本格式如下:[ condition1 ] || [ condition2 ] 。
2.6 if-then 高级特性
- 用于数学表达式的双括号;
- 用于高级字符串处理功能的双方括号。
基本格式如下:
(( expression ))
符 号 | 描 述 |
---|---|
val++ | 后增 |
val– | 后减 |
++val | 先增 |
–val | 先减 |
! | 逻辑求反 |
~ | 位求反 |
** | 幂运算 |
« | 左位移 |
>> | 右位移 |
& | 位布尔和 |
| | 位布尔或 |
&& | 逻辑和 |
|| | 逻辑或 |
示例如下:
val1=10
#
if (( $val1 ** 2 > 90 ))
then
(( val2 = $val1 ** 2 ))
echo "The square of $val1 is $val2"
fi
同时由于使用了双括号,这里的 >
符号也不需要转义。
双方括号的基本格式如下:
[[ expression ]]
它最主要的功能是支持字符串的模式匹配,示例如下:
if [[ $USER == r* ]] #匹配所有以r开头的用户
then
echo "Hello $USER"
else
echo "Sorry, I do not know you"
fi
2.7 case
case variable in
pattern1 | pattern2 ) commands1 ;;
pattern3 ) commands2 ;;
*) default commands ;;
esac
示例如下:
city=上海
case $city in
北京|天津)
echo "华北地区";;
上海)
echo "华东地区";;
广州)
echo "华南地区";;
*)
echo "其他地区";;
esac
三、循环语句
3.1 for
for var in list
do
commands
done
使用示例如下:
for letter in a b c d e f g
do
echo The next letter is $letter
done
# 输出
The next letter is a
The next letter is b
The next letter is c
The next letter is d
The next letter is e
The next letter is f
The next letter is g
默认情况下,
- 空格;
- 制表符;
- 换行符。
如果你想要的采用自定义的字段分隔符,可以通过修改
value="a,b,c,d,e,f"
OLDIFS=$IFS # 保存原有的IFS
IFS=, # 自定义IFS
for letter in $value
do
echo The next letter is $letter
done
IFS=$OLDIFS # 还原默认的IFS,防止影响其他命令的执行
除此之外,
for file in /home/rich/test/*
do
if [ -d "$file" ]
then
echo "$file 是一个目录"
elif [ -f "$file" ]
then
echo "$file 是一个文件"
fi
done
除了上面介绍的
for (( i=1; i <= 10; i++ ))
do
echo "The next number is $i"
done
该风格的
for (( a=1, b=10; a <= 10; a++, b-- ))
do
echo "$a - $b"
done
3.2 while
while test commands
do
other commands
done
使用示例如下:
var1=10
while [ $var1 -gt 0 ]
do
echo $var1
var1=$[ $var1 - 1 ]
done
3.3 until
until test commands
do
other commands
done
使用示例如下:
var1=100
until [ $var1 -eq 0 ]
do
echo $var1
var1=$[ $var1 - 25 ]
done
3.4 break & contiune
和其他大多数编程语言类似,
break 用于退出循环,默认只能跳出当前循环,如果想要跳出多层循环,可以使用break n
进行指定;contiune 用于提前结束本次循环。
基本使用示例如下:
for var1 in 1 2 3 4 5 6 7 8 9 10
do
if [ $var1 -eq 5 ]
then
break
fi
echo "Iteration number: $var1"
done
echo "循环结束"
3.5 处理循环的输出
想要对循环的输出进行处理,可以通过在
for letter in a b c d e f g
do
echo The next letter is $letter
done > output.txt
# cat output.txt
The next letter is a
The next letter is b
The next letter is c
The next letter is d
The next letter is e
The next letter is f
The next letter is g
四、处理用户输入
4.1 命令行参数
命令行参数允许在运行脚本时向脚本传递额外参数,示例如下:
./test 10 20
这些位置参数可以在程序中直接引用:$0
是脚本名,$1
是第一个参数,$2
是第二个参数,依次类推,直到第九个参数 $9
;如果命令行参数不止${10}
。另外还需要注意以下事项:
- 每个参数默认使用空格进行分割,如果参数值包含空格,则需要用单引号或者双引号进行包裹;
- 如果你在启动脚本时使用的是完整的路径,则
$0
也会包含路径信息,此时可以使用basename $0
来获取脚本名称; - 命令行参数的总个数可以使用
$#
来获取; - 如果想要访问所有参数,可以使用
$*
和$@
,两者的区别在于:$*
会将命令行上的所有参数当做一个整体的单词来保存,而$@
会将命令行上的所有参数当做同一个字符串中的多个独立单词来保存,示例如下:
echo
count=1
for param in "$*"
do
echo "\$* Parameter #$count = $param"
count=$[ $count + 1 ]
done
echo
count=1
for param in "$@"
do
echo "\$@ Parameter #$count = $param"
count=$[ $count + 1 ]
done
运行结果:
# ./test.sh a b c
$* Parameter #1 = a b c
$@ Parameter #1 = a
$@ Parameter #2 = b
$@ Parameter #3 = c
4.2 移动变量
$3
的值会移到 $2
中,变量 $2
的值会移到 $1
中,而变量 $1
的值则会被删除(变量 $0
的值,也就是程序名,不会改变
count=1
while [ -n "$1" ]
do
echo "Parameter #$count = $1"
count=$[ $count + 1 ]
shift
done
输出结果如下:
# ./test.sh a b c
Parameter #1 = a
Parameter #2 = b
Parameter #3 = c
如果想要一次移动多个参数,可以使用 shift n
来实现。另外,通过
while [ -n "$1" ]
do
case "$1" in
-a) echo "Found the -a option";;
-b) param="$2" #当找到选项b时,则后面一个参数就是其对应的值
echo "Found the -b option, with parameter value $param"
shift ;;
-c) echo "Found the -c option";;
--) shift
break ;;
*) echo "$1 is not an option";;
esac
shift
done
输出结果如下:
# ./test.sh -a -b heibaiying -d
Found the -a option
Found the -b option, with parameter value heibaiying
-d is not an option
4.3 处理用户输入
命令行参数主要用于初始化脚本运行,但在程序运行的过程中,你可能还需要与用户进行交互,并根据用户的输入来决定程序的走向,此时可以使用
read -p "Please enter your age: " age # -p用于显示提示文本,用户的输入会被保存到age变量中
days=$[ $age * 365 ]
echo "That makes you over $days days old! "
除此之外还支持使用以下参数:
- -s:用于隐藏用户输入,即用户的输入不会显示在终端页面上,通常用在输入密码等敏感信息时;
- -t:用于指定等待的秒数,如果在指定时间内用户没有输入,则
read 命令会以非0 状态码退出,示例如下:
if read -t 5 -p "Please enter your name: " name
then
echo "Hello $name, welcome to my script"
else
echo "Sorry, too slow! "
fi
count=1
cat test | while read line
do
echo "Line $count: $line"
count=$[ $count + 1]
done
五、处理程序输出
5.1 文件描述符
文件描述符 | 缩写 | 描述 |
---|---|---|
0 | STDIN | 标准输入 |
1 | STDOUT | 标准输出 |
2 | STDERR | 标准错误 |
- STDIN:代表
shell 的标准输入,它可以是终端界面的键盘,也可以是使用重定向符号(<)读取的文件。 - STDOUT:代表
shell 的标准输出,它通常是终端界面的显示器,也可以是使用重定向符号(>)创建的文件。 - STDERR:代表
shell 的标准错误输出,默认情况下,STDERR 文件描述符和STDOUT 文件描述符指向相同的地方。也就是说,在默认情况下,错误消息会被输出到显示器上。当然你也可以重定向错误的输出位置:
ls -al badfile 2> test1 #将错误输出重定向到test1文件,正常的输出默认显示到屏幕上
ls -al badfile 2> test2 1> test3 #将错误的输出重定向到test2,正常输出重定向到test1
如果你想要将正常输出和错误输出重定向到同一个文件,可以使用 &>
符号进行简写:
ls -al badfile &> test4
5.2 输出重定向
如果你想要在脚本中输出特定的错误信息,此时可以在文件描述符数字之前加一个
echo "捕获到未知异常" >&2
此时异常信息就会被添加到标准错误输出中。但上面提到过,标准错误输出默认是显示在界面上,如果想要将捕获到的错误信息输出到指定文件中,则还需要对标准错误输出进行重定向:
$ ./test 2> error.txt
如果需要对每条语句都进行临时重定向,此时可以使用 exec
进行一次性永久重定向:
exec 1>testout #之后的所有正常输出都重定向到testout文件中
echo "This is a test of redirecting all output"
echo "from a script to another file."
echo "without having to redirect every individual line"
5.3 输入重定向
exec 0< testfile
5.4 自定义重定向
exec 3>test3out #将文件描述符为3的输出重定向到test3out
echo "and this should be stored in the file" >&3
如果之后想要关闭自定义的文件描述符,需要将它重定向到特殊符号 &-
:
exec 3>&-
需要注意的是一旦关闭了文件描述符,就不能再向它写入任何数据,否则就会抛出异常。
5.5 阻止命令输出
如果你不需要任何输出信息,此时可以将输出重定向到一个叫作/dev/null
,重定向到该位置的任何数据都会被丢弃,不会显示:
$ ls -al > /dev/null
$ cat /dev/null
$
5.6 创建临时文件
通常你会需要创建某些临时文件用于保存程序输出,此时可以使用
# mktemp testing.XXXXXX
testing.UdxhQH
# mktemp testing.XXXXXX
testing.3pe64N
# mktemp testing.XXX
testing.SWw
除此之外,它还支持以下参数:
- -t :该选项会强制
mktemp 在系统的临时目录(如/tmp )下创建文件, 此时它会返回临时文件的全路径; - -d:该选项告诉
mktemp 需要创建的是一个目录而不是文件。
5.7 记录输出
如果你需要将输出既显示在终端上,又写入到文件中,此时可以使用
# date | tee testfile
2020年 02月 20日 星期四 14:21:40 CST
# cat testfile
2020年 02月 20日 星期四 14:21:40 CST
六、创建函数
6.1 创建函数
在
# 方式一:使用function关键字
function name {
commands
}
# 方式二
name () {
commands
}
调用函数时,只需要在对应行中指定函数名即可,示例如下:
function func {
echo "This is an example of a function"
}
func #调用函数
函数的调用必须在函数的定义之后,另外函数名必须是唯一的,如果出现同名函数,则后面的函数定义会覆盖前面的函数定义。
6.2 函数参数
如果想要为函数传递所需的参数,只需要在函数名后面跟上所需的参数即可。在函数体内可以通过 $1
、 $2
来进行引用的;$0
则表示具体的函数名,$#
表示参数的总数:
function addem {
if [ $# -eq 0 ] || [ $# -gt 2 ] #如果没有传递参数或者传递两个以上的参数都返回-1
then
echo -1
elif [ $# -eq 1 ]
then
echo $[ $1 + $1 ]
else
echo $[ $1 + $2 ]
fi
}
echo -n "Adding 10 and 15: "
value=$(addem 10 15)
echo $value #25
同时如上面的例子所示,函数的返回值使用
6.3 退出状态码
默认情况下,函数中最后一条命令的退出状态码就是整个函数的退出状态码。在函数执行后,可以使用变量 $?
来获取函数的退出状态码。如果你想要返回自定义的状态码,可以使用
function func {
read -p "Enter a value: " value
echo "doubling the value"
return $[ $value * 2 ]
}
和命令的退出状态码一致,这里的退出状态码的取值范围必须是
6.4 局部变量
在
value1=100
value2=100
function func {
local value1=200
value2=200
echo "局部变量value1 :" $value1 #200
echo "局部变量value2 :" $value2 #200
}
func
echo "全局变量value1 :" $value1 #100 使用局部变量可以保证同名的全局变量不被污染
echo "全局变量value2 :" $value2 #200 没有使用局部变量,全局变量收到了勿扰
6.5 函数库
某些时候你可能需要引用第三方的函数库,或者你的通用函数比较多,此时可以将通用函数定义到一个统一的文件中。如下,就是一个简单的自定义函数库:
$ cat myfuncs
# 自定义函数库
function addem {
echo $[ $1 + $2 ]
}
function multem {
echo $[ $1 * $2 ]
}
function divem {
if [ $2 -ne 0 ]
then
echo $[ $1 / $2 ]
else
echo -1
fi
}
想要在其他脚本中引用该函数库,可以使用
. ./myfuncs #引用函数库
value1=10
value2=5
result1=$(addem $value1 $value2) #直接调用函数库中的函数
七、控制脚本
7.1 处理信号
信号 | 值 | 描述 |
---|---|---|
1 | SIGHUP | 挂起进程 |
2 | SIGINT | 终止进程(可以通过键盘组合 |
3 | SIGQUIT | 停止进程 |
9 | SIGKILL | 无条件终止进程 |
15 | SIGTERM | 尽可能终止进程 |
17 | SIGSTOP | 无条件停止进程,但不是终止进程 |
18 | SIGTSTP | 停止或暂停进程,但不终止进程程(可以通过键盘组合 |
19 | SIGCONT | 继续运行处于停止状态的进程 |
想要在脚本中捕获这些信号,可以通过
trap commonds signals
使用示例如下:
trap "echo ' Sorry! I have trapped Ctrl-C'" SIGINT #捕获SIGINT信号并执行echo命令
count=1
while [ $count -le 10 ]
do
echo "Loop #$count"
sleep 1
count=$[ $count + 1 ]
done
除了可以在
trap "echo Goodbye..." EXIT #捕获脚本退出的EXIT信号
要想在脚本中的不同位置进行不同的捕获处理,你只需重新使用
trap -- SIGINT #删除对SIGINT命令的捕获
7.2 后台运行
如果想要以后台模式运行
$ ./test.sh &
采用该方式运行的后台进程默认是与终端会话关联在一起的,如果终端会话退出了,那么后台进程也会随之退出,如果希望后台进程在登出控制台之后仍能继续运行,则可以使用
$ nohup ./test.sh &
由于
7.3 作业控制
想要查看
# jobs -l
[1]+ 14630 停止 ./test.sh
[2]- 14831 运行中 ./test.sh > test.out &
-l
表示列出作业的
- -r:只列运行中的作业;
- -s:只列出已停止的作业。
如上输出所示,带加号的作业是默认作业,当默认作业处理完成后,带减号的作业会成为下一个默认作业。不论
在
bg 1 #将作业号为1的作业以后台进程的方式进程重启
fg 1 #将作业号为1的作业以前台进程的方式进程重启
7.4 定时作业
- at:用于一次性的定时作业;
- corn:用于需要周期性执行的定时作业。
at [-f filename] time
-f
用于指定脚本文件,
- 标准的小时和分钟格式,比如
10:15 ; AM/PM 指示符,比如10:15 PM ;- 特定可命名时间,比如
now 、noon、midnight 或者teatime (4 PM) 。
除了指定运行作业的时间,也可以通过不同的日期格式指定特定的日期:
- 标准日期格式,比如
MMDDYY 、MM/DD/YY 或DD.MM.YY ; - 文本日期,比如
Jul 4 或Dec 25 ,加不加年份均可。
另外还可以指定时间增量:
- now + 25 min;
- 10:15 + 7 days。
使用示例如下:
$ at -f test.sh now #立即执行test.sh脚本
使用a~z
或大写字母 A~Z
来指代,作业队列的字母排序越高,作业运行的优先级就越低。默认情况下, -q
参数进行指定。
需要注意的是,显示器并不会关联
最后atq
命令用于查看处于等待状态的作业,并且支持使用 atrm
命令来删除处于等待中的作业:
$ atrm 18 #删除作业号为18的等待作业
min hour dayofmonth month dayofweek command
使用示例如下:
15 10 * * * /root/test.sh #每天15:10分执行test.sh脚本
想要新建基于该crontab -e
命令,然后在打开的文本编辑器(操作类似crontab -l
来查看系统中所有的定时任务:
# crontab -l
15 10 * * * /root/test.sh
Links
- https://mojotv.cn/2018/12/26/shell-cheat-sheet
Shell 语法快速入门 - https://mojotv.cn/2019/07/26/general-shell-resources
linux-Bash 命令快速查询 - https://devhints.io/bash 提取其中的命令语句为
CheatSheet - https://linuxconfig.org/bash-scripting-cheat-sheet