前言
这是我学习shell脚本期间的笔记,其中使用大量自然语言描述,并用各种官方文档,社区文档去佐证我学到的错误理论,如有冒犯先行致歉。
关于生成式AI内容的解释:文中没有直接由生成式AI创作的内容,我所使用的生成式AI只帮助我去调查官方文档佐证内容并补充知识体系,其中有部分内容是生成式AI调查后由我进行整理然后使用自然语言重新描述出来的,我不确定这属不属于生成式AI内容,欢迎各位佬友举报纠错。
因为我的逻辑混乱可能笔记也比较乱,先跟各位佬友道个歉。
还有就是,这个右侧栏目录到底怎么搞啊!!!
以下是笔记正文
shell不仅仅是命令解释器,也是一个脚本语言
shell脚本是一个文件
指定脚本解释器 #!/bin/bash,表示命令交给bash shell执行
注释用来描述程序代码的作用,不会被脚本执行
单行注释使用 # 多行注释使用 <<EOF EOF,后期补充明确,Bash原生并不支持多行注释,此写法是Bash的Here Document模拟出来的多行注释,这更像常见技巧,而不是标准的注释语法
<<EOF
我是注释
我是第二行注释
EOF
后期发现,该多行注释的方法能作为注释工作,但Bash官方将其定义为Here Document,并不是专用的注释语法,如果 Here Document 的定界词未被引用,例如 <<EOF,其中的内容会进行参数展开、命令替换和算术展开
GNU Bash官方文档 3.1.3 Comments:Comments (Bash Reference Manual)
其中写明 a word beginning with ‘#’ introduces a comment. 译为 使用 # 引入注释
GNU Bash官方文档3.6.6 Here Documents:Redirections (Bash Reference Manual)
其中写明 all lines of the here-document are subjected to parameter expansion, command substitution, and arithmetic expansion 译为 here-document 中的所有行都将进行参数扩展、命令替换和算术扩展
命令替换实例展示
$(date)
`date`
更明确更安全的写法如下
: <<'EOF'
这里作为伪多行注释
$USER 和 $(date) 都不会展开
EOF
Bash 原生使用 # 进行单行注释;脚本中常用 Here Document 模拟多行注释。推荐使用 : <<'EOF',避免所写的内容发生变量展开和命令替换。
编写shell脚本的俗成规矩
写命令解释器
注释、描述信息、作者、版权等
代码要有层次性
以sh后缀名结尾
少用cat,多用grep,能一次写完就不要多次
Shell 脚本自动生成注释头
此方法来自誉天教育的孙老师(全名我确实不知道,所以只能这样写了)
autocmd BufNewFile *.py,*.cc,*.sh,*.java exec ":call SetTitle()"
func SetTitle()
if expand("%:e") == 'sh'
call setline(1, "#!/bin/bash")
call setline(2, "##################################################")
call setline(3, "# File Name: ".expand("%"))
call setline(4, "# Version: V1.0")
call setline(5, "# Author: xxx")
call setline(6, "# Email: [email protected]")
call setline(7, "# Created Time: ".strftime("%F %T"))
call setline(8, "# Description:")
call setline(9, "##################################################")
call setline(10, "")
endif
endfunc
将以上代码写入 ~/.vimrc,使用 Vim 新建 .sh 文件时会自动生成脚本注释头。
在Linux中的文件后缀仅用于个人判断,对于系统来说无实际意义
执行脚本的方式
相对路径执行 ./test.sh 需要x执行权限
绝对路径执行 /opt/test.sh 需要x执行权限
bash shell执行 bash test.sh bash不看执行权限,但依旧要有读权限
source 执行 source test.sh或 . test.sh 不看执行权限,但依旧要有读权限
bash执行脚本时会创建一个子shell执行,使用脚本定义变量无法使用
source在当前shell执行脚本,定义的变量可以直接使用
位置化参数(位置变量)
在执行shell脚本时,可以在终端传入变量给脚本(在脚本后面接参数
$1 $2 $3 …
10及以上使用 ${10},这个参数是在脚本后接入的参数的第几个
使用 $0 可以获取第0个位置参数,也就是文件位置
课程里教了一种用后自焚的写法,rm -rf $0,但我研究了一下,实际操作下来可能因为启动命令的差异导致用后自焚的无效
./test.sh # $0 = ./test.sh
bash test.sh # $0 = test.sh
bash /tmp/test.sh # $0 = /tmp/test.sh
source test.sh # $0 不是 test.sh,可能是 bash 或父脚本
bash < test.sh # $0 通常是 bash,也无法定位原脚本
综上,除非能限定用户的启动方式,不然是无法做到100%用后即焚的效果的
如果确需使用此方法,建议写成 rm -f -- "$0" ,此方法可以在减少递归误删的可能时,确保文件名中有空格时依旧可以做到删除
$$代表当前执行脚本的PID,使用 echo $$ 可以在shell脚本运行时获取到当前脚本运行的进程PID
同样拥有扩展写法,使用 ps -aux | grep $$ > /tmp/shell_log将运行时的占用写入此文件,但我能想到的问题就是只能写入一次,或使用时间戳和追加+循环的方式持续写入,以做到脚本运行结束后debug查看占用的能力
我和chatgpt研究了一下,这种模型是可行的,且可以进行持续写入
#!/bin/bash
script_pid=$$ # 保存当前脚本的PID
log="/tmp/shell_log.$script_pid" # 使用PID区分不同脚本的日志
(
# 脚本进程存在时,每秒记录一次资源占用
while kill -0 "$script_pid" 2>/dev/null; do
echo "===== $(date '+%F %T') =====" # 写入采样时间
# 记录PID、CPU、内存、运行时间和命令等信息
ps -p "$script_pid" \
-o pid,ppid,%cpu,%mem,rss,vsz,etime,cmd
sleep 1 # 每隔1秒采样一次
done
) >> "$log" & # 后台运行并追加到日志
monitor_pid=$! # 获取监控进程PID
# 脚本结束时关闭后台监控进程
trap 'kill "$monitor_pid" 2>/dev/null' EXIT
# 此处以后编写脚本的主体任务
sleep 10
注意:ps 显示的 %CPU 是进程启动以来的平均值,不是这一秒的瞬时占用。
$@表示所有的参数(我感觉这个东西很鸡肋),每个参数都是一个个体
$*表示所有的参数,用双引号引起表示是个整体,是一个字符串
这里很绕,我直接用一个演示解释这个地方
root@SH-Debian-Tencent:~# cat test.sh
#!/bin/bash
touch "$@"
root@SH-Debian-Tencent:~# bash test.sh 1 2 3 4
root@SH-Debian-Tencent:~# ls -l
total 8
-rw-r--r-- 1 root root 0 Jul 15 16:49 1
-rw-r--r-- 1 root root 0 Jul 15 16:49 2
-rw-r--r-- 1 root root 0 Jul 15 16:49 3
-rw-r--r-- 1 root root 0 Jul 15 16:49 4
-rw-r--r-- 1 root root 1478 Jul 4 16:51 passwd
-rw-r--r-- 1 root root 22 Jul 15 16:49 test.sh
root@SH-Debian-Tencent:~# cat test.sh
#!/bin/bash
touch "$*"
root@SH-Debian-Tencent:~# bash test.sh 1 2 3 4
root@SH-Debian-Tencent:~# ls -l
total 8
-rw-r--r-- 1 root root 0 Jul 15 16:51 '1 2 3 4'
-rw-r--r-- 1 root root 1478 Jul 4 16:51 passwd
-rw-r--r-- 1 root root 22 Jul 15 16:50 test.sh
$#表示脚本后面的参数个数,一般结合判断使用,判断用户是否传入参数
$? 上一次命令的返回值,0代表执行成功,非0上次命令失败(1-255)
从命令行中接受用户的输入信息
read 从命令行中接受用户信息,可以指定变量名
-p 提示信息
-s秘文输入,静默输入/不回显输入
-t超时时间,单位是秒
read -p "输入你的提示词:" [变量]
read -p "输入你的提示词:" informathion #这样就为用户输入的信息传入变量$informathion
read -t 3 -s -p "输入你的提示词:" informathion #用户输入时秘文输入,三秒不操作超时
注意:read的参数传递有很明确的层级,-t后面跟秒,-p后面写提示信息
read -p -s "输入你的提示词:" informathion #经过后期校对,此处更改-s被识别成了-p的字符串
如果你没有明确变量,则会使用read的默认变量 $REPLY,此变量只有一个,如果有多个read则会相互覆盖
经过和GPT讨论和实验环境验证发现的问题,我在此前犯了这个问题,已经将同样问题的地方进行了校对修正
read -p "输入你的提示词" $informathin #错误写法,嫑学
read的变量名不需要带$,正确写法应该是如下
read -p "输入你的提示词" informathin #正确写法,要学
exit退出码
当满足某一种条件后,执行退出,个人认为常用于各种if判断
exit可以携带退出码,成功是0,如果是1-255则为失败,也可以人为定义
exit的退出码是帮助判断问题的
#!/bin/bash
useradd $1
exit 2
这是的意思携带为2的退出码退出,我可以在手册中定义退出码2为创建成功
但尽量不要学习这种逻辑,1-255应该是各种各样的报错,一般默认0为成功,如果使用后期的条件执行运算,会导致"逻辑与"失效,逻辑与使用退出码来判断成功,成功退出码为0
#!/bin/bash
useradd $1
exit 0
上面的示例则是携带成功退出码退出(也就是退出码为0)
后期修正:如果使用 exit命令直接退出,他不会默认为0,而会继承上一条命令的退出码
在运行结束后使用echo $?命令即可查看退出码
条件执行运算符
使用逻辑条件执行多条命令
&& 逻辑与,当命令1执行成功(返回值为0的时候),则执行命令2;如果命令1执行失败,则命令2不执行
echo a && echo b && echo c
||逻辑或,命令1执行失败则执行命令2(也是通过返回值进行判断)
个人理解这个东西就像是备用路线,来一个实例看一下
pgrep nginx || ps aux | grep nginx || echo "执行失败啦"
这条命令的逻辑就是,先执行 pgrep 命令搜索 nginx 进程,如果执行失败,则启用备用方案 使用 ps aux | grep nginx 来搜索 nginx 进程,如果还是失败,则输出 执行失败啦!
如果第一条就成功了,则后面的命令都不执行
前一个命令返回非 0 才走备用路线;任意一个返回 0,后面就停止
刚跟GPT又掰扯了一下,ps aux | grep nginx这条命令确实会把grep自己也搜出来(前面学习进程管理的时候深有体会),所以可以使用正则表达式
pgrep nginx || ps aux | grep '[n]ginx' || echo "执行失败啦"
算是一种取巧的写法,可以匹配普通文本nginx但无法匹配[n]ginx,防止匹配到grep命令导致的误判为执行成功
! 逻辑非,对命令的结果进行取反,这块感觉有点抽象,但貌似理解了?
上个实例搞一下
! echo a && echo success
如果输出成功,返回值取反即为错误,如果输出失败,则返回值取反即为成功(有点抽象,感觉有点鸡肋)
简单理解一下:如果失败则变为成功,如果成功则变为失败(什么鸟东西
逻辑非记得加空格!
视频课里给出了一个案例:使用逻辑与逻辑或,判断网络能否上外网,如果可以则输出ip success 如果不可以则输出ip error,我想到了用ping,但限制ping次数的参数我不会,去问一下GPT
-c 指定ping次数的参数,我先试验一下输出的返回值是多少
root@SH-Debian-Tencent:~# ping baidu.com -c 4
PING baidu.com (124.237.177.164) 56(84) bytes of data.
64 bytes from 124.237.177.164: icmp_seq=1 ttl=49 time=28.0 ms
64 bytes from 124.237.177.164: icmp_seq=2 ttl=49 time=28.3 ms
64 bytes from 124.237.177.164: icmp_seq=3 ttl=49 time=28.3 ms
64 bytes from 124.237.177.164: icmp_seq=4 ttl=49 time=28.2 ms
--- baidu.com ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3003ms
rtt min/avg/max/mdev = 27.976/28.182/28.288/0.122 ms
root@SH-Debian-Tencent:~# echo $?
0
确定了,输出的返回值是0,再试一下错误的
root@SH-Debian-Tencent:~# ping 10.10.10.10 -c 4
PING 10.10.10.10 (10.10.10.10) 56(84) bytes of data.
--- 10.10.10.10 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 3076ms
root@SH-Debian-Tencent:~# echo $?
1
返回值是1,为错误;解释一下为什么使用10段的ip去ping,10段在网络管理那一章已经知道是内网ip,我的网络环境里没有10段的设备,所以一定会ping失败(经过和GPT的严肃探讨^-^,将这里明确一下,因为在本次实验环境中该地址不可达,所以选用了此ip)
下面开始写实例
ping baidu.com -c 4 &>/dev/null && echo "ip success" || echo "ip error"
为什么这么写呢,因为逻辑与是前序成功才执行,如果执行成功了就success,如果执行失败是跳到逻辑或去执行的
课程中补充了 -W是单次响应的超时时间,所以在此记出来
我都做完了才补充出来这俩参数,麻了
补充:课程中有人使用了超时,我感觉在此使用超时很鸡肋,因为可能是网不好而不是没网络
和GPT交流了一下,可以把 -W 设宽松一点,或多 Ping 几次,减少命令对此的误判
在后期交流中,发现我对退出码和返回值的口语化表达可能在术语上有歧义,所以在此纠正,并不是我不会,是因为我就爱说返回值^-^
解题完毕!
关于和GPT形成的-w和-W的歧义问题在此进行明确
根据iputils官方文档中ping.xml将其定义为如下
-w整个ping命令的超时时间
-W等待单次ping响应的超时时间
iputils官方文档中关于此项的描述:iputils/doc/ping.xml at master · iputils/iputils · GitHub
-w Specify a timeout, in seconds, before
-WTime to wait for a response, in seconds. The option affects only timeout in absence of any responses, otherwise ping waits for two RTTs.
复合指令
将多条命令变成一条集合,这里涉及到shell和子shell
{}执行括号内的命令,在当前shell运行,开头必须加空格,结尾必须是分号,命令分割使用分号或换行
{ pwd;cd /root;}
()执行括号内的命令,在子shell中运行,命令分割使用分号或换行
(pwd;cd /root) #使用此命令不会进入root目录
这里会有一些疑问,那既然实在子shell中运行,那我删文件不是删不掉?(这是课程上有人问的,所以写出来)
答案:会删,虽然操作的不是一个shell,但操作的是同一个用户同一个系统。
可以把子shell理解为多开了一个终端控制台
这里有我刚刚一个疑问,既然小括号内执行不受格式要求,那我为什么要用小括号呢?我直接使用分号分割命令不就可以啦?
我自己想通了,直接使用分号分割命令如果在控制台执行,执行的是当前shell,直接在控制台中使用括号执行,执行的是子shell,但我们上面讲到了使用不同的方式启动脚步分配到是不同的shell,就在执行脚本的方式那里,但说的可能不是很详细
bash执行脚本时会创建一个子shell执行,使用脚本定义变量无法使用
source在当前shell执行脚本,定义的变量可以直接使用
所以根据脚本执行的方式才能判断使用大括号还是小括号去执行命令,但是我感觉使用bash执行的话依旧会在子shell中运行大括号的内容(这里是对的,好绕
简单总结一下吧:使用bash命令启动脚本时,脚本已经在子shell中运行了,所以大括号也是指当前的子shell中运行,如果在这时候使用小括号,则在子shell中再创建一个子shell运行该命令
可能很绕,所以再精简一下
{} 在当前层级的shell中运行命令
()在当前层级的子shell中运行命令(也就是当前层级的下一层级,怎么感觉这么像后室一层又一层一直套娃
条件测试语句
这一块语句巨多,我后期会在速查表中进行补充,现在先把常用的列出来
检查一个条件,然后用退出码回答,成立返回 0,不成立返回非 0
test 条件表达式写法1,常用于在shell中直接测试
[ 条件表达式 ]写法2,常用在脚本中的if判断,也可用此写法使用逻辑符,后期有补充
[[ 条件表达式 ]]写法3,如果使用逻辑与和逻辑或推荐使用此写法(支持正则与通配符)
用来测试文件,数字和字符串
他在课上说这个东西不能接命令,我持否定意见,经过我在Debian环境中的实测,是可以接命令的
他使用的测试语句
[ ping -c 1 baidu.com ]
我使用的测试语句
[ "ping -c 1 baidu.com" ]
但在我看来这东西纯属脱裤子放屁,我直接执行命令然后看返回值不就行了?
后期纠正:确实没办法判断命令,判断的是字符串非空(我脑子瓦特了^-^
文件测试运算符
-f判断文件是否存在,存在返回0
-d判断是否为目录,是返回0
-e判断目录是否存在,是返回0
-s判断文件是否存在且是否为空,不存在或为空返回1,存在且有内容范围0(他讲的时候只说了判断是否为空,但我感觉怪怪的就去查了一下)感觉此参数在应用层面更多是判断一个文件是否有内容,有内容则返回0,没内容则返回1
test -f /etc/passwd
[ -f /etc/passwd ]
[[ -f /etc/passwd ]]
-b是否为块设备
-r -w -x 判断是否有相应的权限
后期经过激烈讨论在此处补充关于-e -f -d -s的官方解释,以免引起歧义
-e 路径对应的对象存在
-f 路径对应的对象存在,并且是普通文件
-d 路径对应的对象存在,并且是目录
-s 路径对应的对象存在,并且大小大于0
GNU Bash官方文档 6.4 Bash Conditional Expressions:Bash Conditional Expressions (Bash Reference Manual)
在此明确此定义
数值测试运算符
关于数值运算测试符,我看到那些命令,我瞬间就知道怎么记了
-eq 等于
-lt小于
-gt大于
-ge大于等于
-le小于等于
-ne不等于,也可以用等于然后取反
我不知道为什么我突然一下就全记住了,我知道怎么背,但我描述不出来,l=low g=big,t=test,后面的我就描述不出来了,但我能记住,我和GPT也交流了一下记忆方法,不过我的描述确实是错的,l=less等等,就不在这里写出来了,只要能记住就是好的,到时候在文档中不会形成歧义就好啦
[ 1 -eq 1 ]
布尔运算符
-a 与运算,等价 && 只有两个表达式同时成立结果才为真
-o 或运算,等价 ||两个表达式只要有一个真即为真
! 非运算,对整个表达式取反,同逻辑表达式里的 !逻辑非
后期与GPT查证,-a -o为test的旧语法,复杂条件可能容易引起歧义,建议使用以下写法
[ 条件1 ] && [ 条件2 ]
[ 条件1 ] || [ 条件2 ]
或
[[ 条件1 && 条件2 ]]
[[ 条件1 || 条件2 ]]
关于 -a 和 -o 为旧语法的争论
GNU Bash官方文档 4.1 Bourne Shell Builtins 中的test描述章节 :Bourne Shell Builtins (Bash Reference Manual)
原文写道:The historical operator-precedence parsing with 4 or more arguments can lead to ambiguities when it encounters strings that look like primaries. The POSIX standard has deprecated the -a and -o primaries and enclosing expressions within parentheses. Scripts should no longer use them. It’s much more reliable to restrict test invocations to a single primary, and to replace uses of -a and -o with the shell’s && and || list operators.
可译为:历史上的四参数及以上优先级解析可能产生歧义;POSIX 已弃用 -a、-o,脚本不应继续使用,应改成 Shell 的 &&、|| 列表运算符。
官方推荐写法如下:
test -n string1 && test -n string2
test "$1" && test "$2"
字符串运算符
= 判断字符串是否相等
!=判断字符串是否非等,这里他使用了取反,而不是非等,我感觉怪怪的所以我去实测了一下 !=究竟是否可行
他使用的(布尔运算符方法)
[ ! a = b ]
我使用的(字符串判断方法)
[ a != b ]
实测下来两种非等都是可以正常使用的,不过他是先判断是否,然后进行取反,我是直接判断是否为非值,两种在此来看是等价的
-z:字符串长度为 0 时成立
-n:字符串长度非 0 时成立,此处因为后期任务发现这里没教,所以进行补充
if判断语句
分为:单分支、双分支和多分支
格式语句:if 条件表达式 then 执行语句 fi
但我感觉大家都爱写成
if 条件表达式
then
执行语句
fi
判断/opt/passwd文件是否存在,存在则输出ok。这个实例我直接自己做了
if [ -f /opt/passwd ] then echo ok fi #报废版本,错误写法
我感觉如果命令不长的话,这样写更简洁。
我问了以下GPT,此版本报废了,呜呜呜呜^-^我终于知道大家为什么要写成那样了,因为一个命令是一个命令,正儿八经的写法应该是这样
if [ -f /opt/passwd ]; then echo ok; fi
也就是这种^-^,因为他们是不同命令,跟编程不一样,他是各种命令组成的if语句^-^换行本身就是分隔符,所以他们才爱用这种写法吗呜呜呜
if [ -f /opt/passwd ]
then
echo ok
fi
双分支就是加入了else
if [ -f /opt/passwd ]
then
echo ok
else
echo no,no,no
fi
多分支结构使用的是 elif 进行多元判断,由上至下依次匹配,匹配到相应的就执行,执行完毕后跳fi退出判断,演示如下:(这里他没讲,所以我补充一下
if [ -f /opt/passwd ]; then
echo ok
elif [ 条件2 ]; then
echo "no,no,no"
elif [ 条件3 ]; then
echo 222
elif [ 条件4 ]; then
echo 333
else
echo 444
fi
多分支演示实例
if [ -f /etc/1 ]
then
echo 1
elif [ -f /etc/2 ]
then
echo 2
elif [ -f /etc/3 ]
then
echo 3
else
echo error
fi
在我看来,简单的单分支结构和双分支结构完全可以依靠逻辑运算符达到一条命令去实现,但并不普遍等价于 if/else 条件B未执行成功已经会到条件C
if [ -f /etc/passwd ]
then
echo file ok
else
echo file error
fi
test -f /etc/passwd && echo file ok || echo file error
case 判断语句
case是一种判断语句,我认为常用于菜单选项或脚本允许入参选择模式,格式如下
read -p "输入值选择功能" MENU_NUM
case $MENU_NUM in
1)
echo 1
;;
2)
echo 2
;;
3)
echo 3
;;
4)
echo a
;;
*)
echo "输入不正确";
exit 1
;;
esac
双分号代表case结束符,代表运行完当前部分就结束整个case
之前做过一个开源项目chemex的一键部署shell脚本,粘贴出菜单部分做case实例
function DockerOPmenu {
echo -e "--------------------------------------------------"
echo -e "Chemex_Shell | Chemex | 一键部署脚本\033[32m v1.4 \033[0m"
echo -e "--------------------------------------------------"
echo -e "您当前位于\033[32m Docker常用命令执行 \033[0m菜单"
echo -e "--------------------------------------------------"
echo -e "1. 输出所有本地容器"
echo -e "2. 输出所有本地镜像"
echo -e "3. 停止Chmex容器"
echo -e "4. 停止Mysql容器"
echo -e "5. 停止docker"
echo -e "6. 输出Chemex容器日志"
echo -e "7. 输出Mysql容器日志"
echo -e "0. 返回上一页"
read -erp "输入你的选择:" input
case "$input" in
0)
menu ;;
1)
clear && sudo docker ps -a && DockerOPmenu ;;
2)
clear && sudo docker images && DockerOPmenu ;;
3)
clear && sudo docker stop chemex && DockerOPmenu ;;
4)
clear && sudo docker stop mysql-test && DockerOPmenu ;;
5)
clear && sudo service docker stop && DockerOPmenu ;;
6)
clear && sudo docker logs chemex && DockerOPmenu ;;
7)
clear && sudo docker logs mysql-test && DockerOPmenu ;;
*)
echo "【错误】没有此选项";;
esac
}
此脚本源地址:ChemexShell/chemex_shell_1.4.sh at main · A-xianchu/ChemexShell · GitHub
case语句适用于对结果已知的状态
case还有很多相关知识,在此没有展开,后期视情况补充
for循环
for [变量] in [值]
do
[命令]
done
for [变量] in [值1] [值2] [值3] [值4]
do
[命令]
done
实例
for i in user1 user2 user3
do
useradd $i
done
for i in 1 2 3 4 5
do
echo $i
done
for i in {1..10}
do
echo $i
done
使用seq命令可以输出设定最大值前面的所有值
seq 10
for i in $(seq 10)
do
echo $i
done
引用命令也可以使用反引号
for i in `seq 10`
do
echo $i
done
同样也可以引用命令的执行结果,举一个批量创建用户的简易例子
for i in $(cat user_list)
do
useradd $i
done
案例:键盘读取源路径和目标路径,实际运行时分别输入 /etc、/opt
#!/bin/bash
read -p "input cp begin dir or file:" begin
read -p "input cp mubiao dir or file:" mb
if [[ -n $begin && -n $mb && -e $begin ]]
then
cp -a $begin $mb &> /dev/null && echo "success" && exit 0 || echo filed && exit 2
else
echo "input error or file null"
exit 1
fi
最开始的思路是各种if嵌套,后面写到一半发现不该这么麻烦,然后用了表达式和逻辑符,对了这是个for循环的案例,我把这玩意直接用if搞了
while循环
我认为while常用于菜单循环
while [条件表达式]
do
执行语句
done
如果条件表达式为真,则一直循环while内部,如果为假则跳出
true代表一直为真,返回值永远是0
while也可以读取文件(按行读取)
#第一种方法
while read line
do
echo $line
done < /etc/passwd
#第二种方法(通过管道符将数据传入while
cat /etc/passwd | while read line
do
echo $line
done
#第三种(我不理解这里为什么
exec < /etc/passwd
while read line
do
echo $line
done
死循环
while true
do
echo hi~
done
until循环
如果条件表达式为假,则一直循环until内部,如果为真则跳出
其格式和while循环一样
until [条件表达式]
do
执行语句
done
可以判断服务启动报警等(应该常用于环境检测
break和continue
视频课里说很多人大学里学了可能分不清这俩的区别是什么,而我就不一样了,我压根忘了这俩是啥东西了^-^
continue是跳过本次循环
break是跳出当前整个循环
一个小实验就可以很明确的看出来了,这里是编写了一个脚本,意思是遇到5就运行continue命令,即跳出本次循环
root@SH-Debian-Tencent:~# cat continue
#!/bin/bash
for i in {1..10}
do
if [ $i -eq 5 ]
then
continue
fi
echo $i
done
echo End
root@SH-Debian-Tencent:~# bash continue
1
2
3
4
6
7
8
9
10
End
而 break是跳出整段循环,通过以下实验可以看出
root@SH-Debian-Tencent:~# cat break
#!/bin/bash
for i in {1..10}
do
if [ $i -eq 5 ]
then
break
fi
echo $i
done
echo End
root@SH-Debian-Tencent:~# bash break
1
2
3
4
End
sleep 命令
暂停命令,或者理解为临时停止
sleep 10 临时停止10秒
我认为此命令一般用于大量命令生成环境等待环境部署成功
END