- A+
系列文章:
Linux Shell 常用命令 - 01篇
8. shell 中各种括号的作用
参考
https://www.jb51.net/article/123081.htm
https://www.jianshu.com/p/3e1eaaa3fee8
8.1 单小括号 ()
一般在命令替换的时候使用
#!/bin/bash today=$(date +%Y%m%d) # shell扫描一遍命令,发现了$()结构,便将$()中的命令执行一次,得到其标准输出,再将此输出赋值给today # 等同于today=`date +%Y%m%d`
8.2 双小括号 (())
使用双括号,在比较过程中使用高级数学表达式
符号 | 描述 |
---|---|
val++ | 后增 |
val-- | 后减 |
++val | 先增 |
--val | 先减 |
! | 逻辑求反 |
~ | 位求反 |
** | 幂操作 |
<< | 左位移 |
>> | 右位移 |
&& | 逻辑与 |
|| | 逻辑或 |
双小括号 (( )) 是 Bash Shell 中专门用来进行整数运算的命令,它的效率很高,写法灵活,是企业运维中常用的运算命令。(())
是一种数学计算命令,它除了可以进行最基本的加减乘除运算,还可以进行大于、小于、等于等关系运算,以及与、或、非逻辑运算。
注:(( )) 只能进行整数运算,不能对小数(浮点数)或者字符串进行运算。
双小括号 (( )) 的语法格式为:
((表达式))
通俗地讲,就是将数学运算表达式放在((
和))
之间。
表达式可以只有一个,也可以有多个,多个表达式之间以逗号,
分隔。对于多个表达式的情况,以最后一个表达式的值作为整个 (( )) 命令的执行结果。
可以使用 $ 获取 (( )) 命令的结果,这和使用 $ 获得变量值是类似的。
在 (( )) 中使用变量无需加上$
前缀,(( )) 会自动解析变量名,这使得代码更加简洁,也符合程序员的书写习惯。
【实例1】利用 (( )) 进行简单的数值计算。
【实例2】利用 (( )) 进行逻辑运算。
运行结果如下:
可以直接使用if (($i<5)), 如果不使用双括号, 则为if [ $i -lt 5 ]
【实例3】利用 (( )) 进行自增(++)和自减(--)运算。
val=10 ((val++)) echo $val # 输出 11
【实例4】利用 (( )) 同时对多个表达式进行计算。
8.3 单方括号 []
方括号定义了测试条件。第一个方括号 ([
) 后和第二个方括号 (]
) 前都要加一个空格,否则会报错。方括号主要包括4类判断:
(1)数值比较:
if [ $val1 -eq $val2 ] if [ $val1 -le $val2 ] ...
(2)字符串比较:
if [ $str1 = $str2 ] if [ $str1 != $str2 ] if [ -n $str1 ] # 检查str1的长度是否非0 if [ -z $str1 ] # 检查str1的长度是否为0
例:test.sh脚本如下:
运行结果:
经过测试,数值也可以使用=, !=,且对于数值与字符串,=与==效果貌似一样?
运行结果:
(3)文件比较
(4)符合条件比较
[ condition1 ] && [ condition2 ] [ condition1 ] || [ condition2 ]
注:[ ]中的 逻辑与 和 逻辑或 使用 -a 和 -o 表示,例: if [ $a -ne 1 -a $a != 2 ]
,或写作:if [ $a -ne 1 ] && [ $a != 2 ]
8.4 双方括号 [[]]
- 双方括号提供了字符串比较的高级特性
- 括号中可以定义一些正则表达式来匹配字符串
- 注意不是所有的shell都支持双方括号
注:&&、||、< 和 > 操作符能够正常存在于[[ ]]条件判断结构中,比如可以直接使用 if [[ $a != 1 && $a != 2 ]]
8.5 示例
if ($i<5) if [ $i -lt 5 ] if [ $a -ne 1 -a $a != 2 ] if [ $a -ne 1] && [ $a != 2 ] if [[ $a != 1 && $a != 2 ]] for i in $(seq 0 4);do echo $i;done for i in `seq 0 4`;do echo $i;done for ((i=0;i<5;i++));do echo $i;done for i in {0..4};do echo $i;done
9. shell if else 语句
9.1 if 语句
if condition then statement(s) fi
如果要将then 和 if 写在一行,要加个分号:
if condition; then statement(s) fi
【实例1】下面的例子使用 if 语句来比较两个数字的大小:
? 其中 read a
表示从命令行读取,并赋值给a
【实例2】在判断条件中也可以使用逻辑运算符,例如:
9.2 if else 语句
if condition then statement1 else statement2 fi
9.3 if elif else 语句
if condition1 then statement1 elif condition2 then statement2 elif condition3 then statement3 ... else statementn fi
例2:
来自 https://www.tutorialspoint.com/unix/if-elif-statement.htm
10. shell while
参考
http://c.biancheng.net/view/2804.html
https://blog.csdn.net/weixin_44324367/article/details/111312156
shell 之 循环执行某操作 - Simple-Sir - 博客园 (cnblogs.com)
while condition do statements done
while 语句和 if else 语句中的 condition 用法都是一样的,可以使用 test 或 [] 命令,也可以使用 (()) 或 [[]]
【实例1】计算从 1 加到 100 的和。
【实例2】计算从 m 加到 n 的值。
【实例3】输出0~10的所有整数
11. shell 日期加减运算
参考
http://blog.sina.com.cn/s/blog_ad6555610101b5ud.html
https://www.cnblogs.com/zhzhang/p/6846300.html
https://www.cnblogs.com/simple-li/p/14040777.html
http://www.dutycode.com/post-60.html
https://www.runoob.com/linux/linux-comm-date.html
Linux date命令可以用来显示或设定系统的日期与时间,语法:
date [-u] [-d datestr] [-s datestr] [--utc] [--universal] [--date=datestr] [--set=datestr] [--help] [--version] [+FORMAT] [MMDDhhmm[[CC]YY][.ss]] 参数: -d datestr : 显示 datestr 中所设定的时间 (非系统时间) --help : 显示辅助讯息 -s datestr : 将系统时间设为 datestr 中所设定的时间 -u : 显示目前的格林威治时间 --version : 显示版本编号
假如今天是 2022-04-19
$ date Tue Apr 19 11:27:50 CST 2022 $ date +%Y%m%d 20220419 ? date 获取当前时间,+%Y%m%d 控制显示格式 $ date -d "+1 day" +%Y-%m-%d 2022-04-20 $ date -d "-1 day" +%Y-%m-%d 2022-04-18 ? 其他写法: date -d "-1days" +%Y-%m-%d date -d "1 day ago" +%Y-%m-%d (其中+%Y-%m-%d 与 +'%Y-%m-%d' 都可) $ date -d "2012-04-10 -1 day" +%Y-%m-%d 2012-04-09 $ date -d "+1 week" +%Y-%m-%d 2022-04-26 同理还可 month, year
例1:脚本示例:
#!/bin/bash if [ $# == 0 ] ; then ydate=`date -d -1days "+%Y%m%d"` ydateother=`date -d -1days "+%Y-%m-%d"` tdate=`date -d -0days "+%Y%m%d"` tdateother=`date -d -0days "+%Y-%m-%d"` elif [ $# == 1 ] ; then ydate=$1 ydateother=`date -d ${ydate} +"%Y-%m-%d"` tdate=`date -d "${ydate} 1days" +"%Y%m%d"` tdateother=`date -d ${tdate} +"%Y-%m-%d"` elif [ $# == 2 ] ; then ydate=$1 ydateother=`date -d ${ydate} +"%Y-%m-%d"` tdate=$2 tdateother=`date -d ${tdate} +"%Y-%m-%d"` else echo "please do not input if calcute yedterday." echo "please input like if calcute 20170510 one day: 20170510" echo "please input like if calcute 20170510 and 20170512 two days : 20170510 20170511" exit -1 fi echo ${ydate}'--'${ydateother}'--'${tdate}'---'${tdateother}
例2: 按月执行201904到202010的py脚本:
#!/bin/bash i=201904 # 定义开始月份 while [ $i -le 202010 ] # 当i小于等于202010时执行循环语句 do dateNo1=$(date +"%Y-%m-%d %H:%M:%S") # 获取当前时间(年月日时分秒) echo "开始执行($dateNo1): $i" # 打印当前时间(年月日时分秒) python tmp_sn_union_mem_20201125.py $i # 执行python脚本,传入月份参数。 此处也可做其他操作 dateNo2=$(date +"%Y-%m-%d %H:%M:%S") # 获取当前时间(年月日时分秒) echo "结束执行($dateNo2): $i" # 打印当前时间(年月日时分秒) i=$[$i + 1] # 当前i值加1 if [ $i -eq 201913 ] # 判断i值是否等于201913,条件必须写在 [] 里 then i=202001 # 如果i值等于201913,则让i值为202001 fi # if结束标记 done # while结束标记
注:
fi: if语句结束标记 done: 循环执行结束标记 i=$[$i + 1]: 变量加减运算要写在 $[] 里面。 if [ $i -eq 201913 ]: 条件必须写在 [] 里
附:数值比较:
-eq
相等(equal)
-ne
不等(not equal)
-gt
大于(greater than)
-lt
小于(less than)
-ge
大于等于 (greater than or equal)
-le
小于等于 (less than or equal)
注:crontab任务调度中,%是个特殊字符,要在前加""进行转义
例:
0 10 * * * cd /home/zzh && sh test.sh $(date -d "1 day ago" +"%Y%m%d") >/dev/null 2>&1
12. 重定向
参考 [linux shell中"2>&1"含义]https://www.cnblogs.com/zhenghongxin/p/7029173.html)
1表示标准输出
2表示标准错误
2>&1 的意思就是将标准错误重定向到标准输出
例:
>./test_run.sh >test_run.sh.log 2>&1 输出结果保存到test_run.sh.log(标准输出与错误)(>会覆盖已存在的内容) >./test_run.sh >>test_run.sh.log 2>&1 输出结果追加到test_run.sh.log(标准输出与错误)(>>追加到末尾) >./test_run.sh >test_run.sh.log 输出结果保存到test_run.sh.log(标准输出),错误信息输出到屏幕 >./test_run.sh 2>test_run.sh.log 输出结果(不包含错误信息)到屏幕,错误信息保存到test_run.sh.log 例有crontab任务: */2 * * * * root cd /opt/xxxx/test_S1/html/xxxx/admin; php index.php task testOne >/dev/null 2>&1 & 可以把/dev/null 可以看作"黑洞". 它等价于一个只写文件. 所有写入它的内容都会永远丢失. 而尝试从它那儿读取内容则什么也读不到. 可以把 & 在命令的最后加上,表示让程序后台执行。 0 5 * * * 表示每天5:00执行
补充:如何既将结果保存到文件,又输出到屏幕:可使用 tee
,示例如下
>./run_test.sh | tee run_test.log
13. crontab 定时执行shell脚本
参考
linux crontab定时执行shell脚本 - 龙昊雪 - 博客园 (cnblogs.com)
linux定时运行命令脚本——crontab_阳光岛主-CSDN博客
crontab命令说明:
crontab命令被用来提交和管理用户的需要周期性执行的任务,与windows下的计划任务类似,当安装完成操作系统后,默认会安装此服务工具,并且会自动启动crond进程,crond进程每分钟会定期检查是否有要执行的任务,如果有要执行的任务,则自动执行该任务。
语法 crontab (选项) (参数) 选项 -e:编辑该用户的计时器设置; -l:列出该用户的计时器设置; -r:删除该用户的计时器设置; -u<用户名称>:指定要设定计时器的用户名称。 参数 crontab文件:指定包含待执行任务的crontab文件。
crontab命令:
crontab -e -> 编辑(进入后,press i 进行编辑,:wq保存并退出) crontab -l -> 查看
crontab文件的含义:
用户所建立的crontab文件中,每一行都代表一项任务,每行的每个字段代表一项设置,它的格式共分为六个字段,前五段是时间设定段,第六段是要执行的命令段,格式如下:
minute hour day month week command 顺序:分 时 日 月 周 其中: minute: 表示分钟,可以是从0到59之间的任何整数。 hour:表示小时,可以是从0到23之间的任何整数。 day:表示日期,可以是从1到31之间的任何整数。 month:表示月份,可以是从1到12之间的任何整数。 week:表示星期几,可以是从0到7之间的任何整数,这里的0或7代表星期日。 command:要执行的命令,可以是系统命令,也可以是自己编写的脚本文件。 在以上各个字段中,还可以使用以下特殊字符: 星号(*):代表所有可能的值,例如month字段如果是星号,则表示在满足其它字段的制约条件后每月都执行该命令操作。 逗号(,):可以用逗号隔开的值指定一个列表范围,例如,“1,2,5,7,8,9” 中杠(-):可以用整数之间的中杠表示一个整数范围,例如“2-6”表示“2,3,4,5,6” 正斜线(/):可以用正斜线指定时间的间隔频率,例如“0-23/2”表示每两小时执行一次。同时正斜线可以和星号一起使用,例如*/10,如果用在minute字段,表示每十分钟执行一次。 注释行要在行首用#来表示
crontab文件条目的一些例子:
30 21 * * * /apps/bin/cleanup.sh 上面的例子表示每晚的21:30运行/apps/bin目录下的cleanup.sh。 45 4 1,10,22 * * /apps/bin/backup.sh 上面的例子表示每月1、10、22日的4:45运行/apps/bin目录下的backup.sh。 10 1 * * 6,0 /bin/find -name "core" -exec rm {} ; 上面的例子表示每周六、周日的1:10运行一个find命令。 0,30 18-23 * * * /apps/bin/dbcheck.sh 上面的例子表示在每天18:00至23:00之间每隔30分钟运行/apps/bin目录下的dbcheck.sh。 0 23 * * 6 /apps/bin/qtrend.sh 上面的例子表示每星期六的23:00运行/apps/bin目录下的qtrend.sh。 你可能已经注意到上面的例子中,每个命令都给出了绝对路径。当使用crontab运行shell脚本时,要由用户来给出脚本的绝对路径,设置相应的环境变量。
13.1关于路径
关于路径错误,因为在crontab中使用了绝对路径执行脚本,因此在该脚本中引用的其它脚本也都需要使用绝对路径,才能被crontab找到并执行。如何避免绝对路径复杂的设置呢,可采用如下格式:
0 5 * * * cd /data/test-ml;sh crontab_run.sh >>/data/test-ml/crontab_run.sh.log 2>&1 & 或 0 5 * * * cd /data/test-ml && ./crontab_run.sh >>/data/test-ml/crontab_run.sh.log 2>&1 & 每天5点执行,并将输出保存至crontab_run.sh.log(包括标准输出与标准错误)
建议使用此方式,先进入该目录,然后再执行脚本;否则,执行脚本中的其它脚本都需要加绝对路径
13.2 crontab 中传入时间参数
15 4 * * * /home/temp/post_analysis/post_analysis_sqoop.sh $(date -d "1 day ago" +"%Y-%m-%d") >/dev/null 2>&1
14 Linux shell 管道
Shell 有一种功能,就是可以将两个或者多个命令(程序或者进程)连接到一起,把一个命令的输出作为下一个命令的输入,以这种方式连接的两个或者多个命令就形成了管道(pipe)。
Linux 管道使用竖线|
连接多个命令,这被称为管道符。Linux 管道的具体语法格式如下:
command1 | command2 command1 | command2 [ | commandN... ]
管道符|
与两侧的命令之间可以存在也可以不存在空格。
当在两个命令之间设置管道时,管道符|
左边命令的输出就变成了右边命令的输入。只要第一个命令向标准输出写入,而第二个命令是从标准输入读取,那么这两个命令就可以形成一个管道。大部分的 Linux 命令都可以用来形成管道。
使用管道的优点:省去创建临时文件。
【示例】将 ls 命令的输出发送到 grep 命令:
[c.biancheng.net]$ ls | grep log.txt log.txt
上述命令是查看文件 log.txt 是否存在于当前目录下。
我们可以在命令的后面使用选项,例如使用-al
选项:
[c.biancheng.net]$ ls -al | grep log.txt -rw-rw-r--. 1 mozhiyan mozhiyan 0 4月 15 17:26 log.txt
我们也可以重定向管道的输出到一个文件(使用重定向操作符>或>>将管道中的最后一个命令的标准输出进行重定向),比如将上述管道命令的输出结果发送到文件 output.txt 中:
[c.biancheng.net]$ ls -al | grep log.txt >output.txt [c.biancheng.net]$ cat output.txt -rw-rw-r--. 1 mozhiyan mozhiyan 0 4月 15 17:26 log.txt
15. 在 Linux shell 中查看与处理文件常用的命令
15.1 less
使用管道将 cat 命令的输出作为 less 命令的输入,这样就可以将 cat 命令的输出每次按照一个屏幕的长度显示,这对于查看长度大于一个屏幕的文件内容很有帮助。
cat /var/log/message | less
15.2 grep
grep 命令用于查找文件里符合条件的字符串。
语法:
grep [-abcEFGhHilLnqrsvVwxy][-A<显示行数>][-B<显示列数>][-C<显示列数>][-d<进行动作>][-e<范本样式>][-f<范本文件>][--help][范本样式][文件或目录...] 参数: -a 或 --text : 不要忽略二进制的数据。 -A<显示行数> 或 --after-context=<显示行数> : 除了显示符合范本样式的那一列之外,并显示该行之后的内容。 -b 或 --byte-offset : 在显示符合样式的那一行之前,标示出该行第一个字符的编号。 -B<显示行数> 或 --before-context=<显示行数> : 除了显示符合样式的那一行之外,并显示该行之前的内容。 -c 或 --count : 计算符合样式的列数。 -C<显示行数> 或 --context=<显示行数>或-<显示行数> : 除了显示符合样式的那一行之外,并显示该行之前后的内容。 -d <动作> 或 --directories=<动作> : 当指定要查找的是目录而非文件时,必须使用这项参数,否则grep指令将回报信息并停止动作。 -e<范本样式> 或 --regexp=<范本样式> : 指定字符串做为查找文件内容的样式。 -E 或 --extended-regexp : 将样式为延伸的正则表达式来使用。 -f<规则文件> 或 --file=<规则文件> : 指定规则文件,其内容含有一个或多个规则样式,让grep查找符合规则条件的文件内容,格式为每行一个规则样式。 -F 或 --fixed-regexp : 将样式视为固定字符串的列表。 -G 或 --basic-regexp : 将样式视为普通的表示法来使用。 -h 或 --no-filename : 在显示符合样式的那一行之前,不标示该行所属的文件名称。 -H 或 --with-filename : 在显示符合样式的那一行之前,表示该行所属的文件名称。 -i 或 --ignore-case : 忽略字符大小写的差别。 -l 或 --file-with-matches : 列出文件内容符合指定的样式的文件名称。 -L 或 --files-without-match : 列出文件内容不符合指定的样式的文件名称。 -n 或 --line-number : 在显示符合样式的那一行之前,标示出该行的列数编号。 -o 或 --only-matching : 只显示匹配PATTERN 部分。 -q 或 --quiet或--silent : 不显示任何信息。 -r 或 --recursive : 此参数的效果和指定"-d recurse"参数相同。 -s 或 --no-messages : 不显示错误信息。 -v 或 --invert-match : 显示不包含匹配文本的所有行。 -V 或 --version : 显示版本信息。 -w 或 --word-regexp : 只显示全字符合的列。 -x --line-regexp : 只显示全列符合的列。 -y : 此参数的效果和指定"-i"参数相同。
【示例1】在当前路径下,查找后缀有 file 字样的文件中包含 test 字符串的文件,并打印出该字符串的行。
grep test *file
结果如下所示:
>grep test test* #查找前缀有“test”的文件包含“test”字符串的文件 testfile1:This a Linux testfile! #列出testfile1 文件中包含test字符的行 testfile_2:This is a linux testfile! #列出testfile_2 文件中包含test字符的行 testfile_2:Linux test #列出testfile_2 文件中包含test字符的行
【示例2】以递归的方式查找符合条件的文件。例如,查找指定目录/etc/acpi 及其子目录(如果存在子目录的话)下所有文件中包含字符串"update"的文件,并打印出该字符串所在行的内容,使用的命令为:
grep -r update /etc/acpi
【示例3】查看result.csv中出现100001的行并输出
>cat result.csv | grep 100001
将输出结果存入文件:
>cat result.csv | grep 100001 >output.txt
查找并返回行号:
>cat result.csv | grep -n 100001
若想在另一个文件中查看相同行号的内容:
> cat result2.csv | sed -n '334007p'
15.3 wc
wc命令用于计算字数
语法:
wc [-clw][--help][--version][文件...] 参数: -c或--bytes或--chars 只显示Bytes数。 -l或--lines 显示行数。 -w或--words 只显示字数。 --help 在线帮助。 --version 显示版本信息。
【示例1】返回行数,字数,字节数
$ cat testfile Linux networks are becoming more and more common, but scurity is often an overlooked issue. Unfortunately, in today’s environment all networks are potential hacker targets, fro0m tp-secret military research networks to small home LANs. Linux Network Securty focuses on securing Linux in a networked environment, where the security of the entire network needs to be considered rather than just isolated machines. It uses a mix of theory and practicl techniques to teach administrators how to install and use security applications, as well as how the applcations work and why they are necesary. $ wc testfile # testfile文件的统计信息 3 92 598 testfile # testfile文件的行数为3、单词数92、字节数598
【示例2】查看文件中包含多少个逗号,结合 grep
:
>cat result.txt | grep -o ',' | wc -l
15.4 sed
sed 可依照脚本的指令来处理、编辑文本文件。
语法:
sed [-hnV][-e<script>][-f<script文件>][文本文件] 参数说明: -e<script>或--expression=<script> 以选项中指定的script来处理输入的文本文件。 -f<script文件>或--file=<script文件> 以选项中指定的script文件来处理输入的文本文件。 -h或--help 显示帮助。 -n或--quiet或--silent 仅显示script处理后的结果。 -V或--version 显示版本信息。 动作说明: a :新增, a 的后面可以接字串,而这些字串会在新的一行出现(目前的下一行)~ c :取代, c 的后面可以接字串,这些字串可以取代 n1,n2 之间的行! d :删除,因为是删除啊,所以 d 后面通常不接任何东东; i :插入, i 的后面可以接字串,而这些字串会在新的一行出现(目前的上一行); p :打印,亦即将某个选择的数据印出。通常 p 会与参数 sed -n 一起运行~ s :取代,可以直接进行取代的工作哩!通常这个 s 的动作可以搭配正规表示法!例如 1,20s/old/new/g 就是啦!
假如有如下文件:
$ cat testfile #查看testfile 中的内容 HELLO LINUX! Linux is a free unix-type opterating system. This is a linux testfile! Linux test Google Taobao Runoob Tesetfile Wiki
【示例1】列出文件的5到7行:
$ cat testfile | sed -n '5,7p' Google Taobao Runoob
【示例2】搜索 testfile 有 oo 关键字的行:
$ cat testfile | sed -n '/oo/p' Google Runoob
【示例3】数据的查找与替换:
除了整行的处理模式之外, sed 还可以用行为单位进行部分数据的查找与替换
语法格式如下:
sed 's/要被取代的字串/新的字串/g'
将 testfile 文件中每行第一次出现的 oo 用字符串 kk 替换,然后将该文件内容输出到标准输出:
>sed -e 's/oo/kk/' testfile
注:sed 后面接的动作,要以 '...' 两个单引号括住
g
标识符表示全局查找替换,使 sed 对文件中所有符合的字符串都被替换,修改后内容会到标准输出,不会修改原文件:
>sed -e 's/oo/kk/g' testfile
选项 i
使 sed 修改文件:
>sed -i 's/oo/kk/g' testfile
批量操作当前目录下以 test 开头的文件:
>sed -i 's/oo/kk/g' ./test*
【示例4】将文件中的的逗号替换为t,写入另一个文件:
>cat ./docs/a.txt |sed 's/,/t/g' >./docs/b.txt
直接修改原文件:
>sed -i 's/,/t/g' ./docs/a.txt
15.5 关于vim
15.5.1 快速清空文件内容
参考 https://blog.csdn.net/qq_21238607/article/details/119541193
#命令模式下 G # 跳至文件最后一行 d1G # 删除首行至光标所在行的所有数据
? 注:输入完 d1G
后,会立马清空,不会要求确认等操作。
15.5.2 撤销与恢复
注意,以上这 3 种命令都必须在 Vim 编辑器处于命令模式时才能使用。