今天我们来探讨shell编程的特殊变量:位置变量。
首先我创建了一个testargs.sh的小脚本:
#!/usr/bin/env bash
#
# Author: jacky18676887374@aliyun.com
# date: 20160813-19:30:59
# Vervion: 0.0.1
# Description:
# Synopsis:
#
#测试脚本的位置参数
#
echo 'This is $# :>>>|'$#
echo 'This is $@ :>>>|'$@
echo 'This is $* :>>>|'$*
echo 'This is $0 :>>>|'$0
echo 'This is $1 :>>>|'$1
echo 'This is $2 :>>>|'$2
echo 'This is $3 :>>>|'$3
echo 'This is $9 :>>>|'$9
echo 'This is $10 :>>>|'$10
echo 'This is ${10} :>>>|'${10}
echo 'This is $11 :>>>|'$11
echo 'This is ${11} :>>>|'${11}
echo 'This is ${-1} :>>>|'${-1}
给脚本a-t共计20个参数,运行之:

由实例我们可以得出初步结论:
-
0: shell脚本的名字;
-
N:N是1开始的正整数,shell脚本的第N个位置参数,当N是个位数数字,可以表示为$N;当N非个位数时候,需要 { } ,即${N} ;
-
#:shell脚本的参数的个数,以十进制计数;
-
*:参数列表
-
@:参数列表
对于前面的三个很好理解,但是*和@都是参数列表,难道说*=@吗?第一反应就是:这不可能,一定是个坑。
到网上查找资料,在Chet Ramey , Brian Fox 著 邵加超(Jerry Fleming)译注的《BASH中文文档》看到说明介绍:

真对不起我的语文老师,几遍下来,依然觉得很生涩,似懂非懂。只能动手结合实例理解:
#!/usr/bin/env bash # #测试脚本的位置参数: @ 和 * # echo 'This is $@ :>>>|'$@ echo 'This is $* :>>>|'$*

貌似看不出来个所以然,恍然想起echo命令是以行显示内容,列表都变成一行输出了。

思路:把参数列表传递给for循环的i变量,然后打印$i,
#!/usr/bin/env bash # #测试脚本的位置参数: @ 和 * # echo 'begin testing $@......' for i in $@ do echo $i done echo 'begin testing $*......' for i in $* do echo $i done

什么情况…还是一样的,难道我的理解错了?再来:
#!/usr/bin/env bash # #测试脚本的位置参数: @ 和 * # echo 'begin testing $@......' for i in "$@" do echo $i done echo 'begin testing $*......' for i in "$*" do echo $i done

真相浮出水面(我果然对不起语文老师)…再看一眼正确演示的脚本↓↓↓↓

由此可以得出最后结论:
-
0: shell脚本的名字;
-
N:N是1开始的正整数,shell脚本的第N个位置参数,当N是个位数数字,可以表示为$N;当N非个位数时候,需要 { } ,即${N} ;
-
#:shell脚本的参数的个数,以十进制计数;
-
*:参数列表,当"$*"时,将所有参数当成一个单位;
-
@:参数列表,当"$@"时,每个参数独立,是多单位的列表清单。
到低脚本支持多少个参数呢?
当测试到N的时候,就忍不住想,shell脚本到低支持多少个参数呢?有什么方法验证呢?抓耳挠腮…还是想到了个笨方法:
思路:seq命令将1..N展开做参数,给shell脚本执行。写好脚本
[root@IP70-CentOS7 ~]# >>echo 'echo This is \$\{10\} \:\|${10}' >> testargs.sh
#往脚本追加一条显示语句。提示位置参数是多少
[root@IP70-CentOS7 ~]# >>./testargs.sh $(seq 1 314551)
#执行脚本,参数由seq命令展开

![]()
-bash: ./testargs.sh: Argument list too long 咦…看来100有点承受不住,再来多几次尝试

314580可以,314595不行…有种曙光即现的感觉…

习题作业:
1、编写脚本/root/bin/systeminfo.sh,显示当前主机系统信息,包括主机名,IPv4地址,操作系统版本,内核版本,CPU型号,内存大小,硬盘大小。
#!/usr/bin/env bash
#
# Author: jacky18676887374@aliyun.com
# date: 20160811-22:58:36
# Vervion: 0.0.1
# Description:
# Synopsis:
#
#编写脚本/root/bin/systeminfo.sh, 显示当前主机系统信息,包括主机名,IPv4 地址,操作系统版本,内核版本,CPU 型号,内存大小,硬盘大小。
IPinfo=`ifconfig | grep 'inet\b' | grep -v '127.0.0.1' | tr -s ' ' | cut -d' ' -f3`
CPUinfo=`lscpu | grep -i "model name:"`
Meninfo=`free -h | sed -n '2p' | tr -s ' ' | cut -d' ' -f2`
Diskinfo=`fdisk -l | sed -n '2p' | sed -r 's/.*[[:space:]]([0-9].*GB).*/\1/g'`
echo 'Hostname :'$(hostname)
echo 'Host IP:'${IPinfo}
echo 'OS version:'$(cat /etc/redhat-release)
echo 'Kernel version:'$(uname -r)
echo 'CPU '$CPUinfo
echo 'Memory :'$Meninfo
echo 'Harddisk:'$Diskinfo
unset IPinfo
unset CPUinfo
unset Meninfo
unset Diskinfo

2、编写脚本/root/bin/backup.sh,可实现每日将/etc/目录备份到/root/etcYYYY-mm-dd中
#!/usr/bin/env bash # # Author: jacky18676887374@aliyun.com # date: 20160811-23:42:05 # Vervion: 0.0.1 # Description: # Synopsis: # #编写脚本/root/bin/backup.sh,可实现每日将/etc/目录备份到/root/etcYYYY-mm-dd中 backdir="/root/etc$(date +%F)" cp -a /etc/. $backdir && echo " backup $backdir finished." unset backdir

3、编写脚本/root/bin/disk.sh,显示当前硬盘分区中空间利用率最大的值
#!/usr/bin/env bash # # Author: jacky18676887374@aliyun.com # date: 20160812-08:12:08 # Vervion: 0.0.1 # Description: # Synopsis: # #编写脚本/root/bin/disk.sh,显示当前硬盘分区中空间利用率最大的值 # maxdisk=`df | grep '/dev/sd' | tr -s ' ' | sort -nr -t' ' -k5 | head -1 | cut -d' ' -f1` maxused=`df | grep '/dev/sd' | tr -s ' ' | sort -nr -t' ' -k5 | head -1 | cut -d' ' -f5` echo '分区利用率最大值为:'$maxused unset maxdisk unset maxused
4、编写脚本/root/bin/links.sh,显示正连接本主机的每个远程主机的IPv4地址和连接数,并按连接数从大到小排序
#!/usr/bin/env bash
#
# Author: jacky18676887374@aliyun.com
# date: 20160812-08:27:18
# Vervion: 0.0.1
# Description:
# Synopsis:
#
#编写脚本/root/bin/links.sh,显示正连接本主机的每个远程主机的IPv4地址和连接数,并按连接数从大到小排序。
echo -e "远程主机连接统计为:\n\t连接数\t远程主机IP"
netstat -nt | tr -s ' ' | cut -d' ' -f5 | tr -cs '0-9.' '\n' | egrep '([0-9]+.){3}[0-9]+' | sort | uniq -c | sort -nr | tr -s ' ' '\t'
5、写一个脚本/root/bin/sumid.sh,计算/etc/passwd文件中的第10个用户和第20用户的ID之和
#!/usr/bin/env bash # # Author: jacky18676887374@aliyun.com # date: 20160812-14:00:37 # Vervion: 0.0.1 # Description: # Synopsis: # #写一个脚本/root/bin/sumid.sh,计算/etc/passwd文件中的第10个用户和第20个用户之和 UID1=`sed -n '10p' /etc/passwd | cut -d: -f3` UID2=`sed -n '20p' /etc/passwd | cut -d: -f3` let Sumid=$UID1+$UID2 echo -e "The 10 user ID is $UID1 ;\nthe 20 user ID is $UID2 ;\n\tthe sum of two users ID is $Sumid ." unset UID1 unset UID2 unset Sumid

6、写一个脚本/root/bin/sumspace.sh,传递两个文件路径作为参数给脚本,计算这两个文件中所有空白行之和
#!/usr/bin/env bash # # Author: jacky18676887374@aliyun.com # date: 20160812-14:43:07 # Vervion: 0.0.1 # Description: # Synopsis: # #写一个脚本/root/bin/sumspace.sh,传递两个文件路径作为参数给脚本,计算这两个文件中所有空白行之和 File1=`grep '^$' $1 | wc -l` File2=`grep '^$' $2 | wc -l` let Sumspace=$File1+$File2 echo "the sum of $1 and $2 spacelines is $Sumspace" unset File1 unset File2 unset Sumspace
![]()
7、写一个脚本/root/bin/sumfile.sh,统计/etc, /var, /usr目录中共有多少个一级子目录和文件
#!/usr/bin/env bash
#
# Author: jacky18676887374@aliyun.com
# date: 20160812-14:52:14
# Vervion: 0.0.1
# Description:
# Synopsis:
#
#写一个脚本/root/bin/sumfile.sh,统计/etc, /var, /usr目录中共有多少个一级子目录和文件
File1=/etc
File2=/var
File3=/usr
Count1=`ls -A $File1 | wc -l`
Count2=`ls -A $File2 | wc -l`
Count3=`ls -A $File3 | wc -l`
let Sumfile=${Count1}+${Count2}+${Count3}
echo -e "$File1 has $Count1 files;\n$File2 has $Count2 files;\n$File3 has $Count3 files;\n\tand total is $Sumfile "
unset File1
unset File2
unset File3
unset Count1
unset Count2
unset Count3
unset Sumfile

8、写一个脚本/root/bin/argsnum.sh,接受一个文件路径作为参数;如果参数个数小于1,则提示用户“至少应该给一个参数”,并立即退出;如果参数个数不小于1,则显示第一个参数所指向的文件中的空白行数
#!/usr/bin/env bash
#
# Author: jacky18676887374@aliyun.com
# date: 20160812-15:36:56
# Vervion: 0.0.1
# Description:
# Synopsis:
#
#写一个脚本/root/bin/argsnum.sh,接受一个文件路径作为参数;如果参数个数小于1,则提示用户“至少应该给一个参数”,并立即退出;如果参数个数不小于1,则显示第一个参数所指向的文件中的空白行数
[[ $# < 1 ]] && echo '至少应该给一个参数(文件)' || (ArgSpace=`grep '^$' $1 | wc -l` ; echo "There is ${ArgSpace1} space lines in $1")
unset ArgSpace

9、写一个脚本/root/bin/hostping.sh,接受一个主机的IPv4地址做为参数,测试是否可连通。如果能ping通,则提示用户“该IP地址可访问”;如果不可ping通,则提示用户“该IP地址不可访问”
#!/usr/bin/env bash # # Author: jacky18676887374@aliyun.com # date: 20160812-15:53:58 # Vervion: 0.0.1 # Description: # Synopsis: # #写一个脚本/root/bin/hostping.sh,接受一个主机的IPv4地址做为参数,测试是否可连通。如果能ping通,则提示用户“该IP地址可访问”;如果不可ping通,则提示用户“该IP地址不可访问” ping -c 2 $1 &> /dev/null [[ $? == 0 ]] && echo '该IP地址可访问' || echo '该IP地址不可访问'

10、chmod -rw /tmp/file1,编写脚本/root/bin/per.sh,判断当前用户对/tmp/fiile1文件是否不可读且不可写
#!/usr/bin/env bash # # Author: root # date: 20160812-17:44:13 # Vervion: 0.0.1 # Description: # Synopsis: # #chmod -rw /tmp/file1,编写脚本/root/bin/per.sh,判断当前用户对/tmp/fiile1文件是否不可读且不可写 File=/tmp/file1 [ ! -r $File -a ! -w $File ] && echo '用户对文件不可读且不可写' || echo '用户对文件可读或可写,或可读写' unset File

11、编写脚本/root/bin/nologin.sh和login.sh,实现禁止和充许普通用户登录系统。
cat nologin.sh #!/usr/bin/env bash # # Author: jacky18676887374@aliyun.com # date: 20160812-17:51:10 # Vervion: 0.0.1 # Description: # Synopsis: # #编写脚本/root/bin/nologin.sh,实现禁止普通用户登录系统 #判断/etc/nologin文件是否存在,如文件存在,则提示文件存在;如果不存在,则提示文件不存在,提示创建文件;无论文件是否存在都可以touch,完成后都输出普通用户无法登录。 # File=/etc/nologin [ -f $File ] && echo "$File already exist." || echo -e "$File does not exist.\ncreating $File..." touch /etc/nologin echo "Linux ordinary users are not allowed to log on" unset File

#!/usr/bin/env bash # # Author: jacky18676887374@aliyun.com # date: 20160812-17:51:10 # Vervion: 0.0.1 # Description: # Synopsis: # #编写脚本/root/bin/login.sh,实现允许普通用户登录系统 #判断/etc/nologin文件是否存在,如文件不存在,提示文件不存在;如文件存在,则进行删除文件,提示文件已删除。完成后都输出普通用户允许登录。 # File=/etc/nologin [ ! -f $File ] && echo -e "$File does not exist." || (rm -rf $File ; echo "$File deleted") echo "Linux ordinary users are allowed to log on" unset File

12、写一个脚本/root/bin/hostping.sh,接受一个主机的IPv4地址做为参数,先判断是否合格IP,否,提示IP格式不合法并退出,是,测试是否可连通。如果能ping通,则提示用户“该IP地址可访问”;如果不可ping通,则提示用户“该IP地址不可访问”
cat hostping3.sh
#!/usr/bin/env bash
#
# Author: jacky18676887374@aliyun.com
# date: 20160812-15:53:58
# Vervion: 0.0.1
# Description:
# Synopsis:
#
#写一个脚本/root/bin/hostping.sh,接受一个主机的IPv4地址做为参数,先判断是否合格IP,否,提示IP格式不合法并退出,是,测试是否可连通。如果能ping通,则提示用户“该IP地址可访问”;如果不可ping通,则提示用户“该IP地址不可访问”
echo $1 | egrep '\<([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){2}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\>' &>/dev/null
[[ $? -ne 0 ]] && echo "$1 is invalid" && exit
ping -c 3 -W 2 $1 &> /dev/null
[[ $? -eq 0 ]] && echo '该IP地址可访问' || echo '该IP地址不可访问'

13、计算1+2+3+…+100的值
[root@IP70-CentOS7 ~]# >>echo {1..100} | tr ' ' '+' | bc #方法一
[root@IP70-CentOS7 ~]# >>echo $(seq 1 100) | tr ' ' '+' | bc #方法二

14、计算从脚本第一参数A开始,到第二个参数B的所有数字的总和,判断B是否大于A,否提示错误并退出,是则计算之
#!/usr/bin/env bash # # Author: jacky18676887374@aliyun.com # date: 20160813-18:57:05 # Vervion: 0.0.1 # Description: # Synopsis: # #计算从脚本第一参数A开始,到第二个参数B的所有数字的总和,判断B是否大于A,否提示错误并退出,是则计算之 # [[ $1 -gt $2 ]] && echo "Error: $1 greater than $2" && exit echo $(seq $1 $2) | tr ' ' '+' | bc

原创文章,作者:昭其,如若转载,请注明出处:http://www.178linux.com/34043


评论列表(1条)
有自己的思考与分析,通过自己的实践来验证自己的想法,很有思考性,再接再厉。