2023.03.14 辛苦排版了一会想起来之前有大佬早就整理过了,链接供参考.以下为原文
先简单写一点,以后在整理
记性不好,看完就忘…
sh bash脚本编程调试
当我们写c或其他程序时,都有调试这一技能,调试可以说是很重要的了…能节约你的时间,shell脚本也有调试…
两种方式–执行时添加参数 脚本里添加参数
参数 | 含义 |
---|---|
-n | 不执行脚本,仅检查是否有语法错误 |
-v | 执行脚本前,把脚本内容输出到屏幕 |
-x | 将执行时的内容输出到屏幕 |
-e | 执行出现错误时退出 |
- 执行时添加
假如现在要执行的脚本叫做test.sh,那么
bash -n test.sh
可以检查脚本语法是否有错,无错误没有任何输出,echo $?可以得0.同理,其他的参数也一样.
另外这些参数可以组合使用–但是,当加入n后,脚本就不会执行了,所以-nx
的x
将不会起作用…
- 脚本里添加
脚本第一行一般都是
#!/bin/bash
...#脚本内容
...
这种指定哪个解释器执行脚本的语句,这样,参数可以跟在后面:
#!/bin/bash -n
...#脚本内容
...
这样就可以检查语法错误了
另外还可以针对某一段执行,如:
#!/bin/bash
...#段1
...
set -n#开始检查语法
...#段2
...
set +n#停止检查
这样就可以只针对段2检查语法错误,-n
开始,+n
停止,另外的x
,v
也是一样
需要注意的是,若采用#!/bin/bash -e
这种形式,那么如果执行脚本的时候使用的不是指定的shell,比如采用sh test.sh
,那么设置的-e参数不起作用,但是set -e可以生效。
环境变量
直接问chatgpt了
Q:shell的环境变量,和普通变量有什么不同?如果定义了一个变量,与运行的程序一同执行或者不一同执行有什么区别?
GPT-3 A:shell的环境变量是用来定义系统的运行环境的,它们可以被系统和所有的子程序共享,而普通变量只能在当前shell中使用。
如果定义了一个变量,与运行的程序一同执行时,这个变量可以在程序中使用;如果不一同执行时,程序中就不能使用这个变量。
GPT-4 A:
根据网上的搜索结果,shell变量和环境变量的区别是:
shell变量只在当前的shell中有效,用于设置短期的工作条件。环境变量对所有从shell启动的进程都有效,用于传递信息。
shell变量可以用export命令转换为环境变量。环境变量可以用unset命令取消。
如果定义了一个变量,与运行的程序一同执行或者不一同执行有什么区别?这取决于你是否将该变量导出为环境变量。如果是,那么该变量就可以被程序访问;如果不是,那么该变量就只能在当前的shell中使用。
了解详细信息:
1. stackoverflow.com
2. digitalocean.com
3. askubuntu.com
看来还是gpt-4好用,哈哈
我会关心这个问题是之前交叉编译的时候,指定CC,但是不起作用,故有此一问。
运算符
今天看脚本把&& ||和c里面的逻辑运算符混了…
符号 | 意义 |
---|---|
cmd1&&cmd2 | cmd1执行成功(返回0),cmd2才执行 |
cmd1||cmd2 | cmd1执行成功(返回0),cmd2不执行 |
这里说的返回0,是一个环境变量$?
,总之不能跟逻辑与或混了…
bash支持的运算符如下,假定变量 a 为 10,变量 b 为 20::
- 数学运算符
运算符 | 说明 | 举例 |
---|---|---|
+ | 加法 | `expr $a + $b` 结果为 30。 |
- | 减法 | `expr $a - $b` 结果为 -10。 |
* | 乘法 | `expr $a * $b` 结果为 200。 |
/ | 除法 | `expr $b / $a` 结果为 2。 |
% | 取余 | `expr $b % $a` 结果为 0。 |
= | 赋值 | a=$b 把变量 b 的值赋给 a。 |
== | 相等 | 用于比较两个数字,相同则返回 true。 [ $a == $b ] 返回 false。 |
!= | 不相等 | 用于比较两个数字,不相同则返回 true。 [ $a != $b ] 返回 true。 |
- 关系运算符
运算符 | 说明 | 举例 |
---|---|---|
-eq | 检测两个数是否相等,相等返回 true。 | [ $a -eq $b ] 返回 false。 |
-ne | 检测两个数是否不相等,不相等返回 true。 | [ $a -ne $b ] 返回 true。 |
-gt | 检测左边的数是否大于右边的,如果是,则返回 true。 | [ $a -gt $b ] 返回 false。 |
-lt | 检测左边的数是否小于右边的,如果是,则返回 true。 | [ $a -lt $b ] 返回 true。 |
-ge | 检测左边的数是否大于等于右边的,如果是,则返回 true。 | [ $a -ge $b ] 返回 false。 |
-le | 检测左边的数是否小于等于右边的,如果是,则返回 true。 | [ $a -le $b ] 返回 true。 |
- 布尔运算符
运算符 | 说明 | 举例 |
---|---|---|
! | 非运算,表达式为 true 则返回 false,否则返回 true。 | [ ! false ] 返回 true。 |
-o | 或运算,有一个表达式为 true 则返回 true。 | [ $a -lt 20 -o $b -gt 100 ] 返回 true。 |
-a | 与运算,两个表达式都为 true 才返回 true。 | [ $a -lt 20 -a $b -gt 100 ] 返回 false。 |
- 逻辑运算符
运算符 | 说明 | 举例 |
---|---|---|
&& | 逻辑的 AND | [[ $a -lt 100 && $b -gt 100 ]] 返回 false |
|| | 逻辑的 OR | [[ $a -lt 100 || $b -gt 100 ]] 返回 true |
- 字符串运算符
运算符 | 说明 | 举例 |
---|---|---|
= | 检测两个字符串是否相等,相等返回 true。 | [ $a = $b ] 返回 false。 |
!= | 检测两个字符串是否不相等,不相等返回 true。 | [ $a != $b ] 返回 true。 |
-z | 检测字符串长度是否为0,为0返回 true。 | [ -z $a ] 返回 false。 |
-n | 检测字符串长度是否不为 0,不为 0 返回 true。 | [ -n “$a” ] 返回 true。 |
$ | 检测字符串是否不为空,不为空返回 true。 | [ $a ] 返回 true。 |
- 文件测试运算符
选项 | 意义 |
---|---|
[ -a FILE ] | 如果 FILE 存在则为真。 |
[ -b FILE ] | 如果 FILE 存在且是一个块特殊文件则为真。 |
[ -c FILE ] | 如果 FILE 存在且是一个字特殊文件则为真。 |
[ -d FILE ] | 如果 FILE 存在且是一个目录则为真。 |
[ -e FILE ] | 如果 FILE 存在则为真。 |
[ -f FILE ] | 如果 FILE 存在且是一个普通文件则为真。 |
[ -g FILE ] | 如果 FILE 存在且已经设置了SGID则为真。 |
[ -h FILE ] | 如果 FILE 存在且是一个符号连接则为真。 |
[ -k FILE ] | 如果 FILE 存在且已经设置了粘制位则为真。 |
[ -p FILE ] | 如果 FILE 存在且是一个名字管道(F如果O)则为真。 |
[ -r FILE ] | 如果 FILE 存在且是可读的则为真。 |
[ -s FILE ] | 如果 FILE 存在且大小不为o则为真。 |
[ -t FD ] | 如果文件描述符 FD 打开且指向一个终端则为真。 |
[ -u FILE ] | 如果 FILE 存在且设置了SUID (set user ID)则为真。 |
[ -w FILE ] | 如果 FILE 如果 FILE 存在且是可写的则为真。 |
[ -x FILE ] | 如果 FILE 存在且是可执行的则为真。 |
[ -O FILE ] | 如果 FILE 存在且属有效用户ID则为真。 |
[ -G FILE ] | 如果 FILE 存在且属有效用户组则为真。 |
[ -L FILE ] | 如果 FILE 存在且是一个符号连接则为真。 |
[ -N FILE ] | 如果 FILE 存在 且已被修改如果ied since it was last read则为真。 |
[ -S FILE ] | 如果 FILE 存在且是一个套接字则为真。 |
[ FILE1 -nt FILE2 ] | 如果 FILE1比 FILE2 要新 , 或者 FILE1 存在而 FILE2 不存在则为真。 |
[ FILE1 -ot FILE2 ] | 如果 FILE1 比 FILE2 要老, 或者 FILE2 存在且 FILE1 不存在则为真。 |
[ FILE1 -ef FILE2 ] | 如果 FILE1 和 FILE2 指向相同的设备和节点号则为真。 |
[ -o OPTIONNAME ] | 如果 shell选项 “OPTIONNAME” 开启则为真。 |
变量
命令 | 意义 |
---|---|
read | 从键盘读取变量值 |
array | 数组 |
declare | 声明变量类型 |
echo $var | 查看变量内容 |
unset $var | 取消设定变量 |
set | 查看所有变量 |
export var | 将自定义变量变为环境变量,也就是子程序可以调用此变量了 注1 |
注1
:source或.执行脚本即当前shell执行,不是在子程序里执行,可以做到立即生效的作用
比较有用的几个环境变量:
变量 | 意义 |
---|---|
$HOME | 代表用户的家目录 |
$SHELL | 目前正在使用的shell |
$PATH | 可执行文件搜索路径 |
$LANG | 语言变量 |
$RANDOM | 随机数变量 |
$PS1 | 提示字符的设定 |
$$ | 这个程式的PID(脚本运行的当前进程ID号) |
$0 | 这个程式的执行名字 |
$n | 这个程式的第n个参数值,n=1…9 |
$* | 这个程式的所有参数,此选项参数可超过9个。 |
$# | 这个程式的参数个数 |
$- | 显示shell使用的当前选项,与set命令功能相同 |
$@ | 跟$*类似,但是可以当作数组用 |
$! | 执行上一个背景指令的PID(后台运行的最后一个进程的进程ID号) |
$? | 执行上一个指令的返回值 (显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误) |
$OSTYPE, $HOSTTYPE, $MACHTYPE | 操作系统、主机、机器类型 |
${} $() $的区别
$()和反引号``的作用结果相同都是用来作命令替换的,通俗来讲就是把命令执行后的结果作为参数返回。
${}和$的作用是变量替换, 与 {}与 与效果相同,${}中的{}相当于一个界线符,可以很好的划分界限以免混淆
$(())整数运算
变量操作
形式 | 意义 |
---|---|
${vat#str} | 从变量首开始删除,找最短符合删除 |
${vat##str} | 从变量首开始删除,找最长符合删除 |
${vat%str} | 从变量尾开始删除,找最短符合删除 |
${vat%%str} | 从变量尾开始删除,找最长符合删除 |
${var/oldstr/newstr} | 找到的第一个被替换 |
${var//oldstr/newstr} | 找到的全部被替换 |
${file: m:n} | 提取从m处开始的n个字符,首字符为0 |
var=${str-expr} | 变量未定义赋值,否则将str赋给var |
var=${str:-expr} | 变量未定义或为空赋值,否则将str赋给var |
var=${str+expr} | 变量若定义则赋值,否则将str赋给var |
var=${str:+expr} | 变量非空赋值,否则将str赋给var |
var=${str=expr} | 若变量str未定义,设定变量str,将值赋给str和var,否则将str赋给var |
var=${str:=expr} | 变量未定义或为空,将值赋给str和var,否则将str赋给var |
var=${str?expr} | 变量不存在报错,否则将str赋给var |
var=${str:?expr} | 变量不存在或为空报错,否则将str赋给var |
记忆方法:
#是去掉左边 (在键盘上#在${}的左边)
%是去掉右边(在键盘上%在${}的右边)
单一符号是最小匹配﹔两个符号是最大匹配。
一些有用的管道命令
命令 | 意义 |
---|---|
cut | 使用某种格式从文件中截取每一行中的某一字段 |
grep | 利用正则表达式分析一行数据 |
sort | 根据制定格式对行排序 |
uniq | 去重 |
wc | 统计相关字、行、字符数 |
tee | 双向重导向,可以重定向输出到文件的同时依旧输出到屏幕 |
tr | 文字替换 |
col | tab替换为空格 |
join | 归并两个文件中相同的部分 |
paste | 将两个文件贴在一起,以tab分隔 |
expand | tab转空格 |
split | 分隔文件 |
xargs | 参数代换 |
sed | 流编辑,可直接编辑文件;添加多行时要以\ 分隔;替换: sed 's/要被取代的字符串/新的字符串/g' 多用于以行为单位的处理 |
printf | 格式化打印 |
awk | 字段处理 |
diff | 比较文件差异 |
cmp | 对比文件(以字节为单位) |
patch | 根据diff修订文件 |
pr | 显示时加入标题页码 |
注
:
- awk 的指令间隔:所有 awk 的动作,亦即在 {} 内的动作,如果有需要多个指令辅助时,可利用分号;间隔, 或者直接以 [Enter]
按键来隔开每个指令 - 逻辑运算当中,如果出现是否等于的情况,则务必使用两个等号==
- 格式化输出时,在 printf 的格式设定当中,务必加上 \n ,才能进行分行!
- 与 bash shell 的变量不同,在 awk 当中,变量可以直接使用,不需加上 $ 符号。
脚本编写
test命令 与判断符号 [ ]
注意:判断符号里面都要有两个空格分隔如[空格
-e filename空格
]
判断符有以下特点:
- 在中括号 [] 内的每个组件都需要有空格键来分隔;
- 在中括号内的变量,最好都以双引号括号起来;
- 在中括号内的常量,最好都以单或双引号括号起来;
条件判断
原型:
if[ expr1];then
...
elseif[expr2];then
...
elseif[exprn];then
...
else
...
fi
原型
case $var in
变量内容1)
...
;;
变量内容2)
...
;;
*)
...
;;
easc
最后的*)
相当于switch里的default
例子:
case "$foo" in ""|"$DMROOT."|"$DMROOT.DeviceInfo."*)
...
;;
如上case语句表示若$foo变量的值为空,或者为$DMROOT.或者为$DMROOT.DeviceInfo.xxx(xxx为任意字符),则可以匹配。
循环
原型:
while [ condition ]
do
程序段落
done
until [ condition ]
do
程序段落
done
for var in con1 con2 con3 ...
do
程序段
done
#作为循环选取,可使用`$(seq 1 100)`或bash内建的{1..100}来完成1
#到100序列,字母也可以如{a..g}
for (( 初始值; 限制值; 执行步阶 ))
do
程序段
done
分号
在Linux bash shell中,语句中的分号一般用作代码块标识
比如
if [ -f requirements.txt ]; then echo ok; fi
和
if [ -f requirements.txt ]
then echo ok;
fi
效果是一样的。如果你在shell里面按行依次输入命令,然后查看命令历史,看到的也是一行的以分号分割的命令。