shell特性([ ]判断、快捷键)
1、shell判断符[ ]
(()) () [] [[]] {}、字符截取(切片) - 凡人半睁眼 - 博客园 (cnblogs.com)
2、关系运算符只支持整数的比较,不支持字符串,除非字符串是变量并且值为整数!
[ ] 中使用的比较符 | [[ ]] 中使用的比较符 | (( )) 中使用的比较符 | 作用 |
---|---|---|---|
-eq 或 == | -eq 或 == | == | 等于 |
-ne 或 != | -ne 或 != | != | 不等于 |
-gt 或 \> | -gt 或 > | > | 大于 |
-lt 或 \< | -lt 或 < | < | 小于 |
-ge | -ge | >= | 大于等于 |
-le | -le | <= | 小于等于 |
还有:在 [ ] 中 使用 > 和 < 得用转义字符!而 [[ ]] 、(( )) 中不需要!
逻辑运算符:
[ ]中逻辑运算符 | [[ ]] 中逻辑运算符 | (( )) 中逻辑运算符 | 作用 |
---|---|---|---|
-a | && | & 或 && | 两边的条件都成立,结果才为 true(与) |
-o | || | | 或 || | 一边条件成立则为 true(或) |
! | ! | 无 | 条件为 true ,返回为 flase(非) |
3、Shell脚本中的set指令,比如set -x 和 set -e,set -u
set -x
打开脚本调试功能,也可以通过bash -x ***.sh打开
如果输出结果以双加号(++)开头则表示命令在子Shell中执行
set -n
检查shell语法,但不会执行,等同于bash -n xxx.sh
set -e
每个脚本都应该在文件开头加上set -e,这句语句告诉bash如果任何语句的执行结果不是true则应该退出。这样的好处是防止错误像滚雪球般变大导致一个致命的错
误,而这些错误本应该在之前就被处理掉。如果要增加可读性,可以使用set -o errexit,它的作用与set -e相同。
在"set -e"之后出现的代码,一旦出现了返回值非零,整个脚本就会立即退出。
set -o pipefail
设置了这个选项以后,包含管道命令的语句的返回值,会变成最后一个返回非零的管道命令的返回值
#!/bin/bash set -o pipefail ls a.txt | echo "hi" >/dev/null [[ $? -ne 0 ]] && exit 1 echo "test"
结果
set -o pipefail是去捕获最后一个返回非零的管道命令的返回值,也就是ls a.txt的返回值;
没有a.txt文件,返回值为非0,因此[[ $? -ne 0 ]]条件成立,脚本退出,不执行echo “test”
set +h
hashall
可以让Bash记录执行过的命令路径,并保存到一个内存的Hash表
中,当再次执行相同命令时就不需要再通过PATH变量查找该命令的路径,这样可以提高效率。
但有时,程序的路径发生了变化,因为有Hash记录的存在反而会导致命令执行失败
set -u
设置该选项后,当脚本在执行过程中尝试使用未定义过的变量时,报错并退出运行整个脚本(默认会把该变量的值当作空来处理)
set指令能设置所使用shell的执行方式,可依照不同的需求来做设置。
-a 标示已修改的变量,以供输出至环境变量。 -b 使被中止的后台程序立刻回报执行状态。 -C 转向所产生的文件无法覆盖已存在的文件。 -d Shell预设会用杂凑表记忆使用过的指令,以加速指令的执行。使用-d参数可取消。 -e 若指令传回值不等于0,则立即退出shell。 -f 取消使用通配符。 -h 自动记录函数的所在位置。 -H Shell 可利用"!"加<指令编号>的方式来执行history中记录的指令。 -k 指令所给的参数都会被视为此指令的环境变量。 -l 记录for循环的变量名称。 -m 使用监视模式。 -n 只读取指令,而不实际执行。 -p 启动优先顺序模式。 -P 启动-P参数后,执行指令时,会以实际的文件或目录来取代符号连接。 -t 执行完随后的指令,即退出shell。 -u 当执行时使用到未定义过的变量,则显示错误信息。 -v 显示shell所读取的输入值。 -x 执行指令后,会先显示该指令及所下的参数。 +<参数> 取消某个set曾启动的参数
4、bash 内置命令exec
exec表示执行一个命令,但是不会启动子shell,而只是替代当前shell并且所有环境都会被清空。
注:source 和 . 不启用新的shell,在当前shell中执行,设定的局部变量在执行完命令后仍然有效;
bash或sh 或shell script执行时,另起一个子shell,其继承父shell的环境变量,其子shelll的变量执行完后不影响父shell,注意三类的区别
exec是用被执行的命令行替换掉当前的shell进程,且exec命令后的其他命令将不再执行。例如在当前shell中执行 exec ls 表示执行ls这条命令来替换当前的shell 即为执行完后会退出当前shell。
为了避免这个结果的影响,一般将exec命令放到一个shell脚本中,用主脚本调用这个脚本,调用处可以用bash xx.sh(xx.sh为存放exec命令的脚本)。这样会为xx.sh
建立一个子shell去执行,当执行exec后该子脚本进程就被替换成相应的exec的命令
其中有一个例外:当exec命令对文件描述符操作的时候,就不会替换shell,而是操作完成后还会继续执行后面的命令
exec 3<&0 表示将操作符3也指向标准输入
exec ls 在shell中执行ls,ls结束后不返回原来的shell中了 exec <file 将file中的内容作为exec的标准输入 exec >file 将file中的内容作为标准写出 exec 3<file 将file读入到fd3中 sort <&3 fd3中读入的内容被分类 exec 4>file 将写入fd4中的内容写入file中 ls >&4 Ls将不会有显示,直接写入fd4中了,即上面的file中 exec 5<&4 创建fd4的拷贝fd5 exec 3<&- 关闭fd3
5、Linux中万物皆文件,文件描述符也是文件。默认:
fd=0的标准输入是/dev/stdin文件
fd=1的标准输出是/dev/stdout文件
fd=2的标准错误是/dev/stderr文件
2>&1:将标准错误重定向到标准输出(常用)
特殊重定向:
&>file:这是特殊的重定向方式,表示将标准错误和标准输出都重定向到file文件中,等价于>file 2>&1
&>>file:这是特殊的重定向方式,表示将标准错误和标准输出都追加到file文件中,等价于>>file 2>&1
6、快速清空文本
: > access.log > access.log true > access.log cat /dev/null > access.log echo -n "" > access.log echo > access.log truncate -s 0 access.log
7、dd运用
dd快速生成大文件
$ dd if=/dev/zero of=file.txt bs=1M count=1024
擦除硬盘数据
$ dd if=/dev/urandom of=/dev/sda
制作系统盘(sdb
可以 U 盘,也可以是普通硬盘)
$ dd if=ubuntu-server-amd64.iso of=/dev/sdb
dd将小写字母全部转换成大写
$ dd if=testfile_2 of=testfile_1 conv=ucase
8、查看某个进程的运行时间(etime运行时间、rss内存)
ps -p 1858 -o etimes,etime ELAPSED ELAPSED 22382 06:13:02
9、计算程序运行时间(time命令)
$ time ./test real 0m1.003s user 0m0.000s sys 0m0.000s
real
:表示的钟表时间,也就是从程序执行到结束花费的时间;user
:表示运行期间,cpu 在用户空间所消耗的时间;sys
:表示运行期间,cpu 在内核空间所消耗的时间;
由于 user
和 sys
只统计 cpu 消耗的时间,程序运行期间会调用 sleep 发生阻塞,也可能会等待网络或磁盘 IO,都会消耗大量时间。因此对于类似情况,real
的值就会大于其它两项之和。另外,也会遇到 real
远远小于 user
+ sys
的场景,如果程序在多个 cpu 上并行,那么 user
和 sys
统计时间是多个 cpu 时间,实际消耗时间 real
很可能就比其它两个之和要小了。
10、执行历史命令(实用)
!!
:重复执行上条命令;!N
:重复执行 history 历史中第 N 条命令,N 可以通过 history 查看;!pw
:重复执行最近一次,以pw
开头的历史命令,这个非常有用,小编使用非常高频;!$
:表示最近一次命令的最后一个参数;
$ vim /root/sniffer/src/main.c $ mv !$ !$.bak # 相当于 $ mv /root/sniffer/src/main.c /root/sniffer/src/main.c.bak11、Vim 保存一个没有权限的已编辑文件
在保存文件前更改文件权限。
命令是:
:w !sudo tee %
这个命令将会要你输入密码,就像在命令行中使用 sudo vim一样。
11、在命令 history 中显示时间戳
临时设置:
export HISTTIMEFORMAT="%F %T `whoami` "
永久设置:
echo 'export HISTTIMEFORMAT="%F %T `whoami` "' >> /etc/profile
12、间接引用变量
VAR1="2323232"
VAR2="VAR1"
变量VAR1和变量VAR2,这个VAR2的值是VAR1的名字,那么我们现在想通过VAR2来获取VAR1的值
$ echo ${!VAR2} 2323232 $ eval echo \$$VAR2 2323232
eval会对后面的cmdLine进行两遍扫描,如果在第一遍扫面后cmdLine是一个普通命令,则执行此命令;如果cmdLine中含有变量的间接引用
利用eval实现将结果分别赋值给变量
#cat tt.sh for i in $(echo "4 5 6"); do eval a$i=$i done echo $a4 $a5 $a6 #cat tt1.sh num=0 for i in $(eval echo $*);do #eval将{1,2}分解为1 2 let num+=1 eval node${num}="$i" done echo $node1 $node2 $node3 # bash a.sh 192.168.1.1{1,2} 192.168.1.11 192.168.1.12
13、shell脚本中“if”语法如何嵌套
if [ 条件 ] then 命令1 命令2 ….. else if [ 条件 ] then 命令1 命令2 …. else 命令1 命令2 ….. fi fi
14、数字运算
expr 1 + 2909 2910 test=$[16 + 4] $ echo $test 20 $let 5+2 7 ###小数计算 echo "scale=2; 2.03+2.04" | bc
注:expr做数字的四则运算
-
请用空格隔开每个项。
-
请将反斜杠(\)放在 Shell 特殊字符前面。
-
请对包含空格和其他特殊字符的字符串用引号括起来。
15、shell高级变量
${string/substring/replacement} 仅仅替换第一次匹配 ${string//substring/replacement} 替换所有的匹配
范例:
zjz="123,124,zhz,zjksd" #3设定字符串 str=${zjz//,/ } ##替换字符串中的逗号为空格 echo $str ##输出效果 123 124 zhz zjksd
脚本运用:字符串的替换
#cat tt.sh #!/bin/bash str="1,2,3,4"; str=${str//,/ }; arr=($str);
#遍历数组 for each in ${arr[*]} do echo $each done
16、shell特性之快捷键
ctrl + w ## 从光标处删至该单词结束 ctrl + y ## 撤销删除 ctrl + k ## 从光标处删除至行尾 ctrl + u ## 从光标处删除至行首
17、进制转换(2#表示:将1010二进制数转换为10进制数)
echo $(( 2#1010 ))
18、shell中${} 的用法,删除&替换
字符串的删除
echo ${i##*/} 删除 / 前的所有内容
tt=$i echo{tt:22} #取的22位以后的所有字符
实例
for i in \ goharbor/harbor-core:v2.5.0 goharbor/harbor-jobservice:v2.5.0 goharbor/notary-signer-photon:v2.5.0 goharbor/harbor-db:v2.5.0 goharbor/harbor-registryctl:v2.5.0 goharbor/redis-photon:v2.5.0 goharbor/harbor-portal:v2.5.0 goharbor/trivy-adapter-photon:v2.5.0 goharbor/nginx-photon:v2.5.0 goharbor/registry-photon:v2.5.0 goharbor/notary-server-photon:v2.5.0 goharbor/chartmuseum-photon:v2.5.0 goharbor/prepare:v2.5.0 goharbor/harbor-log:v2.5.0 goharbor/harbor-exporter:v2.5.0 ; \ do IMAGE=$(echo ${i##goharbor/} |cut -d: -f1); docker pull mikrobithu/$IMAGE:dev-arm; docker tag mikrobithu/$IMAGE:dev-arm goharbor/$IMAGE:v.2.5.0 ; docker tag mikrobithu/$IMAGE:dev-arm goharbor/$IMAGE:v.dev-arm ; done
file=/dir1/dir2/dir3/my.file.txt
${file#*/}:删掉第一个/ 及其左边的字符串:dir1/dir2/dir3/my.file.txt ${file##*/}:删掉最后一个/ 及其左边的字符串:my.file.txt ${file#*.}:删掉第一个. 及其左边的字符串:file.txt ${file##*.}:删掉最后一个. 及其左边的字符串:txt ${file%/*}:删掉最后一个 / 及其右边的字符串:/dir1/dir2/dir3 ${file%%/*}:删掉第一个/ 及其右边的字符串:(空值) ${file%.*}:删掉最后一个 . 及其右边的字符串:/dir1/dir2/dir3/my.file ${file%%.*}:删掉第一个 . 及其右边的字符串:/dir1/dir2/dir3/my 记忆的方法为: # 是 去掉左边(键盘上#在 $ 的左边) %是去掉右边(键盘上% 在$ 的右边) 单一符号是最小匹配;两个符号是最大匹配 取子串及替换 ${file:0:5}:提取最左边的5 个字节:/dir1 ${file:5:5}:提取第5 个字节右边的连续5个字节:/dir2 也可以对变量值里的字符串作替换: ${file/dir/path}:将第一个dir 替换为path:/path1/dir2/dir3/my.file.txt ${file//dir/path}:将全部dir 替换为path:/path1/path2/path3/my.file.txt ${#var} 可计算出变量值的长度: ${#file} 可得到27 ,因为/dir1/dir2/dir3/my.file.txt 是27个字节
echo ${PATH#*:} # 参数替换
${file#*/}: 拿掉第一条/及其左边的字符串:dir1/dir2/dir3/my.file.txt ${file##*/}: 拿掉最后一条/及其左边的字符串:my.file.txt ${file#*.}: 拿掉第一个.及其左边的字符串:file.txt ${file##*.}: 拿掉最后一个.及其左边的字符串:txt ${file%/*}: 拿掉最后条/及其右边的字符串:/dir1/dir2/dir3 ${file%%/*}: 拿掉第一条/及其右边的字符串:(空值) ${file%.*}: 拿掉最后一个.及其右边的字符串:/dir1/dir2/dir3/my.file ${file%%.*}: 拿掉第一个.及其右边的字符串:/dir1/dir2/dir3/my 记忆的方法为: [list]#是去掉左边, ##最后一个 %是去掉右边, %%第一个
$ echo ${PATH} /usr/local/share/miniconda3/bin/:/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/op_admin/.local/bin:/home/op_admin/bin echo ${PATH#*:} /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/op_admin/.local/bin:/home/op_admin/bin $ echo ${PATH##*:} /home/op_admin/bin
19、一行输入对个命令,使用 ; 隔开
echo hello; echo there; echo zjz
20、逗号链接了一系列的算术操作,虽然里边所有的内容都被运行了,但只有最后一项被返回
let "t2 = ((a = 9, 15 / 3))" #echo $t2 5 #echo $a 9
21、命令组()
在()中的命令列表,将作为一个子 shell 来运行,在()中的变量,由于是在子 shell 中,所以对于脚本剩下的部分是不可用的。
#(a=hello;echo $a) hello #echo $a 9
22、man帮助,常用1、5、8三个帮助级别
23、shell脚本中输出当前的路径的用法
方式1
path=$(cd `dirname $0`;pwd)
这条简单的命令就可以进入当前的路径,并且将路径值存在变量里。
- 首先是使用反引号包起来的dirname $0
命令。使用反引号代表执行其中的命令并返回命令结果。而$0
代表脚本的文件名。这条指令就可以得到脚本的路径。
- 然后使用dirname通过文件名获取路径,因为$0
并不是绝对路径,所以使用dirname得到的也不是绝对路径。这时使用cd进入,然后再使用pwd命令得到当前路径的绝对路径。
方式2
24、shell的通配符和正则表达式
通配符:通配符是一种简单的模式匹配工具,通常用于文件名匹配。通配符可以用来匹配一个或多个字符
在 Bash shell 中,常用的通配符有以下几种:
星号(*):匹配任意数量的字符(包括零个或多个字符)。
问号(?):匹配单个任意字符。
方括号([ ]):匹配方括号内列举的任意一个字符。例如,[abc] 匹配 a、b 或 c 这三个字符中的任意一个。
连字符(-):用于表示范围,例如 [a-z] 表示匹配 a 到 z 之间的任意一个字符。
叹号加方括号([! ]):匹配任何不在方括号中列举的字符。
正则表达
则表达式是一种更强大的模式匹配工具,可以处理复杂的文本模式。它使用一些特殊的字符来表示文本模式,例如,点号(.)表示任何单个字符,星号(*)表示前面的字符可以出现零次或多次,加号(+)表示前面的字符可以出现一次或多次,等等。
正则表达式可以很方便地进行复杂的模式匹配,但是学习曲线比通配符要陡峭一些。
25、变量赋值给变量取值
worker_ip=${!worker_var}
while read -r line; do node_name=$(echo "$line" | awk -F',' '{print $1}') node_ip=$(echo "$line" | awk -F',' '{print $2}') eval "work_node$(printf %02d $ip_counter)=$node_ip" ((ip_counter++)) done < /tmp/list if [ ${node_num} -ge 2 ]; then sed_command="sed -i.bak" # Replace the current node's IP sed_command+=" -e 's/ipaddr01/${curr_node_ip}/g'" # Replace the worker nodes' IPs for ((i=2; i<=node_num; i++)); do worker_var="work_node$(printf "%02d" $((i-1)))" worker_ip=${!worker_var} if [[ -n $worker_ip ]]; then sed_command+=" -e 's/ipaddr$(printf "%02d" $i)/${worker_ip}/g'" fi done # Apply the replacements sed_command+=" /etc/ansible/hosts" # Use sudo to execute sed command eval sudo $sed_command else echo "Error: node_num must be 2 or greater." exit 1 fi
26、(( )) 支持变量的直接加减运算
node_num=2 $((node_num + 1))
https://www.junmajinlong.com/shell/script_course/shell_tutorial/ 骏马金龙
https://www.cnblogs.com/f-ck-need-u/p/7427357.html shell测试命令test、[ ]、[[ ]]