shell基本语法
随机数
echo $RANDOM |md5sum |cut -c 1-8
运算符
整数: 方法1:
a=$[1+3] echo $a
方法2:
#!/bin/bash val=`expr 2 + 2` echo "两数之和为 : $val"
浮点数: 方法1: a=`echo "20 20 100"|awk '{printf("%g",$1*$2/$3)}'` echo $a 方法2: echo "scale=1; 2.5*4" | bc
详细请查看: http://www.runoob.com/linux/linux-shell-basic-operators.html (算术运算符、关系运算符、布尔运算符、逻辑运算符、字符串运算符、文件测试运算符)
数组
例1. 数组赋值和显示,增加数组元素 ,显示数组个数 [root@aming shellDir]# a=(1 2 3 4) 数组赋值 [root@aming shellDir]# echo ${#a[*]} 1 2 3 4 例2.显示数组第一个 [root@aming shellDir]# echo ${a[0]} 1 例3.增加数组元素 [root@aming shellDir]# a[4]=9 例4.显示数组所有值 [root@aming shellDir]# echo ${a[@]} 1 2 3 4 9 例5.显示数组个数 [root@aming shellDir]# echo ${#a[*]} 5 例6.数组清楚某位 [root@aming shellDir]# echo ${a[*]} 1 2 3 4 9 [root@aming shellDir]# unset a[2] [root@aming shellDir]# echo ${a[*]} 1 2 4 9 例7.数组切片显示 [root@aming shellDir]# echo ${a[*]} 1 2 4 9 [root@aming shellDir]# echo ${a[*]:0:2} :0第几个索引 :2显示位数 1 2 例8. 数组随机赋值,显示排序 [root@aming shellDir]# cat 3arry.sh #!/bin/bash ## ## for i in `seq 0 9` do a[$i]=$RANDOM done echo ${a[*]} echo ${a[*]} | sed "s/ /\n/g" | sort -n [root@aming shellDir]# sh 3arry.sh 5177 13327 606 685 26238 24009 28414 8994 17346 5379 606 685 5177 5379 8994 13327 17346 24009 26238 28414 例子9.冒泡排序 [root@aming shellDir]# cat 3arry.sh #!/bin/bash for i in `seq 0 9` do a[$i]=$RANDOM done echo ${a[*]} t=0 for i in `seq 0 9` do num=$[8-$i] for j in `seq 0 $num` do c=$[$j+1] if [ ${a[$j]} -gt ${a[$c]} ] then t=${a[$j]} a[$j]=${a[$c]} a[$c]=$t fi done done echo ${a[*]} [root@aming shellDir]# sh 3arry.sh 28927 7755 29130 16348 8305 4271 25701 16865 22021 30807 4271 7755 8305 16348 16865 22021 25701 28927 29130 30807
判断符号表示
> -gt < -lt == -eq != -ne >= -ge <= -le
select用法
select check_phpmod in have noHave do case $check_phpmod in have) break ;; noHave) echo "install php mod json pdo_mysq first " exit 1 ;; *) echo "only 1(have) or 2(noHave)" exit 1 ;; esac done
脚本分区
fdisk $dev_name &>/dev/null <<EOF d n p 1 +512M w EOF
脚本所有正确和错误输出指定文件
exec 1>> /tmp/acc.log 2>> /tmp/err.log
date #时间命令
符号的表示: date +%F 日期 date +%T 时间 date +%Y 4位年 date +%y 2位年 date +%m 月 date +%d 日 date +%H 时 date +%M 分 date +%S 秒 date +%w 第几周 date +%W 今年的第几周,一年有52周 显示时间: date +"%F %T" #显示结果 2017-02-06 22:16:39 date +"TIME is %F %H:%M:%S" #TIME is 2017-02-06 22:17:43 date +"%Y-%m-%d %H:%M:%S" #2016-08-26 01:11:17 date +%s #显示时间戳 date -d '2013-2-22 22:14' +%s #指定日期的时间戳 指定时间: date -d "-2 days" +%F date -d "-2 month" +%F date -d "-2 year" +%F date -d "-2 hour" +%T date -d "-2 minute" +%T date -d "-200 second" +%T date -d "+1 days 20150401" +%Y%m%d agoDate=`date -d "-1 minutes" +"%Y:%H:%M"` 时间戳转日期: date -d @1361542596 #Fri Feb 22 22:16:36 CST 2013 date -d @12884342 +"%Y-%m-%d %H:%M:%S" #2013-02-22 22:16:36 设定时间: date -s "2015-5-8 19:48:00"
常用:
date +"%Y-%m-%d_%H-%M-%S"
date -d "-1 day" +"%Y-%m-%d_%H-%M-%S"
[] 判断用法
-s file 文件大小非0时为真 [ -f "somefile" ] :判断是否是一个文件 [ -x "/bin/ls" ] :判断/bin/ls是否存在并有可执行权限 [ -n "$var" ] :判断$var变量是否有值,非空为真 [ -z "$var" ] : 空为真 [ "$a" = "$b" ] :判断$a和$b是否相等 -r file 用户可读为真 -w file 用户可写为真 -x file 用户可执行为真 -f file 文件为正规文件为真 -d file 文件为目录为真 -c file 文件为字符特殊文件为真 -b file 文件为块特殊文件为真 -s file 文件大小非0时为真 -t file 当文件描述符(默认为1)指定的设备为终端时为真
判断两数大小
if ( [ "`echo "$tps >= $crit_tps" | bc`" == "1" ] || [ "`echo "$kbread >= $crit_read" | bc`" == "1" ] || \ [ "`echo "$kbwritten >= $crit_written" | bc`" == "1" ] ) then msg="CRITICAL" status=2 else if ( [ "`echo "$tps >= $warn_tps" | bc`" == "1" ] || [ "`echo "$kbread >= $warn_read" | bc`" == "1" ] || \ [ "`echo "$kbwritten >= $warn_written" | bc`" == "1" ] ) then msg="WARNING" status=1 else msg="OK" status=0 fi
解析参数
[root@git-ued /opt/sh 12:23:18&&11]#cat test.sh #!/bin/bash #sh test.sh -d 5 -w w -c c -s 10 while getopts "d:s:w:c:h" OPT; do case $OPT in "d") disk=$OPTARG;; "w") warning=$OPTARG;; "c") critical=$OPTARG;; "s") second=$OPTARG;; "h") help;; esac done echo $disk $warning $critical $second
检查参数不能为空
[ ! -b "/dev/$disk" ] && echo "ERROR: Device incorrectly specified" && help ( [ "$warn_tps" == "" ] || [ "$warn_read" == "" ] || [ "$warn_written" == "" ] || \ [ "$crit_tps" == "" ] || [ "$crit_read" == "" ] || [ "$crit_written" == "" ] ) && echo "ERROR: You must specify all warning and critical levels" && help ( [ "$second" == "" ] ) && second=1 ( [[ "$second" -ge "$maxsecond" ]]) && second=10 ( [[ "$warn_tps" -ge "$crit_tps" ]] || \ [[ "$warn_read" -ge "$crit_read" ]] || \ [[ "$warn_written" -ge "$crit_written" ]] ) && \ echo "ERROR: critical levels must be highter than warning levels" && help
特殊字符:*,?,\,>,>>,2>,2>>,;,~,&,[],&&,||
* 表示通配符 ?批配单个 #注释 \ 脱义符 可以把特殊符号变成普通字符 | 管道符 cat /etc/passwd | wc -l > >> 重定向和重定向追加 echo "abc" > 1.txt 2> 2>> 错误重写向 ls abcd 2> 1.txt < 反重写向 如:wc -l < 1.txt 这种写法不常见 $ 变量前缀 前面命令的最后参数!$ 文本中是行尾,尾行 ; 连接命令 ~ 用户家目录 & 命令后面,把这个命令放在后台运行 && 并且 || 或者 [0-9] 批配0-9中的任意一个 ls [12].txt 相当于 ls 1.txt 2.txt
逻辑运算&&,||,;,!:
&& 并 ls 1.tx && ls 2.txt 前面的执行成功,后面命令才会执行 常用左边逻辑真 || 或 ls 1.txt || ls 2.txt 只执行一个命令,前面的成功,后面不执行。前面的不成功,后面的执行。常用左边逻辑非 ; 和 ls /home/ ; cd /home ! 取反 [ ! -f /tmp/1.txt ] && touch /tmp/1.txt
函数:
注意 1. 函数中的变量在shell中是会被在外面使用的,要想不被使用 local number=1 2. function myFun(){} function是可以不写的 3. 函数位置必须在调用之前 4. 函数返回值只能是数字,用echo $? 来获得这个返回值 例1: [root@aming shellDir]# cat 3fun.sh #!/bin/bash ## ## function sumFun(){ sum=$[$1+$2] echo $sum return $sum } sumFun 1 3 returnSum=`echo $?` echo "w m d sum: $returnSum"
while
例1:死循环 #!/bin/bash while : do date +%T sleep 3 done 例2: 判断条件 #!/bin/bash n=1 while [ $n -le 10 ] do echo $n n=$[$n+1] done 例3. 直到输入数字为至。 #!/bin/bash n=1 number=0 while [ -n "$n" ] do read -p "please input a number : " number n=`echo $number | sed "s/[0-9]//g"` done echo "this number is : $number" 例3.批量添加用户 #!/bin/bash ## if [ -f /root/user.txt ] then exec < /root/user.txt while read line do username=`echo $line | awk '{print $1}'` password=`echo $line | awk '{print $2}'` useradd $username && echo $password | passwd --stdin $username done else echo "user.txt is not have..." fi
if
if 例 grep -q "$name" /etc/passwd -q参数是批配到也不输出,在if判断中批配到就是真 [root@aming shellDir]# cat 7.sh #!/bin/bash ## ## read -t 10 -p "please input a user name:" name name="^$name:" if grep -q "$name" /etc/passwd then echo "is have" else echo "no have" fi 例1. if [ ] then fi [root@aming shellDir]# cat 4.sh #/bin/bash ## if [ $a -gt $b ] 注意空格 ## a=$1 b=$2 echo "\$a=$a \$b=$b" if [ $a -gt $b ] then echo "$a > $b" fi [root@aming shellDir]# sh 4.sh 5 3 $a=5 $b=3 5 > 3 例2. if [ ] then else fi [root@aming shellDir]# cat 4.sh #/bin/bash ## ## a=$1 b=$2 echo "\$a=$a \$b=$b" if [ $a -gt $b ] then echo "$a > $b" else echo "$a <= $b" fi [root@aming shellDir]# sh 4.sh 3 4 $a=3 $b=4 3 <= 4 例3. if [ ] then elif [ ] then else fi 注意:elif [] then 可以多个 [root@aming shellDir]# cat 4.sh #/bin/bash ## ## read -t 10 -p "please input your score :" number if [ $number -gt 90 ] then echo " is good " elif [ $number -gt 70 ] then echo " is middle" elif [ $number -gt 60 ] then echo " is bad" else echo "is.........you are die" fi [root@aming shellDir]# sh 4.sh please input your score :95 is good [root@aming shellDir]# sh 4.sh please input your score :80 is middle [root@aming shellDir]# sh 4.sh please input your score :65 is bad
for
for 例1. 循环相加 [root@aming shellDir]# cat 9for.sh #!/bin/bash ## ## for i in `seq 1 10` do echo "$i" done sum=0 for i in {1..10} do sum=$[$i+$sum] done echo $sum [root@aming shellDir]# sh 9for.sh 1 2 3 4 5 6 7 8 9 10 55 例2. for循环中 文件内容 把空格当分隔符 for默认就可以按照空格或者行来遍历 [root@aming shellDir]# cat 10for.sh #!/bin/bash ## ## fileName="1.txt" [ -f $fileName ] || cp /etc/passwd ./$fileName for i in `cat $fileName` do echo $i done [root@aming shellDir]# cat 1.txt 1 2 3 1111111111111111 22222222222 [root@aming shellDir]# sh 10for.sh 1 2 3 1111111111111111 22222222222 例3. 循环ls结果 [root@aming shellDir]# for file in `ls` ; do echo $file;done 10for.sh 1.sh 1.txt 2.sh 3.sh 4.sh 5.sh 6.sh 7.sh 8case.sh 9for.sh
seq
seq 例1: [root@aming shellDir]# seq 1 10 1 2 3 4 5 6 7 8 9 10 例2: [root@aming shellDir]# seq -w 1 10 注意:-w参数把位数定在 最大位上 01 02 03 04 05 06 07 08 09 10 例3: [root@aming shellDir]# seq 1 2 10 中间的2是位长 1 3 5 7 9 例3: [root@aming shellDir]# seq 10 -2 1 10 8 6 4 2
case
例1: [root@aming shellDir]# cat 8case.sh #!/bin/bash ## ## read -t 10 -p " please input a number: " n number=`echo $n | sed "s/[0-9\.]//g"` if [ -n "$number" ] then echo "is not a number...." else n=$[$n%2] case $n in 0) echo "is odd..." ;; 1) echo "is even..." ;; *) ;; esac fi
可以用${file}分别替换得到不同的值
${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
脚本中set -e
set -e #执行过程出错就退出,不会滚血球,越滚越严重
脚本所在目录
cd `dirname $0` #脚本所在目录
脚本中各参数的函数
$0 :代表脚本自身; $1 :代表脚本传入的第一个参数; $2 :代表脚本传入的第二个参数; $# :代表传递给脚本或函数的参数个数; $* :传递给脚本或函数的所有参数。该参数是否包含在 “ ” 内是有区别的; $@ :传递给脚本或函数的所有参数。该参数是否包含在 “ ” 内是没有区别的; $? :上个命令的退出状态,或函数的返回值; $ : 当前Shell进程ID;
正则通配符号说明
. 表示任意一个字符,包括特殊符号 * 表示*号前面的字符,0个或多个 等价于 {0,} .* 表示任意个任意字符 ? 0或1个?号前面的字符 等价于{0,1} + 1个或多个+号前面的字符 等价于 {1,} (abc) #把abc当位一起的 egrep '(rr){1,3}' /etc/passwd ()里的出现1到3次都可以 ? + 用egrep,要用grep 必须脱义 或加 -E参数
awk
解释一下:在awk中使用脱义字符\是起不到作用的,如果想打印特殊字符,只能使用' " " ' 这样的组合才可以。 这里自左至右为单引号 双引号 双引号 单引号其中两个单引号为一对, 例: 想脱义$那就是' "$" ' 脱义单引号那就是 ' " ' " ' awk求和: df -m | sed -n '2,$'p | awk '{tot=tot+$2} END {print tot/1024}' awk求平均值: tps=`echo "$iostatus" | awk '{(tot=tot+$2)} ; END {print tot/NR}'` 截取文档中的某段 -F 指定分隔符号 awk -F ':' '{print $1}' 1.txt 使用自定义符连接每一段 awk -F ':' '{print $1"#"$2"#"$3}' 1.txt awk -F ':' '{OFS="#"} {print $1,$2,$3}' 1.txt 匹配 awk '/oo/' 1.txt awk '/root|hui/' /etc/passwd awk '/root/&&/hui/' 1.txt 针对某个段 匹配 awk -F ':' '$1 ~/oo/' 1.txt awk -F ':' '$1 ~/^h/' 1.txt 多次区配 awk -F ':' '/root/ {print $1,$3}; $1 ~/test/; $3 ~/20/' 1.txt awk中也可以使用if关键词 awk -F ':' '{if($1=="root") print $0}' 1.txt 条件操作符 ==,>,<,!=,>=,<= 第三段为0 awk -F ':' '$3=="0"' 1.txt 第三段大于等于500 awk -F ':' '$3>=500' 1.txt 说明 :当比较数字时,不能加双引号,如果写成$3>="500" 就不符合我们的需求 第七段不是'/sbin/nologin' awk -F ':' '$7!="/sbin/nologin"' 1.txt 第三段小于第四段 awk -F ':' '$3<$4' 1.txt 第三段大于5,并且第三段小于7 awk -F ':' '$3>5 && $3<7' 1.txt 第三段大于5或者第七段为'/bin/bash' awk -F ':' '$3>5 || $7=="/bin/bash"' 1.txt awk内置变量NF(段数) NR(行数) head -n3 1.txt | awk -F ':' '{print NF}' head -n3 1.txt | awk -F ':' '{print $NF}' head -n3 1.txt | awk -F ':' '{print NR}' 打印20行以后的行 awk 'NR>20' 1.txt 打印20行以后并且第一段包含ssh的行 awk -F ':' 'NR>20 && $1 ~/ssh/' 1.txt 更改某个段的值 awk -F ':' '$1="root"' 1.txt 数学计算,把第三段和第四段值相加,并赋予第七段 awk -F ':' '{$7=$3+$4; print $0}' 1.txt awk -F ':' '{OFS=":"} {$7=$3+$4; print $0}' 1.txt 显示分隔符 #统计网站日uv awk '{print $1}' /data/nginx/logs/access_2015-06-18.log |sort |uniq -c|sort -rn |awk '{print $2}'|uniq -c |awk '{(tot=tot+$1)}; END {print tot}' #统计某个时间段的日志 #!/usr/bin/env bash set -e set -x #设置要统计的起始时间 INTIME='[15/Mar/2017:08:10:01' #设置要统计的结束时间 LASTIME='[15/Mar/2017:09:00:00' #定义日志路径: LOG='/data/nginx/logs/app/access_2017-03-15.log' #统计访问正常的: awk "\$4 >= \"${INTIME}\" && \$4 <= \"${LASTIME}\"" $LOG |egrep '200|301|302' |wc -l #统计访问有问题的: awk "\$4 >= \"${INTIME}\" && \$4 <= \"${LASTIME}\"" $LOG |egrep -v '200|301|302' |wc -l #3156 awk '$4 >= "[30/Jul/2017:19:00:00" && $4 <= "[30/Jul/2017:21:00:00" ' /usr/local/nginx/logs/www.3156.cn.log
sed
替换 sed替换 可用/// ### @@@ sed '1,10s#nologin#login#g' 1.txt 1到10行替换 g是全局 sed 's#nologin#login#g' 1.txt 全文本替换 \可以脱义路径符/ sed 's/^.*$/login/g' 1.txt 所有行换成 login sed 's/[0-9]//g' 1.txt 数字清空 sed 's/[a-Z]//g' 1.txt 所有字母清空 sed 's/[^0-9a-Z]//g' 1.txt 非字母数字清空 sed -r 's/([a-Z0-9]+)(:.*:)(.*$)/\3\2\1/g' 1.txt 头尾互换 -r脱义+号 ()把行分为3段 sed 's/^.*$/& login/g' 1.txt 每行后面加 空格login sed 's/^/#/g' 1.txt 行头加# 匹配 sed -n '10'p 1.txt 打印第10行 sed -n '1,10'p 1.txt 打印第1到10行 sed -n '10,$'p 1.txt 打印第10行到末行 sed -n '/root/'p 1.txt 打印出包含root的行,比grep多// 多p sed -n '/r.o/'p 1.txt =====grep 'r.o' /etc/passwd ==r.o 表示ro中间任意一个字符都能批配 批配一个 sed -n '/r*o/'p 1.txt ==========grep 'r*o' /etc/passwd == 表示0个或多个*前面的字符 sed -n '/r.*o/'p 1.txt===========grep 'r.*o' /etc/passwd == 表示0个或多个任意字符,*前面 sed -n '/r\?o/'p 1.txt ==========grep 'r\?o' /etc/passwd == 表示0个或1个?前面的字符 sed -n '/r\+o/'p 1.txt ========== grep 'r\+o' /etc/passwd 一个或多个+号前面的字符 sed -n '/root\|nologin/'p 1.txt ======== grep --color 'root\|nologin' 1.txt 批配root,nologin行 sed -n '/root/p; /games/p' 1.txt ===========sed -n -e '/root/'p -e '/games/'p 1.txt 批配root,games sed -n '/\(oo\)\+/'p 1.txt ============grep -E '(oo)+' 1.txt 批配1对或多对oo sed -r -n '/(oo)+/'p 1.txt =======sed -n '/\(oo\)\+/'p 1.txt sed -r 相当对 grep -E 的功能,可以不脱义 删除 sed '/^$/'d 1.txt == 相当于grep的 -v 取反 grep --color -v '^$' 1.txt sed '1,19'd 1.txt 删除1到19行,不写入文件,只显示在屏幕上 sed -i '1,5'd 1.txt 删除1到5行,写入文件。 -i参数的作用, 不建议用,怕误操作。 sed最后添加一行 sed -i '$a\command[check_iostat]=/usr/local/nagios/libexec/check_iostat -d sda -w 100,200,300 -c 200,300,400 -s 10 ' /usr/local/nagios/etc/nrpe.cfg sed批配行后加一行 sed -i '/^\[mysqld\]$/a\datadir = /data/mysql' /etc/my.cnf sed匹配行首加# sed '/root/ s/^/#/' /etc/passwd sed更多用法: http://www.361way.com/sed-process-lines/2263.html sed批配行后面的数据 sed -n '/2017-11-24 17:10/,$'p catalina.out 脚本中暂停tail -f tail -f text.log | sed '/JVM running/Q'
grep和egrep
grep 过滤出指定的行 #grep 批配ip格式 grep '\([0-9]\{1,3\}\.\)\{3\}[0-9]\{1,3\}' grep 'root' /etc/passwd grep -i 不区分大小写 grep --color 'root' /etc/passwd 把批配的字符标颜色 grep -o root /etc/passwd 仅显示匹配的字符 grep -q root /etc/passwd #匹配到内容也不输出,常用在脚本中 grep -n 'root' /etc/passwd 把批配的行显示出行号 grep -c 'root' /etc/passwd 把批配的行数显示出来 ,如果只有2行就显示2 grep -v 'root' /etc/passwd 把不批配的行显示出来,也就是取反 grep -A 2 'root' /etc/passwd 把批配行和下面2行都显示出来 grep -B 2 'root' /etc/passwd 把批配行和上面2行都显示出来 grep -C 2 'root' /etc/passwd 把批配行和上面2行,下面2行都显示出来 grep -r 'iptables' /etc/* 遍历批配 grep -r --include="*.php" 'eval' /data/ 把一个目录下,过滤所有*.php文档中含有eval的行 grep '[0-9]' /etc/passwd 过滤出包含数字的行 grep '[aN]' /etc/passwd 过滤出包含a,N的行 grep '[a-zA-Z]' /etc/passwd 过滤出包含字母的行 grep '^[a-z]' /etc/passwd 过滤出以小写字母开头的行 grep '[0-9]' /etc/passwd 含数字的行 grep '[^0-9]' /etc/passwd 含有非数字的行 grep -v '[^0-9]' 1.txt 不含有非数字的行 纯数字 grep '^&' 1.txt 空行 grep 'r.o' /etc/passwd ==r.o 表示ro中间任意一个字符都能批配 批配一个 grep 'r*o' /etc/passwd == 表示0个或多个*前面的字符 grep 'r.*o' /etc/passwd == 表示0个或多个任意字符,*前面 grep 'r\?o' /etc/passwd == 表示0个或1个?前面的字符 grep -E 'r?o' /etc/passwd === grep 'r\?o' /etc/passwd 结果是一样的 grep -E == egrep egrep能用的 grep -E 都能用 egrep 'r+o' /etc/passwd 一个或多个+号前面的字符 ===grep -E 'r+o' /etc/passwd egrep egrep 'root|aming' /etc/passwd |表示或的意思 grep 'root' /etc/passwd | grep 'aming' /etc/passwd 批配root又批配aming 用这种 方式表示并且 egrep '(rr)+' /etc/passwd 表示 批配1个或多个()里的字符 egrep '(rr){1,3}' /etc/passwd ()里的出现1到3次都可以
脚本中退出tail -f
tail -f text.log | sed '/JVM running/Q'
shell解析json
下载jq 二进制文件 https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64 cp jq-linux64 /usr/bin cd /usr/bin mv jq-linux64 jq chmod +x jq #json文件如下,ceshi.json { "menu": { "id": "file", "value": "File:", "popup": { "menuitem": { "value": "New", "onclick": "CreateNewDoc()" } } } } ##解析 cat ceshi.json | jq .menu.id | sed 's/\"//g'