知乎专栏 |
系统变量,Shell常用的系统变量并不多,但却十分有用,特别是在做一些参数检测的时候。下面是Shell常用的系统变量
表示方法 描述 $n $1 表示第一个参数,$2 表示第二个参数 ... $# 命令行参数的个数 $0 当前程序的名称 $? 前一个命令或函数的返回码 $* 以"参数1 参数2 ... " 形式保存所有参数 $@ 以"参数1" "参数2" ... 形式保存所有参数 $$ 本程序的(进程ID号)PID $! 上一个命令的PID
其中使用得比较多得是 $n $# $0 $? ,看看下面的例子:
#!/bin/sh if [ $# -ne 2 ] ; then echo "Usage: $0 string file"; exit 1; fi grep $1 $2 ; if [ $? -ne 0 ] ; then echo "Not Found \"$1\" in $2"; exit 1; fi echo "Found \"$1\" in $2"; 上面的例子中使用了$0 $1 $2 $# $? 等变量 下面运行的例子: ./chapter2.2.sh usage chapter2.2.sh Not Found "usage" in chapter2.2.sh -bash-2.05b$ ./chapter2.2.sh Usage chapter2.2.sh echo "Usage: $0 string file"; Found "Usage" in chapter2.2.sh
0 表示正常结束运行, 1 表示异常退出
[root@iZ621r6pk9aZ nginx]# ping -W 2 -c 2 www.google.com PING www.google.com (172.217.24.196) 56(84) bytes of data. 64 bytes from hkg12s13-in-f4.1e100.net (172.217.24.196): icmp_seq=1 ttl=57 time=1.51 ms 64 bytes from hkg12s13-in-f4.1e100.net (172.217.24.196): icmp_seq=2 ttl=57 time=1.44 ms --- www.google.com ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1001ms rtt min/avg/max/mdev = 1.447/1.479/1.512/0.050 ms [root@iZ621r6pk9aZ nginx]# echo $? 0
我们ping 一个不存在的IP地址,然后 Ctrl+C 推出程序,返回值是 1.
[root@iZ621r6pk9aZ nginx]# ping -W 2 -c 2 172.16.1.100 PING 172.16.1.100 (172.16.1.100) 56(84) bytes of data. ^C --- 172.16.1.100 ping statistics --- 2 packets transmitted, 0 received, 100% packet loss, time 999ms [root@iZ621r6pk9aZ nginx]# echo $? 1
如果 redis 用户不存,就创建一个名为 redis 的用户。
id redis if [ $? -eq 1 ] then adduser -s /bin/false -d /var/lib/redis redis fi
!!:再次执行上一条命令 !$:上一条命令的最后一个单词 {a..b}:按照从a到b顺序的一个数字列表 {a,b,c}:三个词a,b,c. 可以这样使用 touch /tmp/{a,b,c} {$1-$9}:执行shell脚本时的命令行参数 $0:正在执行的命令名称 $#:当前启动的命令中传入的参数个数 $?:上一条命令的执行返回值。 $$:该shell的进程号。 $*:从$1开始,启动该shell脚本的所有参数。
$ mkdir -p {a..z} $ ls a b c d e f g h i j k l m n o p q r s t u v w x y z $ mkdir -p {a..z}{0..9} $ ls a0 b0 c0 d0 e0 f0 g0 h0 i0 j0 k0 l0 m0 n0 o0 p0 q0 r0 s0 t0 u0 v0 w0 x0 y0 z0 a1 b1 c1 d1 e1 f1 g1 h1 i1 j1 k1 l1 m1 n1 o1 p1 q1 r1 s1 t1 u1 v1 w1 x1 y1 z1 a2 b2 c2 d2 e2 f2 g2 h2 i2 j2 k2 l2 m2 n2 o2 p2 q2 r2 s2 t2 u2 v2 w2 x2 y2 z2 a3 b3 c3 d3 e3 f3 g3 h3 i3 j3 k3 l3 m3 n3 o3 p3 q3 r3 s3 t3 u3 v3 w3 x3 y3 z3 a4 b4 c4 d4 e4 f4 g4 h4 i4 j4 k4 l4 m4 n4 o4 p4 q4 r4 s4 t4 u4 v4 w4 x4 y4 z4 a5 b5 c5 d5 e5 f5 g5 h5 i5 j5 k5 l5 m5 n5 o5 p5 q5 r5 s5 t5 u5 v5 w5 x5 y5 z5 a6 b6 c6 d6 e6 f6 g6 h6 i6 j6 k6 l6 m6 n6 o6 p6 q6 r6 s6 t6 u6 v6 w6 x6 y6 z6 a7 b7 c7 d7 e7 f7 g7 h7 i7 j7 k7 l7 m7 n7 o7 p7 q7 r7 s7 t7 u7 v7 w7 x7 y7 z7 a8 b8 c8 d8 e8 f8 g8 h8 i8 j8 k8 l8 m8 n8 o8 p8 q8 r8 s8 t8 u8 v8 w8 x8 y8 z8 a9 b9 c9 d9 e9 f9 g9 h9 i9 j9 k9 l9 m9 n9 o9 p9 q9 r9 s9 t9 u9 v9 w9 x9 y9 z9 $ touch {a..z}{0..9}/{a..z}{0..9} $ ls a0 b0 c0 d0 e0 f0 g0 h0 i0 j0 k0 l0 m0 n0 o0 p0 q0 r0 s0 t0 u0 v0 w0 x0 y0 z0 a1 b1 c1 d1 e1 f1 g1 h1 i1 j1 k1 l1 m1 n1 o1 p1 q1 r1 s1 t1 u1 v1 w1 x1 y1 z1 a2 b2 c2 d2 e2 f2 g2 h2 i2 j2 k2 l2 m2 n2 o2 p2 q2 r2 s2 t2 u2 v2 w2 x2 y2 z2 a3 b3 c3 d3 e3 f3 g3 h3 i3 j3 k3 l3 m3 n3 o3 p3 q3 r3 s3 t3 u3 v3 w3 x3 y3 z3 a4 b4 c4 d4 e4 f4 g4 h4 i4 j4 k4 l4 m4 n4 o4 p4 q4 r4 s4 t4 u4 v4 w4 x4 y4 z4 a5 b5 c5 d5 e5 f5 g5 h5 i5 j5 k5 l5 m5 n5 o5 p5 q5 r5 s5 t5 u5 v5 w5 x5 y5 z5 a6 b6 c6 d6 e6 f6 g6 h6 i6 j6 k6 l6 m6 n6 o6 p6 q6 r6 s6 t6 u6 v6 w6 x6 y6 z6 a7 b7 c7 d7 e7 f7 g7 h7 i7 j7 k7 l7 m7 n7 o7 p7 q7 r7 s7 t7 u7 v7 w7 x7 y7 z7 a8 b8 c8 d8 e8 f8 g8 h8 i8 j8 k8 l8 m8 n8 o8 p8 q8 r8 s8 t8 u8 v8 w8 x8 y8 z8 a9 b9 c9 d9 e9 f9 g9 h9 i9 j9 k9 l9 m9 n9 o9 p9 q9 r9 s9 t9 u9 v9 w9 x9 y9 z9 $ ls a0 a0 b0 c0 d0 e0 f0 g0 h0 i0 j0 k0 l0 m0 n0 o0 p0 q0 r0 s0 t0 u0 v0 w0 x0 y0 z0 a1 b1 c1 d1 e1 f1 g1 h1 i1 j1 k1 l1 m1 n1 o1 p1 q1 r1 s1 t1 u1 v1 w1 x1 y1 z1 a2 b2 c2 d2 e2 f2 g2 h2 i2 j2 k2 l2 m2 n2 o2 p2 q2 r2 s2 t2 u2 v2 w2 x2 y2 z2 a3 b3 c3 d3 e3 f3 g3 h3 i3 j3 k3 l3 m3 n3 o3 p3 q3 r3 s3 t3 u3 v3 w3 x3 y3 z3 a4 b4 c4 d4 e4 f4 g4 h4 i4 j4 k4 l4 m4 n4 o4 p4 q4 r4 s4 t4 u4 v4 w4 x4 y4 z4 a5 b5 c5 d5 e5 f5 g5 h5 i5 j5 k5 l5 m5 n5 o5 p5 q5 r5 s5 t5 u5 v5 w5 x5 y5 z5 a6 b6 c6 d6 e6 f6 g6 h6 i6 j6 k6 l6 m6 n6 o6 p6 q6 r6 s6 t6 u6 v6 w6 x6 y6 z6 a7 b7 c7 d7 e7 f7 g7 h7 i7 j7 k7 l7 m7 n7 o7 p7 q7 r7 s7 t7 u7 v7 w7 x7 y7 z7 a8 b8 c8 d8 e8 f8 g8 h8 i8 j8 k8 l8 m8 n8 o8 p8 q8 r8 s8 t8 u8 v8 w8 x8 y8 z8 a9 b9 c9 d9 e9 f9 g9 h9 i9 j9 k9 l9 m9 n9 o9 p9 q9 r9 s9 t9 u9 v9 w9 x9 y9 z9
neo@MacBook-Pro ~ % echo $RANDOM 15254
$RANDOM 的范围是 0 ~ 32767
for i in {1..10}; do echo -e "$i \t $RANDOM" done
HISTSIZE 将最后多少条历史记录保存到文件中
HISTFILESIZE 定义 ~/.bash_history 的行数
HISTTIMEFORMAT 历史记录格式
export HISTSIZE=10000 export HISTFILESIZE=10000 export HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S " export TIME_STYLE=long-iso
格式如下
903 2019-06-03 00:48:46 docker ps 904 2019-06-03 00:48:49 docker images 905 2019-06-03 00:48:53 docker rmi -f $(docker images -q) 906 2019-06-03 00:48:56 docker stop $(docker ps -a -q) 907 2019-06-03 00:48:57 docker rm -f $(docker ps -a -q) 908 2019-06-03 00:48:57 docker rmi -f $(docker images -q) 909 2019-06-03 00:48:57 docker volume rm $(docker volume ls -q) 910 2019-06-03 00:49:00 docker
$ set -- `echo aa bb cc` $ echo $1 aa $ echo $2 bb $ echo $3 cc $ set -- aa bb cc
set -e: 执行的时候如果出现了返回值为非零,整个脚本 就会立即退出("Exit immediately if a simple command exits with a non-zero status." )
set +e: 执行的时候如果出现了返回值为非零将会继续执行下面的脚本
演示脚本,使用 set -e 运行 aaa 找不到这个命令出错,脚本此时会退出。
[root@gitlab ~]# cat test.sh set -e echo "A" aaa echo "B" [root@gitlab ~]# bash test.sh A test.sh: line 3: aaa: command not found
将 set -e 改为 set +e 后,aaa 虽然执行失败,程序不会退出,并且继续运行,我们可以看到输出 B
[root@gitlab ~]# cat test.sh set +e echo "A" aaa echo "B" [root@gitlab ~]# bash test.sh A test.sh: line 3: aaa: command not found B
如果 CHANNEL_NAME 没有被赋值,那么他的默认值是 "mychannel"
CHANNEL_NAME=$1 : ${CHANNEL_NAME:="mychannel"} echo $CHANNEL_NAME
如果 logfile 值已经存在侧不会覆盖
$ logfile=/var/log/test.log $ echo $logfile /var/log/test.log $ logfile=${logfile:-/tmp/test.log} $ echo $logfile /var/log/test.log
如果变量为空才能设置
$ unset logfile $ logfile=${logfile:-/tmp/test.log} $ echo $logfile /tmp/test.log
export CATALINA_OUT=/www/logs/tomcat/catalina.out
unset 销毁变量
unset CATALINA_OUT
功能说明:声明 shell 变量。 语 法:declare [+/-][rxi][变量名称=设置值] 或 declare -f 补充说明:declare为shell指令,在第一种语法中可用来声明变量并设置变量的属性([rix]即为变量的属性),在第二种语法中可用来显示shell函数。若不加上任何参数,则会显示全部的shell变量与函数(与执行set指令的效果相同)。 参 数: +/- "-"可用来指定变量的属性,"+"则是取消变量所设的属性。 -f 仅显示函数。 r 将变量设置为只读。 x 指定的变量会成为环境变量,可供shell以外的程序来使用。 i [设置值]可以是数值,字符串或运算式。
数值运算表达式
$((EXPR))
neo@netkiller ~ % echo $((1+1)) neo@netkiller ~ % echo $((5*5)) neo@netkiller ~ % echo $(( (1 + 1) * 2 )) 4
num=$(awk "BEGIN {print $num1+$num2; exit}") num=$(python -c "print $num1+$num2") num=$(perl -e "print $num1+$num2") num=$(echo $num1 + $num2 | bc)
巧用linux服务器下的/dev/shm, 实现斐波拉切数列
[neo@netkiller ~]# cat mblq.sh TEMP_FILE=/dev/shm/mblq echo 0 > $TEMP_FILE echo 1 >> $TEMP_FILE count=$1 for i in `seq $count` do first=$(tail -2 $TEMP_FILE |head -1) two=$(tail -1 $TEMP_FILE) echo $((first+two)) >> $TEMP_FILE done cat $TEMP_FILE [neo@netkiller ~]# bash mblq.sh 15 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
[neo@netkiller ~]# cat abcde.sh #!/bin/bash str="abcde"; for ((m=1;m<=${#str};m++));do for ((n=0;n<${#str};n++));do [[ ${#str}-$n -lt $m ]] && continue || echo -n ${str:$n:$m}' ' done done [neo@netkiller ~]# bash abcde.sh a b c d e ab bc cd de abc bcd cde abcd bcde abcde
$ MYVAR=foodforthought.jpg $ echo ${MYVAR##*fo} rthought.jpg $ echo ${MYVAR#*fo} odforthought.jpg
一个简单的脚本例子
mytar.sh #!/bin/bash if [ "${1##*.}" = "tar" ] then echo This appears to be a tarball. else echo At first glance, this does not appear to be a tarball. fi $ ./mytar.sh thisfile.tar This appears to be a tarball. $ ./mytar.sh thatfile.gz At first glance, this does not appear to be a tarball.
$ MYFOO="chickensoup.tar.gz" $ echo ${MYFOO%%.*} chickensoup $ echo ${MYFOO%.*} chickensoup.tar MYFOOD="chickensoup" $ echo ${MYFOOD%%soup} chicken
$ test="aaa bbb ccc ddd" $ echo ${test% *} aaa bbb ccc $ echo ${test%% *} aaa
左侧截取
neo@MacBook-Pro-Neo ~/git/Lisa % STR=Netkiller; echo ${STR:3} killer
右侧截取
file=netkiller.rpm $echo ${file: -3}
范围截取:${varible:n1:n2}:截取变量varible从n1到n2之间的字符串。
$ EXCLAIM=cowabunga $ echo ${EXCLAIM:0:3} cow $ echo ${EXCLAIM:3:7} abunga
neo@MacBook-Pro-Neo ~ % str="123456789" neo@MacBook-Pro-Neo ~ % str="123456789"; echo ${str:3:(6-3)}
$cat name.sh #!/bin/bash while read line ; do fistname=${line% *} lastname=${line#* } echo $fistname $lastname done <<EOF neo chen jam zheng EOF $ bash name.sh neo chen jam zheng
定义数组
arr=(Hello World) arr[0]=Hello arr[1]=World
访问数组
echo ${arr[0]} ${arr[1]} ${arr[*]} # All of the items in the array ${!arr[*]} # All of the indexes in the array ${#arr[*]} # Number of items in the array ${#arr[0]} # Length of item zero
追加操作
ARRAY=() ARRAY+=('foo') ARRAY+=('bar')
#!/bin/bash array=(one two three four [5]=five) echo "Array size: ${#array[*]}" echo "Array items:" for item in ${array[*]} do printf " %s\n" $item done echo "Array indexes:" for index in ${!array[*]} do printf " %d\n" $index done echo "Array items and indexes:" for index in ${!array[*]} do printf "%4d: %s\n" $index ${array[$index]} done
#!/bin/bash array=("first item" "second item" "third" "item") echo "Number of items in original array: ${#array[*]}" for ix in ${!array[*]} do printf " %s\n" "${array[$ix]}" done echo arr=(${array[*]}) echo "After unquoted expansion: ${#arr[*]}" for ix in ${!arr[*]} do printf " %s\n" "${arr[$ix]}" done echo arr=("${array[*]}") echo "After * quoted expansion: ${#arr[*]}" for ix in ${!arr[*]} do printf " %s\n" "${arr[$ix]}" done echo arr=("${array[@]}") echo "After @ quoted expansion: ${#arr[*]}" for ix in ${!arr[*]} do printf " %s\n" "${arr[$ix]}" done
array=({23..32} {49,50} {81..92}) echo "Array size: ${#array[*]}" echo "Array items:" for item in ${array[*]} do printf " %s\n" $item done
while 与 array
declare -a array=('1:one' '2:two' '3:three'); len=${#array[@]} i=0 while [ $i -lt $len ]; do echo "${array[$i]}" let i++ done
array 与 read
declare -a array=('1:one' '2:two' '3:three'); while read -e item ; do echo "$item \n" done <<< ${array[@]} mapfile CONFIG <<END 192.168.0.1 80 192.168.0.1 8080 192.168.0.2 8000 192.168.0.2 80 192.168.0.1 88 END printf %s "${CONFIG[@]}" for line in "${CONFIG[@]}" do read ipaddr port <<< $(echo ${line}) echo "$ipaddr : $port" done
字符串
QUEUES="example|sss"
类似列表的数据结构
for caption in $(echo $QUEUES | tr '|' ' '); do echo $caption done
拆分为数组形式
captions=($(echo $QUEUES | tr '|' ' ')) for element in "${captions[@]}" do echo "$element" done for key in ${!captions[@]}; do echo ${key} ${captions[${key}]} done
[net@netkiller tmp]# cat test.sh read ipaddr port <<< $(echo www.netkiller.cn 80) echo $ipaddr echo $port [net@netkiller tmp]# bash test.sh www.netkiller.cn 80
$ i=5 $ a_5=250 $ eval echo $"a_$i"
# neo="Neo Chen" # name=neo # eval "echo \$$name" Neo Chen
有两个选项 -l 代表小写 -u 代表大写。
typeset -u name name='neo' echo $name typeset -l nickname nickname='netkiller' echo $nickname typeset -l nickname nickname='NETKILLER' echo $nickname
操作演示
[root@localhost ~]# typeset -u name [root@localhost ~]# name='neo' [root@localhost ~]# echo $name NEO [root@localhost ~]# [root@localhost ~]# typeset -l nickname [root@localhost ~]# nickname='netkiller' [root@localhost ~]# echo $nickname netkiller [root@localhost ~]# [root@localhost ~]# typeset -l nickname [root@localhost ~]# nickname='NETKILLER' [root@localhost ~]# echo $nickname netkiller
envsubst 的功能非常类似模版引擎,我这么一说,做开发的小伙伴瞬间秒懂。现在做一个实验。
添加环境变量到预设文件 source.sh
export NAME=Neo export NICKNAME=Netkiller
模版文件 template.tpl
NAME=${NAME} NICKNAME=${NICKNAME}
生成目标文件
[root@localhost tmp]# source source.sh && envsubst < template.tpl > my.conf [root@localhost tmp]# cat my.conf NAME=Neo NICKNAME=Netkiller
设置默认值
cat <<'EOF'> template.tpl #!/bin/bash echo ${NAME} echo ${NICKNAME} echo ${AGE} echo ${HOST} EOF export NAME=${NAME:-'NONE'} export NICKNAME=${NICKNAME:-'NONE'} export AGE=${AGE:-'26'} export HOST=${HOST:-`hostname -I|awk '{print $1}'`} envsubst < template.tpl > my.sh cat my.sh bash my.sh
运行结果
[root@localhost tmp]# cat <<'EOF'> template.tpl > #!/bin/bash > echo ${NAME} > echo ${NICKNAME} > echo ${AGE} > echo ${HOST} > EOF [root@localhost tmp]# [root@localhost tmp]# export NAME=${NAME:-'NONE'} [root@localhost tmp]# export NICKNAME=${NICKNAME:-'NONE'} [root@localhost tmp]# export AGE=${AGE:-'26'} [root@localhost tmp]# export HOST=${HOST:-`hostname -I|awk '{print $1}'`} [root@localhost tmp]# envsubst < template.tpl > my.sh [root@localhost tmp]# [root@localhost tmp]# cat my.sh #!/bin/bash echo NONE echo Netkiller echo 26 echo 192.168.30.12 [root@localhost tmp]# bash my.sh NONE Netkiller 26 192.168.30.12