Shell编程 详解特殊变量之位置参数

今天我们来探讨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个参数,运行之:

wKioL1euxpzATMQ_AAA1W86YR2Y246.png

由实例我们可以得出初步结论:

  • 0: shell脚本的名字;

  • NN是1开始的正整数shell脚本的第N个位置参数,当N是个位数数字,可以表示为$N;当N非个位数时候,需要 { }  ,即${N} ;

  • #:shell脚本的参数的个数,以十进制计数;

  • *:参数列表

  • @:参数列表

对于前面的三个很好理解,但是*和@都是参数列表,难道说*=@吗?第一反应就是:这不可能,一定是个坑。

到网上查找资料,在Chet Ramey , Brian Fox 著 邵加超(Jerry Fleming)译注的《BASH中文文档》看到说明介绍:


wKiom1eux17jQvJEAAHPt7--rbg268.png


真对不起我的语文老师,几遍下来,依然觉得很生涩,似懂非懂。只能动手结合实例理解:

#!/usr/bin/env bash
#
#测试脚本的位置参数: @ 和 *
#
echo 'This is $@ :>>>|'$@
echo 'This is $* :>>>|'$*

wKioL1euy4yB2pxHAAARpzT4GXc634.png

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

看来要出大招了j_0061.gif


wKioL1euzdHi4PvgAAEwYL5o5c8922.png



思路:把参数列表传递给for循环的i变量,然后打印$i,

一言不合,说干就干j_0066.gif

#!/usr/bin/env bash
#
#测试脚本的位置参数: @ 和 *
#
echo 'begin testing $@......'
for i in $@
do
    echo $i
done
echo 'begin testing $*......'
for i in $*
do
    echo $i
done

wKioL1eu0Q7gtPyiAAAblavWOs8492.png

什么情况…还是一样的,难道我的理解错了?再来:

#!/usr/bin/env bash
#
#测试脚本的位置参数: @ 和 *
#
echo 'begin testing $@......'
for i in "$@"
do
    echo $i
done
echo 'begin testing $*......'
for i in "$*"
do
    echo $i
done

wKioL1eu0iSA_AFVAAAX39BIBpo423.png

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

wKiom1eu01CjdsEGAABFEE8b9_8724.png

由此可以得出最后结论

  • 0: shell脚本的名字;

  • NN是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命令展开

wKiom1eu38HyVXq0AAAkjr8vFZ4487.png

看来10万是小意思嘛…再来个0,一百万j_0061.gif

wKioL1eu4D-yXr3RAAAVB_iOPc0218.png

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

wKioL1eu4ZWxV4UHAACS7ewXWgk076.png

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

wKioL1eu5bfj1atqAACrilBIzaY610.png

大功告成j_0057.gif看来shell脚本最多支持314590个参数。




习题作业:

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

1470985652723914.png

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

1470985876490258.png

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

1470985977411200.png

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'

1470986088490304.png

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

1470986400397747.png

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

1470986609507552.png

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

1470987858158626.png

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

1470988296753959.png

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地址不可访问'

1470988872262963.png

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

Image 20160812162305.png

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

1470992214521829.png

#!/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

1470993408732069.png

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地址不可访问'

1470999117313086.png

13、计算1+2+3+…+100的值

[root@IP70-CentOS7 ~]# >>echo {1..100} | tr ' ' '+' | bc    #方法一
[root@IP70-CentOS7 ~]# >>echo $(seq 1 100) | tr ' ' '+' | bc    #方法二

1471000900679856.png

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

1471000425129395.png

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

(0)
昭其昭其
上一篇 2016-08-15
下一篇 2016-08-15

相关推荐

评论列表(1条)

  • 马哥教育
    马哥教育 2016-08-16 17:02

    有自己的思考与分析,通过自己的实践来验证自己的想法,很有思考性,再接再厉。