-
bash(sh) /path/script.sh
-
赋予执行权限 /path/script.sh
或 ./scripts.sh
-
source script.sh
或 . script.sh
- 在此方法中执行,子 shell 中定义的变量,可在父 shell 中调用(其他方式父 shell 不能直接调用子 shell 的变量)
-
bash(sh) < script.sh
或 cat script.sh | bash(sh)
- 以
#!/bin/bash
或 #!/bin/sh
开头
- 注释标注, 作者、联系方式、时间、版本、脚本描述
- 脚本尽量不是使用中文注释
- 脚本以
.sh
为扩展名
- 成对书写符号、条件控制语句等。如:
[]
、{}
、if []; then fi
- 设置 : 变量名=值
- 打印 : echo $变量名
- 取消 : unset 变量名
变量名=value
变量名='value'
变量名="value"
变量名=$(ls)
变量名=`ls`
变量名一般由字母、数字、下划线组成
a=123
b=123-$a
## 当值没有单(双)引号的时候,变量的值为 123-变量 a 的值,若变量值出现空格,则值为第一个空格之前的数据
c='123-$a'
## 当值存在单引号的时候,值为什么,打印结果则为什么,引号内内容视为一个整体
d="123-$a"
## 当值为双引号的时候,若值中存在变量、命令(需要转义)等,会优先把变量、命令结果输出,在打印所有值
变量名 |
说明 |
$0 |
获取当前执行脚本的名称,如果执行脚本带有路径,则包含脚本路径 |
$n |
获取当前执行脚本的第 n 个参数,n=1-9,n 为 0 时,表示脚本文件名,如果 n 大于 9,用大括号括起来${10},参数以空格隔开 |
$* |
获取当前脚本所有传入的参数,将所有的参数视为单个字符串,相当于"$1$2$3"… |
$# |
获取当前脚本传入参数的个数总数 |
$@ |
获取当前脚本所有传入参数,将所有的参数分别传入至其他变量或脚本(获取脚本最后一个参数:${@: -1} |
$? |
确定上一个指令的返回值,0 成功, 非 0 不成功 2 : 权限拒绝 ;
1-125 : 运行失败,参数传递错误 ;
126 : 找到该命令,但无法执行 ;
127 :未找到运行的命令 ;
128 : 命令被强行中断 ; 脚本中一般用exit 0 , 在执行脚本,返回值给$?, 函数中一般用return 返回值给$? |
$$ |
当前脚本执行的进程号 |
$! |
获得之前(上一个)进程 ID |
$_ |
上一条命令的最后一个参数 |
$PPID |
父进程的进程 ID |
$PS1 |
主提示符串,默认值是$ |
$*
和 $@
的示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
[root@00 ~]# set -- hello my "linux shell"
[root@00 ~]# echo $#
3
[root@00 ~]# echo $1
hello
[root@00 ~]# echo $2
my
[root@00 ~]# echo $3
linux shell
[root@00 ~]# for i in "$@"; do echo $i;done
hello
my
linux shell
[root@00 ~]# for i in "$*"; do echo $i;done
hello my linux shell
|
表达式 |
说明 |
${#string} |
返回$string 的长度 |
${string:position} |
在$stirng 中,从$position 之后开始提取子串 |
${string:position:length} |
在$string ,从位置$position 之后开始提取长度为$length 的子串 |
${string#substring} |
从变量$string 开头开始删除最短匹配$substring 的子串 |
${string##substring} |
从变量$string 开头开始删除最长匹配$substring 的子串 |
${string%substring} |
从变量$string 结尾开始删除最短匹配$substring 的子串 |
${string%%substring} |
从变量$string 结尾开始删除最长匹配$substring 的子串 |
${string/pattern/parameter} |
在变量string 中,使用parameter 替换pattern 匹配的第一个值 |
${string//pattern/parameter} |
在变量string 中,使用parameter 替换所有pattern 匹配的值 |
${string/#pattern/parameter} |
在变量string 中,使用parameter 替换以pattern 开头的值 |
${string/%pattern/parameter} |
在变量string 中,使用parameter 替换以pattern 结尾的值 |
运算符号 |
替换 |
${value:-word} |
如果变量value 存在且非null ,则返回变量的值,否则,返回word 字符串. 例: res=${value:-word} ,如果value 未定义,则res 的值为word |
${value:=word} |
如果变量value 存在且非null ,则返回变量的值,否则,则设置这个变量值为word . 例: res=${value:=word} ,如果value 未定义,则res 的值为word ,value 值也为word |
${value:+word} |
如果value 存在且非null ,则返回word ,否则返回null .例res=${value:+word} ,如果value 已经定义, 则res 的值为word ,如果value 值未定义,则res 值为null(空) |
${value:?message} |
如果变量value 存在且非null ,则返回变量value 的值,否则返回信息bash: value: message ,例 echo ${value:?is null} ,如果value 值已定义,则返回value 定义值,否者返回 bash: value: is null ,退出状态码为1 |
在Shell脚本中,[]
和[[]]
都用于条件测试,但它们之间存在一些重要的差异
- 兼容性:
[]
是POSIX标准的测试语句,因此它在所有POSIX兼容的shell中都可以使用,包括bash、dash、ksh等。而[[]]
是bash的扩展,只能在bash和一些兼容bash的shell中使用,如zsh。
- 排序:在
[[]]
中,你可以使用<
和>
来比较字符串的字典序。例如,[[ "abc" < "def" ]]
会返回真。而在[]
中,这样的比较会导致语法错误。
- 逻辑操作符:在
[]
中,你需要使用-a
和-o
来表示逻辑与和逻辑或。例如,[ "$a" -eq 1 -a "$b" -eq 2 ]
。而在[[]]
中,你可以使用更直观的&&
和||
。例如,[[ "$a" -eq 1 && "$b" -eq 2 ]]
。
- 字符串匹配:在
[[]]
中,==
右边的字符串会被视为模式,而在[]
中,它只是一个普通的字符串。例如,[[ "$a" == a* ]]
会检查$a
是否以a
开头,而[ "$a" == a* ]
会检查$a
是否等于字符串a*
。
- 正则匹配:
[[]]
支持使用=~
进行正则表达式匹配。例如,[[ "$a" =~ ^a.* ]]
会检查$a
是否以a
开头。而[]
不支持正则表达式。
- 变量引用:在
[]
中,如果一个变量未定义,那么它会被视为一个空字符串,除非你用双引号引起来。例如,如果$a
未定义,那么[ $a == "" ]
会导致语法错误,而[ "$a" == "" ]
则不会。而在[[]]
中,即使变量未定义,也不需要引号。
运算符 |
说明 |
|
++ -- |
自增与自减; 符号在前代表先运算在赋值,符号在后代表先赋值在运算 |
- |
+ - ! ~ |
- |
- |
* / % |
乘、除、模 |
/ 取整 ; % 取余 |
+ - |
加、减 |
- |
< <= > >= |
小于、小于等于、大于、大于等于 |
- |
== != =~ |
等于、不等于、正则匹配符 |
[[ $VAR =~ ^[a-zA-Z] ]] ,正则不可用引号括起来,变量可单双引号 |
<< >> |
位运算: 左移、右移 |
二进制计算 |
&& |
逻辑的 and |
true && false ,结果 false |
` |
|
` |
= += -= *= /= %= |
赋值运算 |
(a+=b) == (a=a+b) ; 其他同理 |
** |
幕运算 |
2**3=8 |
字符串比较运算符(建议在(()) 和[[]] 中使用的) |
算术运算符(建议在[] 以及test 中使用的) |
说明 |
== 或= |
-eq |
检测 2 个数是否相等,相等返回true |
!= |
-ne |
检测 2 个数是否不相等,相等返回true |
> |
-gt |
检测左边的数是否大于右边的,如果是,则返回true |
>= |
-ge |
检测左边的数是否大于等于右边的,如果是,则返回true |
< |
-lt |
检测左边的数是否小于右边的,如果是,则返回true |
<= |
-le |
检测左边的数是否小于等于右边的,如果是,则返回true |
运算符(建议在[[]] 中使用) |
运算符(建议在[] 和test 中使用) |
说明 |
! |
! |
非运算,表达式为true ,则返回false ,否则返回true ; 例: [!false] 返回true |
|| |
-o |
或运算,有一个表达式为true,则返回``true |
&& |
-a |
与运算,2 个表达式都为true,才返回``true |
判断符(man test) |
说明(如果存在为 true) |
-e |
文件存在 |
-f |
被测文件是一个 regular 文件(正常文件,非目录或设备) |
-s |
如果文件存在且文件大小大于零,则返回真 |
-d |
被测对象是目录 |
-b |
被测对象是块设备 |
-c |
被测对象是字符设备 |
-p |
被测对象是管道 |
-h |
被测文件是符号连接 |
-L |
被测文件是符号连接 |
-s |
被测文件是一个 socket |
-t |
关联到一个终端设备的文件描述符。用来检测脚本的stdin[-t0] 或[-t1] 是一个终端 |
-r |
文件具有读权限,针对运行脚本的用户 |
-w |
文件具有写权限,针对运行脚本的用户 |
-x |
文件具有执行权限,针对运行脚本的用户 |
-u |
set-user-id(suid)标志到文件,即普通用户可以使用的 root 权限文件,通过 chmod +s file 实现 |
-k |
设置粘贴位 |
-O |
运行脚本的用户是文件的所有者 |
-G |
文件的 group-id 和运行脚本的用户相同 |
-N |
从文件最后被阅读到现在,是否被修改 |
-z |
字符串为 null,即长度为 0 |
-n |
字符串不为 null,即长度不为 0 |
= |
字符串是否相等,可以使用== 代替= |
!= |
字符串是否不相等 |
f1 -nt f2 |
文件 f1 是否比 f2 新 |
f1 -ot f2 |
文件 f1 是否比 f2 旧 |
f1 -ef f2 |
文件 f1 和 f2 是否硬连接到同一个文件 |
重定向操作符 |
功能 |
< filename |
重定向输入 |
> filename |
重定向输出 |
>> filename |
追加输出 |
2> filename |
重定向标准错误输出 |
2>> filename |
重定向和追加标准错误输出 |
&> filename |
重定向标准输出和标准错误输出 |
>& filename |
重定向标准输出和标准错误输出(首选方式) |
2>&1 |
将标准错误输出重定向到输出的去处 |
1>&2 |
将输出重定向到标准错误输出的去处 |
> |
重定向输出时忽略 noclobber |
<> filename |
如果是一个设备文件(/dev),使用巍峨年作为标准输入和标准输出 |
(())
- 代表直接进行运算:,如:
echo $((1+1+2))
或 ((a=1+1+2)) ; echo $a
let
- 将等式直接进行运算, 如:
i=1; i=i+1 ; (echo $i) == i+1
, 使用let
将等式直接进行计算i=1;let i=i+1; (echo $i) == 2
expr
- 只能用于整数的计算,可用于判断变量是否为整数(运算符号两边必须有空格,特殊符号需要需要转移), 如
(expr 1 + 1) == 2; (expr 1 + 1.1) == (expr: non-integer argument)
- 计算字符串长度
a=123456; expr length $a
bc
- 可用于小数计算,进制之间的转换, 如:
(echo 1.1 + 2| bc) == 3.1
、(echo "obase=2;10"|bc) == 1010
$[]
typeset
- 个人的理解就是定义多个 int 类型的变量,若定义变量值为非数字,则值将被赋予为 0(建议自行测试). 如:
a=0;typeset -i b=1 c=a;(echo $b $c) == (1 0)
、a="bb" ; b="123";typeset -i c=a d=b e=222; (echo $c $d $e) == (0 123 222)
-p 设置提示信息; -t 设置输入等待时间(默认s),超过时间自动退出
1
2
3
4
|
[root@00 ~]# read -p "hello bash :" num1 num2 # " 和变量之间需要一个空格
hello bash : hello_1 hello_2
[root@00 ~]# echo $num1 $num2
hello_1 hello_2
|
test <表达式>
,如: test -f /etc/hosts
[ <表达式> ]
,如: [ -f /etc/hosts ]
[[ <表达式> ]]
,如: [[ -f /etc/hosts ]]
、[[ -d /etc && -f /etc/hosts ]]
、[[ -d /etc -a -f /etc/hosts ]]
、[[ -d /etc || -f /etc/hosts ]]
、[[ -d /etc -o -f /etc/hosts ]]
- 上述一和二是等等价的, 如:
test -f /etc/hosts == [ -f /etc/hosts ]
[[]]
与[]
的区别是在[[]]
中可以使用通配符模式进行匹配(如[[ hello == hell? ]] == true
:其匹配字符串或通配符时,可不需要引号,[]
也可不需要引号,但据说有时候会出现故障,因此建议加上引号), &&、||、>、<
等操作符也可以应用于[[]]
中,但不能应用于[]
中
关于()
、(())
、[]
、[[]]
、{}
几个的区别,接入一个很详细的文档,但是我表示没怎么看懂,先记录下来,以后慢慢看
https://blog.csdn.net/taiyang1987912/article/details/39551385
1
2
3
4
5
6
7
8
9
|
<函数名>(){
...
return n
}
function <函数名>(){
...
return n
}
|
- 直接执行函数名即可
- 执行函数的时候,不需要带括号
- 函数定义必须在调用之前, shell 执行,从上向下
- 带参数调用和脚本传参一样
- 在函数中,
$0
仍然是脚本名称 , $1..n
代表是脚本参数
- 在函数中使用
exit
会退出整个脚本,return
是跳出当前函数
- 函数脚本引用
- 脚本内加载
. /path/scrpt.sh
,相当于将/path/script.sh
内容直接加载到当前脚本中
1
2
3
4
5
6
7
8
9
10
11
12
|
case $VAR in
"1"|a) echo 1
;;
2|3) echo 2 or 3
;;
*) echo default
;;
esac
if [ $VAR == $VAR ]; then
...
fi
|
1
2
3
4
5
6
7
8
|
# 满足条件进入(先判断,在执行)
while True ; do
...
done
# 先执行一次,在判断
until True ; do
...
done
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 示例1(文件最后一行内容需要换行符,否则将无法读取)
exec < file
while read line; do
echo $line
done
# 示例2 (文件最后一行内容需要换行符,否则将无法读取)
cat FILE | while read line ; do
echo $line
done
# 示例3,此方法根据评测据说效率最高 (文件最后一行内容需要换行符,否则将无法读取)
while read line ; do
...
done < FILE
|
select 可用于选择包含多个选项的菜单
1
2
3
4
5
6
|
# set shuttle list
PS3="请选择要操作的编号 : "
select shuttle in columbia endeavour challenger discovery atlantis enterprise pathfinder; do
echo "$REPLY. $shuttle selected"
done
|
1
2
3
4
5
6
7
8
|
# foreach
for i in <...> ; do
...
done
# for
for ((<..>;<..>; <..> )); do
...
done
|
1
2
3
4
5
6
7
8
9
|
for i in {1..100}; do
echo $i
done
for (( i = 0; i < 100; i ++ )); do
echo $i
done
# echo {1..100}+ |sed 's#\+$##g'|bc
|
- 定义
array=(v1 v2 v3)
{ array=(v1 v2 v3) } == { declare -A array=([0]=v1 [1]=v2 [2]=v3) }
- 长度
len=${#array[@]}
或者 len=${#array[*]}
- 打印
echo ${array[0]}
、echo ${array[1]}
…
- 变量列表
${array[*]}
、${array[@]}
- 增、删、改
array[3]=v4
、unset array[0]
、array[0]=v5
- 增加 :(整型数组) 在数组增加时候,需要指定下标,增加到那个下标下面,则取值的时候就应该从那个下标取定义的值(如果要深入使用,此处一定要自行测试,他与其他语言数组有一定区别,个人理解,要说用其他语言来形容的话,他就相当于只有 map 或多维 map 的概念)
- 打印数组下标
echo ${!array[@]}
-n
不会执行脚本,仅仅检查语法是否有问题,并给出错误提示
-v
在执行脚本是时候,先将脚本的内容输出到屏幕上,然后执行脚本,也会提示错误信息,相当于先 cat 了
-x
将执行的脚本内容即输出显示到屏幕上面
-e
令脚本在发生错误时退出而不是继续运行
-u
检查是否使用了未赋值的变量
1
2
3
4
5
6
7
8
9
10
|
#!/bin/bash
for i in {1..20} ; do
if [ $i -gt 10 ]; then
set -x # <<====== 分段调试开始
echo "i>20: $i"
set +x # <<====== 分段调试结束
else
echo "i<=10: $i"
fi
done
|
1
2
3
4
5
6
7
8
9
10
11
12
|
[root@00 ~]# set -x # <<==== 调试启动
[root@00 ~]# for i in 1 2 3 ; do echo $i ; done
+ for i in 1 2 3
+ echo 1
1
+ for i in 1 2 3
+ echo 2
2
+ for i in 1 2 3
+ echo 3
3
[root@00 ~]# set +x # <<==== 调试结束
|
信号 |
- |
说明(stty -a 查看键盘按键对应的信号) |
1 |
HUP |SIGHUP |
挂起,通常因终端雕像或用户退出引发 |
2 |
INT |SIGINT |
中断,通常是按下Ctrl+C 引发 |
3 |
QUIT |SIGQUIT |
退出,通常是按下Ctrl+/ 引发 |
6 |
ABRT |SIGABRT |
终止,通常因为严重的执行错误而引发 |
9 |
SIGKILL |
立即终止进程 |
14 |
ALRM |SIGALRM |
报警,通常用来处理超时 |
15 |
TERM |SIGTERM |
终止,通常在系统挂机时发送 |
20 |
TSTP |SIGSTP |
停止进程的运行,但该型号可以被处理和忽略,用户键入SUSP 字符(通常是Ctrl+z )发出这个信号 |
trap 命令用于在接受到信号后将要采取的行动,常用于脚本程序中断时完成清理工作,可用于跳板机脚本制作(由于脚本从上向下进行执行,因此需要将在要保护的程序片段之前进行 trap 设置)
语法:
trap -l
打印所有信号
trap -p
打印当前 trap 设置
示例:
trap "" signals
为空表示这个信号失效
trap "commands" signals
表示收到signals
信号时,信号功能副为同时执行commands
命令
trap signals
信号复原,取消已经设置的信号
1
2
3
4
5
6
7
8
|
# 临时生效,终端退出失效
[root@00 ~]# trap "" 2 # 设置信号
[root@00 ~]# trap -p # 打印设置信号
trap -- '' SIGINT
[root@00 ~]# # << 此时按Ctrl+c 无任何反映
[root@00 ~]# trap 2 # 信号复原
[root@00 ~]# ^C # 复员后 Ctrl+c
[root@00 ~]# ^C # 复员后 Ctrl+c
|
http://tldp.org/LDP/abs/html/contributed-scripts.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
#!/bin/bash
# 长短选项兼容
# ./scripts.sh -h
# ./scripts.sh -s <values>
# ./scripts.sh --src_dir <values>
# ./scripts.sh -k <values>
# ./scripts.sh --key_prefix <values>
# ./scripts.sh -b <values>
# ./scripts.sh --bucket <values>
# ./scripts.sh -f <values>
# ./scripts.sh --file_type <values>
# ./scripts.sh --skip_fixed_strings <values>
# ./scripts.sh --skip_file_prefixes <values>
# ./scripts.sh --skip_path_prefixes <values>
# ...
while getopts "hs:k:b:f:-:" opt; do
case $opt in
s) SRC_DIR=$OPTARG;;
k) KEY_PREFIX=$OPTARG;;
b) BUCKET=$OPTARG;;
f) FILE_TYPE=$OPTARG;;
h) usage;;
-)
case $OPTARG in
src_dir) SRC_DIR=$2; shift;;
key_prefix) KEY_PREFIX=$2; shift;;
bucket) BUCKET=$2; shift;;
skip_fixed_strings) SKIP_FIXED_STRINGS=$2; shift;;
skip_file_prefixes) SKIP_FILE_PREFIXES=$2; shift;;
skip_path_prefixes) SKIP_PATH_PREFIXES=$2; shift;;
skip_suffixes) SKIP_SUFFIXES=$2; shift;;
file_type) FILE_TYPE=$2; shift;;
ignore_dir) IGNORE_DIR=$2; shift;;
check_exists) CHECK_EXISTS=$2; shift;;
check_hash) CHECK_HASH=$2; shift;;
rescan_local) RESCAN_LOCAL=$2; shift;;
log_level) LOG_LEVEL=$2; shift;;
log_file) LOG_FILE=$2; shift;;
delete_on_success) DELETE_ON_SUCCESS=$2; shift;;
*) echo "Invalid option: --$OPTARG"; exit 1;;
esac;;
:) echo "Option -$OPTARG requires an argument."; exit 1;;
\?) echo "Invalid option: -$OPTARG"; exit 1;;
esac
done
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
# scripts.sh -h
# scripts.sh -a action
# scripts.sh -a action -n step1 -n step2
# scripts.sh -a action -e "var1=value1,var2=value2"
declare -- ACTIONS=""
declare -- STEPS=""
declare -- ENV_VARS=""
while getopts "ha:n:e:" opt; do
case $opt in
h)
usage
exit 0
;;
a)
ACTIONS=$OPTARG
;;
n)
STEPS+=$OPTARG" "
;;
e)
ENV_VARS=$OPTARG
;;
:)
echo "Option -$OPTARG requires an argument." >&2
usage
exit 1
;;
\?)
echo "Invalid option: -$OPTARG" >&2
usage
exit 1
;;
esac
done
shift $((OPTIND - 1))
# 将传入的key=value参数转换成环境变量
# 例如: --env key=value
IFS=',' read -ra ENV_ARR <<<"$ENV_VARS"
for var in "${ENV_ARR[@]}"; do
IFS='=' read -ra VAR_ARR <<<"$var"
declare -g "${VAR_ARR[0]}=${VAR_ARR[1]}"
done
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
# 双冒号 双冒号短参数必须贴近或无参数,长参数必须等号赋值(长参数名可以不用写完)或无参数(注:无参数时变量偏移也是2位)
# 单冒号参数可以贴近也可以不贴近,但参数必选
# f d a 必须接受参数
# s 参数可选
#
ARGS=`getopt -o f:s::d:a: --long filename:,source::,desc:,action:: -- "$@"`
eval set -- "$ARGS"
while true ; do
case "$1" in
-f|--filename)
fileName=$2 ; shift 2 ;;
-s|--source)
case "$2" in
"") sourceDir='.' ; shift 2 ;;
*) sourceDir=$2 ; shift 2 ;;
esac ;;
-d|--desc)
descDir=$2 ; shift 2;;
-a|--action)
case "$2" in
"copy"|"move") action=$2 ; shift 2 ;;
*) action="copy" ; shift 2 ;;
esac ;;
--) shift ; break ;;
*) echo "Internal error!" ; exit 1 ;;
esac
done
|
- shellscript 中转义 !
1
2
3
|
#!/bin/bash
echo -e "\0041"
echo -e "#\0041/bin/bash"
|
1
2
3
4
5
|
# 一般放在 export PATH 之后
if [ "$(id -u)" -eq 0 ]; then
echo "Switching to www user..."
exec runuser -u www -- "$0" "$@"
fi
|