| 知乎专栏 | 
系统变量,Shell常用的系统变量并不多,但却十分有用,特别是在做一些参数检测的时候。下面是Shell常用的系统变量
表示方法 描述 $n $1 表示第一个参数,$2 表示第二个参数 ... $# 命令行参数的个数 $0 当前程序的名称 $? 前一个命令或函数的返回码 $* 以"参数1 参数2 ... " 形式保存所有参数 $@ 以"参数1" "参数2" ... 形式保存所有参数 $$ 本程序的(进程ID号)PID $! 上一个命令的PID
[root@cc tmp]# cat test.sh echo $# echo $@ [root@cc tmp]# ./test.sh helloworld 1 helloworld
其中使用得比较多得是 $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
			
			
		计算字符串长度
    		
echo ${#PATH}
    		
			
			
    		
$ VAR="This string is stored in a variable VAR"
$ echo ${#VAR}
39    		
    		
			
		定义数组
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