本章的 Shell 脚本,如无特别说明,均指 Bash。
前提:用户对脚本有 rx
权限。
一般可直接输入脚本路径,默认会使用当前 Shell 执行(之后说具体情况):
脚本
如果脚本在当前目录下,务必加 ./
,不能直接写文件名:
./脚本文件名
也适用于其他的可执行程序。
不需要脚本有 x
权限,但至少有 r
权限。
Shell命令 脚本
如果脚本在当前目录,不需要再加 ./
,如:
bash test.sh
source
或 .
不需要脚本有 x
权限,但至少有 r
权限。
source 脚本
. 脚本
与其他方式的区别:其中定义的变量在此次 bash 进程中会保留下来。
以 #
开头,在一行内生效。
也可以将命令屏蔽掉。
# 这是注释
ls -al # 列出当前目录下所有文件的详细信息
# rm -rf /
形如:
#!/bin/bash
放在第一行,必须写绝对路径。
用来告诉 Shell ,脚本由谁来执行,不一定是 Shell;写了之后可以直接执行:
#!/bin/cat
Hello world
但不是所有命令都会忽略注释,包括 shebang,如上例的执行结果:
#!/bin/cat
Hello world
env
搭配使用上例考虑到 bash 可能不在 /bin
下,比较保险的写法:
#!/usr/bin/env bash
如果要使用的命令带选项,应该给 env
加上 -S
选项:
#!/usr/bin/env -S bash -r
read
- 输入read [选项] [变量名]
变量名本身不用加 $
,下同。
如不指定变量名,则会放在 REPLY
变量中。
选项:
-p 文字
:提示文本-t 整数
:等待的秒数,如等待期间无输入,则跳过,变量名会赋为空值,返回值非 0
仅支持整数四则运算、取余、括号(+
-
*
/
%
(
)
),除法仅取整数部分。
可使用如下格式:
$((算式))
# 等于算式结果
例:
ding@ding-server:~$ echo 1+2
1+2
ding@ding-server:~$ echo (1+2)
-bash: syntax error near unexpected token `1+2'
ding@ding-server:~$ echo $(1+2)
1+2: command not found
ding@ding-server:~$ echo $((1+2))
3
bc
- 简单的计算器bc [选项] [文件]
... | bc [选项]
不指定文件,则进入交互模式。
选项:
-l
:加载数学库,支持更多命令只列举一部分,详细的可参考手册
+
-
*
/
%
(
)
a ^ b # b 为整数
a = 1
scale # 保留小数位数,默认为 0,加减乘余不受限
ibase # 输入进制,默认为 10
obase # 输出进制,默认为 10
a 操作符= b # a = a 操作符 b
a ++ # 输出 a,之后 a += 1
a -- # 输出 a,之后 a -= 1
++ a # a += 1,之后输出 a
-- a # a -= 1,之后输出 a
1
,否则为 0
a < b
a > b
a == b
a != b
a <= b
a >= b
0
或 1
a && b
a || b
!a
length(语句) # 返回语句表示数字的总位数
scale(语句) # 返回语句表示数字的小数位数
sqrt(语句) # 算术平方根,只能用在非负数
-l
选项后支持的一些函数# 默认 scale = 20
s(x) # sin x,x 为弧度制
c(x) # cos x,x 为弧度制
a(x) # arctan x,返回弧度值
l(x) # ln x
e(x) # e^x
求 π 值:
ding@ding-server:~$ echo "scale=20; 4*a(1)" | bc -l
3.14159265358979323844
test
- 判断直接执行无显示,需看返回值:0
为真,1
为假。
# test 后为条件
test 选项 文件
test 文件1 选项 文件2
test 整数1 选项 整数2
test 选项 字符串
test 字符串1 选项 字符串2
文件类型判断
-e
:是否存在-s
:是否存在且非空-b |
-c |
-d |
-p |
-f |
-L / -h |
-S |
---|---|---|---|---|---|---|
块设备 | 字符设备 | 目录 | 管道 | 普通文件 | 符号链接 | 套接字 |
文件权限检测:是否存在且有对应权限
-r |
-w |
-x |
-u |
-g |
-k |
---|---|---|---|---|---|
读 | 写 | 执行 | SUID | SGID | SBIT |
-nt
:(newer than)前面的文件是否比后面的文件新-ot
:(older than)前面的文件是否比后面的文件旧-ef
:两个文件是否是一个文件(看是否指向同一个 inode)选项 | 全称 | 含义 |
---|---|---|
-eq |
equal | == |
-ne |
not equal | != |
-gt |
greater than | > |
-lt |
less than | < |
-ge |
greater than or equal | >= |
-le |
less than or equal | <= |
-z
:字符串长度是否为 0
(是否为空字符串),若空为 0
(真)-n
或省略:字符串长度是否非 0
(是否非空字符串) ,若空为 1
(假)==
!=
-a
:与test 条件1 -a 条件2
-o
:或test 条件1 -o 条件2
!
:非test ! 条件
[ 条件 ]
注意条件两端以及条件内的判断符两边都要有空格;变量最好用双引号包裹,以免出现其中带空格的情况。
[ "$HOME" == "$MAIL" ]
if
Shell 中 0
为真,非 0
为假。
if 条件
then
条件为真的语句
fi
if 条件
then
条件为真的语句
else
条件为假的语句
fi
if 条件1
then
条件 1 为真的语句
elif 条件2
then
条件 1 为假,条件 2 为真的语句
else
上述条件均为假的语句
fi
elif
块可以写多个。
case
/ switch
case $变量名 in
值1)
变量值为值 1 时的语句,即语句 1
;;
值2)
变量值为值 2 时的语句,即语句 2
;;
*)
以上条件都不满足时的语句,即默认语句
;;
esac
值1)
块可以写多个。
while
条件成立时进行循环,直到不成立为止。
while 条件
do
循环体
done
until
进行循环,直到条件成立为止。
until 条件
do
循环体
done
for
for 变量名 in 值1 值2 值3
do
循环体
done
给变量名分别赋不同值进行循环。
上面的 值1 值2 值3
被称为序列。序列一般可以用空格或换行符分割,但后者一般只用在变量中。
想要生成 1
~100
的序列,可以用:
seq 1 100
# 或
{1..100}
用在 for
中如下:
for 变量名 in $(seq 1 100)
do
循环体
done
for 变量名 in {1..100}
do
循环体
done
后者的形式也可以用于字母:
{a..g}
for (( 循环前操作; 条件; 每轮循环后操作 ))
do
循环体
done
一般会在循环前赋值,条件和循环后操作都会操作值。
一般的定义方式:
function 函数名() {
代码段
}
最简单的定义方式:
函数名 {
代码段
}
函数可以给返回值,写在代码段中:
return 要返回的值
一般的调用方式:
函数名
带参数的调用方式:
函数名 参数1 参数2 ...
取函数执行完后的返回值:
$?
$#
:参数个数$*
:作为字符串输出所有参数,分隔符默认为空格$@
:作为数组输出所有参数(可用作序列)${数字}
:第几个参数(从 1
开始,10
以下可以不用大括号)例:
函数名 参数1 参数2 参数3
$#=3
$*="参数1 参数2 参数3"
$@="参数1" "参数2" "参数3"
$1="参数1"
$2="参数2"
$3="参数3"
shift
- 参数变量号偏移shift
例:
函数名 参数1 参数2 参数3
$#=3
$*="参数1 参数2 参数3"
$@="参数1" "参数2" "参数3"
$1="参数1"
$2="参数2"
$3="参数3"
函数体中使用 shift
语句之后:
$#=2
$*="参数2 参数3"
$@="参数2" "参数3"
$1="参数2"
$2="参数3"
命令路径 选项1 选项2 选项3
$0="命令路径"
$1="选项1"
$2="选项2"
$3="选项3"
$#=3
$@="选项1" "选项2" "选项3"
$*="选项1 选项2 选项3"
也适用偏移。
exit 返回值
bash [选项] [脚本文件]
选项:
-n
:不执行脚本,仅查询语法问题-v
:执行脚本前,输出脚本内容-x
:将使用到的脚本内容先显示在屏幕上例:
设 t.sh
内容如下:
#!/bin/bash
echo $0
for i in $@
do
echo $i
done
ding@ding-server:~$ bash -v ./t.sh a c d f e
#!/bin/bash
echo $0
./t.sh
for i in $@
do
echo $i
done
a
c
d
f
e
ding@ding-server:~$ bash -x ./t.sh a c d f e
+ echo ./t.sh
./t.sh
+ for i in $@
+ echo a
a
+ for i in $@
+ echo c
c
+ for i in $@
+ echo d
d
+ for i in $@
+ echo f
f
+ for i in $@
+ echo e
e
变量名=变量值 bash [选项] [脚本文件]
利用 source
或 .
环境变量 RANDOM
为整数,取值范围 0
~32767
。
将其转化为较小的 0
~n
区间的一种方式:
$(($RANDOM % (n+1)))
ding@ding-server:~$ date # 显示当前日期时间
Mon 30 May 2022 06:24:22 PM CST
ding@ding-server:~$ date +%Y-%m-%d # 格式化输出日期
2022-05-30
ding@ding-server:~$ date +%H:%M:%S # 格式化输出时间
18:24:52
ding@ding-server:~$ date +"%Y-%m-%d %H:%M:%S" # 格式化输出日期时间
2022-05-30 18:31:35
ding@ding-server:~$ date +%s # 日期时间转时间戳
1653906461
ding@ding-server:~$ date --date='@2147483647' # 时间戳转日期时间
Tue 19 Jan 2038 11:14:07 AM CST
ding@ding-server:~$ date --date='2022-01-01 10:00:00' # 日期时间输入
Sat 01 Jan 2022 10:00:00 AM CST
ding@ding-server:~$ date --date='2022-01-01 10:00:00 UTC' # 日期时间时区输入
Sat 01 Jan 2022 06:00:00 PM CST
该命令不检查文件是否存在,所以也可以作为分割字符串的方式。
ding@ding-server:~$ dirname /dir1/dir2/file1
/dir1/dir2
ding@ding-server:~$ basename /dir1/dir2/file1
file1
ding@ding-server:~$ dirname dir1/dir2/file1
dir1/dir2
ding@ding-server:~$ basename dir1/dir2/file1
file1
ding@ding-server:~$ dirname file1
.
ding@ding-server:~$ basename file1
file1