命令行参数

$0 表示程序名。
$1 至 \$9则是位置参数。
$# 表示参数的个数。
$* 将所有参数当做一个整体来引用
$@ 把每个参数作为一个字符串返回,可以使用for循环来遍历
$? 最近一个执行的命令的退出状态。0表示执行成功
$_ 上一个命令的最后一个参数。使用快捷键 ESC+. 也是这个效果

位置参数

位置参数不止9个,更多的参数也是一样支持的。只是要使用${10}这样的形式引用。
$1 和 ${1}的效果是一样的。
不用花括号的话,$10 会被认为是 $1 和一个字符 0。

带空格的参数值
每个参数都是用空格分隔的。要在参数值中包含空格,必须要使用引号(单引号或双引号都可)。

将文本字符串作为参数传递时,引号并非数据的一部分。它们只是表明数据的起止位置。

获取脚本名

$0 表示脚本名,但是不同的调用方法返回的结果也是不同的。下面的脚本就是简单的打印$0的值:

$ cat filename.sh
#!/bin/bash
echo $
$ ./ filename.sh
-bash: ./: 是一个目录
$ cat filename.sh
#!/bin/bash
echo $
$ ./filename.sh
./filename.sh
$ bash filename.sh
filename.sh
$ bash /root/filename.sh
/root/filename.sh
$

使用 basename 命令
如果要使用脚本名称来进行判断,可以先用命令 basename 把路径的信息给过滤掉。命令的效果如下:

$ basename /var/log/messages
messages
$

所以上面的脚本可以修改成这样:

$ cat filename.sh
#!/bin/bash
echo $(basename $)
$ ./filename.sh
filename.sh
$ bash filename.sh
filename.sh
$ bash /root/filename.sh
filename.sh
$

测试参数

在脚本中使用参数要确保参数存在,否则运行时有可能会报错:

$ cat add.sh
#!/bin/bash
echo $ + $ = $[ $ + $ ]
$ ./add.sh
+ =
$ ./add.sh
./add.sh:行2: + : 语法错误: 期待操作数 (错误符号是 "+ ")
$

如果只是当做字符串引用,也不会报错。没有传参的参数默认都是空:

$ cat hello.sh
#!/bin/bash
echo Hello $ $.
$ ./hello.sh Tom Jerry
Hello Tom Jerry.
$ ./hello.sh Jerry
Hello Jerry .
$ ./hello.sh
Hello .
$

判断参数是否存在
在 shell 中利用 -n 来判定字符串非空,-z 则正好相反,空即是真。上面已经测试过了,未定义的参数默认是空:

$ cat hello.sh
#!/bin/bash
if [ -n "$1" ]
then
echo Hello $.
else
echo Hello Nobody.
fi
$ ./hello.sh Tom
Hello Tom.
$ ./hello.sh
Hello Nobody.
$

这里的判断的 \$1 要加上双引号,否则会被认为是字符串。一个字符串当然非空,所以结果会永远为真。

判断参数的个数
上面的例子的脚本也可以通过判断参数数量是否大于0来实现:

$ cat hello.sh
#!/bin/bash
echo 参数数量: $#
if [ $# -gt ]
then
echo Hello $.
else
echo Hello Nobody.
fi
$ ./hello.sh
参数数量:
Hello Nobody.
$ ./hello.sh Tom
参数数量:
Hello Tom.
$ ./hello.sh Tom Jerry
参数数量:
Hello Tom.
$

这里 -gt 比较的是前后两个数字(INT),所以\$#是不加引号的。
用这种方法也能判断参数是否存在。两种方法,效果一样,不同的书上都看到有人使用。

这里是一样加法的例子,必须要传入2个参数:

$ cat add.sh
#!/bin/bash
if [ $# -eq ]
then
echo $ + $ = $[ $ + $ ]
else
echo 需要参数: , 实际参数: $#.
fi
$ ./add.sh
+ =
$ ./add.sh
需要参数: , 实际参数: .
$ ./add.sh
需要参数: , 实际参数: .
$

如果要表示不相等,就是 if [ $# -ne 2 ]

获取最后一个参数
这是一个使用 $# 的小技巧。使用${$#}似乎就是参数的最后一个变量了。
但是其实不然,花括号里不能这样用$,这里要把里面的换成感叹号:

$ cat hello.sh
#!/bin/bash
if [ $# -gt ]
then
echo Hello ${!#}.
else
echo Hello Nobody.
fi
$ ./hello.sh Tom Jerry
Hello Jerry.
$

如果没有任何命令行参数,那么就是返回$0,也就是脚本名。

上面感叹号的问题,效果是引用变量的值而不是变量自身。类似于指针的取值。把#号换成一个有名字的变量来说明比较直观:

$ cat parameter.sh
#!/bin/bash
paramater=key
key=value
echo "${paramater}"
echo "${!paramater}"
echo "${key}"
$ ./parameter.sh
key
value
value
$

不加感叹号,就是直接去该变量的值。加上感叹号,就是取变量值所对应的变量名的那个变量的值。和php中$$有点类似

获取所有参数

$* 和 $@ 都是表示所有的字符串,但是在遍历的时候会有区别:

$ cat all.sh
#!/bin/bash
echo '$* 的效果:'
count=
for i in "$*"
do
echo $count: $i
count=$[ $count + ]
done
echo '$@ 的效果:'
count=
for i in "$@"
do
echo $count: $i
count=$[ $count + ]
done
$ ./all.sh Oliver Barry Kara Sara Kane
$* 的效果:
: Oliver Barry Kara Sara Kane
$@ 的效果:
: Oliver
: Barry
: Kara
: Sara
: Kane
$

$*就一个整体的值,无法遍历。要遍历每一个变量要使用$@。这里的双引号很重要。

不加引号的话,就是把 \$* 和 \$@ 的内容(变量解析后就是多个词)传递给for循环遍历,这样两个参数的效果是一样的。和直接传不加引号的字符串的效果一样。
加上引号,引号里的内容就是一个整体。如果是\$*,这个整体里的所有内容还是一个词,不会拆。如果是\$@,这个整体里会按空格拆分成多个词。
下面是演示的效果:

$ cat all2.sh
#!/bin/bash
echo '$* 不加引号的效果:'
count=
for i in $*
do
echo $count: $i
count=$[ $count + ]
done
echo '$@ 不加引号的效果:'
count=
for i in $@
do
echo $count: $i
count=$[ $count + ]
done
echo '直接遍历不加引号的字符的效果:'
count=
for i in Oliver Barry Kara Sara Kane
do
echo $count: $i
count=$[ $count + ]
done
echo '加引号遍历的效果:'
count=
for i in "Oliver Barry Kara Sara Kane"
do
echo $count: $i
count=$[ $count + ]
done
$ ./all2.sh Oliver Barry Kara Sara Kane
$* 不加引号的效果:
: Oliver
: Barry
: Kara
: Sara
: Kane
$@ 不加引号的效果:
: Oliver
: Barry
: Kara
: Sara
: Kane
直接遍历不加引号的字符的效果:
: Oliver
: Barry
: Kara
: Sara
: Kane
加引号遍历的效果:
: Oliver Barry Kara Sara Kane
$

强调:特殊参数$@一定要用在双引号内,效果是每个参数都扩展为分隔的单词。在使用for循环遍历的时候会体现出效果。

移动变量 shift

shift 命令能够用来操作命令行参数。默认情况下将每个参数向左移动一个位置。被移出的参数就被丢弃了,无法恢复。
先掌握这个命令的使用,使用这个命令可以方便地解析命令行参数。

使用示例

下面是一个简单的示例:

$ cat pop.sh
#!/bin/bash
count=
while [ -n "$1" ]
# while [ $# -ne ]
do
echo "$count: $1"
count=$[ $count + ]
shift
done
$ ./pop.sh Oliver Barry Kara Sara Kane
: Oliver
: Barry
: Kara
: Sara
: Kane
$

这里有2中判断方法来判断是否还有参数,效果是一样的。

移动多个位置

带参数执行shift,指明要移动几个位置就可以了:

$ cat pop.sh
#!/bin/bash
count=
# while [ -n "$1" ]
while [ $# -ne ]
do
if [ -n "$2" ]
then
echo "$count: $1, $2"
shift
else
echo "$count: $1"
shift
fi
count=$[ $count + ]
done
$ ./pop.sh Oliver Barry Kara Sara Kane
: Oliver, Barry
: Kara, Sara
: Kane
$

简单修改下上面的脚本,一次输出2个参数,然后移动2个位置。

处理选项

当shell脚本需要多个命令行参数时,在调用脚本的时候就必须将所有参数按固定的顺序。
或者还可以使用选项来指定参数的值。

case 配合 shift

这个例子里有带值的选项也有不带值的选项:

$ cat format.sh
#!/bin/bash
prefix="" # 前缀
base="test" # 默认字符串
suffix="" # 后缀
upper=off # 是否大写
# 解析命令行参数
while [ -n "$1" ]
do
case "$1" in
-a) suffix="$2"
shift ;;
-b) prefix="$2"
shift ;;
-s) base="$2"
shift ;;
-u) upper=on ;;
*) echo "$1 is not an option"
exit ;; # 发现未知参数,直接退出
esac
shift
done
# 添加前缀和后缀
output="${prefix:+${prefix}_}${base}${suffix:+_${suffix}}"
# 判断是否要全大写输出
if [ $upper = on ]
then
output=${output^^}
fi
# 输出结果
echo "$output"
$ ./format.sh -a after
test_after
$ ./format.sh -s hello -b befor
befor_hello
$ ./format.sh -s hello -u -a after -b befor
BEFOR_HELLO_AFTER
$ ./format.sh -s hello -u -a after -b befor -l
-l is not an option
$

case语句找到一个选项就处理一个选项。如果还需要在命令行提供其他参数,可以在通用情况的处理部分中处理。而这里因为不需要提供任何参数,凡是解析不正确的就报告错误并退出(exit 1)。

能解析参数的版本
这个版本匹配所有的参数进行格式化输出:

$ cat format.sh
#!/bin/bash
prefix="" # 前缀
base="test" # 默认字符串
suffix="" # 后缀
upper=off # 是否大写
# 显示声明一下这是个数组变量,其实没有必要
declare -a names # 需要格式化输出的所有原始字符串
# 解析命令行参数
while [ -n "$1" ]
do
case "$1" in
-a) suffix="$2"
shift ;;
-b) prefix="$2"
shift ;;
-s) base="$2"
shift ;;
-u) upper=on ;;
*) names=("${names[@]}" "$1") ;;
esac
shift
done
names[]=${names[]:-$base}
for name in "${names[@]}"
do
# 添加前缀和后缀
output="${prefix:+${prefix}_}${name}${suffix:+_${suffix}}"
# 判断是否要全大写输出
if [ $upper = on ]
then
output=${output^^}
fi
# 输出结果
echo "$output"
done
$
$ ./format.sh -a after -b befor -u value1 value2 value3
BEFOR_VALUE1_AFTER
BEFOR_VALUE2_AFTER
BEFOR_VALUE3_AFTER
$ ./format.sh -a after after1 -b befor befor1 -u value1 value2 value3
BEFOR_AFTER1_AFTER
BEFOR_BEFOR1_AFTER
BEFOR_VALUE1_AFTER
BEFOR_VALUE2_AFTER
BEFOR_VALUE3_AFTER
$ ./format.sh -a after after1 -b befor befor1 -u -v value1 value2 value3
BEFOR_AFTER1_AFTER
BEFOR_BEFOR1_AFTER
BEFOR_-V_AFTER
BEFOR_VALUE1_AFTER
BEFOR_VALUE2_AFTER
BEFOR_VALUE3_AFTER
$

看最后的两项的结果,提供的命令行参数有问题,但是程序无法发现。
倒数第二项可以认为提供的参数是对的,但是选项和参数交替出现。
而最后一项提供了一个错误的选项,但是无法识别出来。
解决这个问题,需要更加规范的方法来分离参数和选项。下一小节的内容。

数组带空格的问题
数组添加元素有很多方法,这里是一种重新创建数组的做法:

array_name=("${array_name[@]}" value1 ... valueN)

可以一次添加多个元素,如果字符串包含空格,就要加上引号。

和命令行参数的\$@与\$*一样,数组所有的元素也有这两个类似的符号。最严谨的方法是使用 "${names[@]}" 使用带双引号的@。
添加元素和取出元素的时候都要注意,否则存在带空格的元素的时候就会破坏数组原本的元素分隔。
添加元素这里使用:

names=("${names[@]}" "$1")

不单是数组里的元素,被添加的元素也要加上双引号,否则如果有空格,就会按多个元素被添加进数组。

遍历元素使用:

for name in "${names[@]}"

只有添加的时候正确了,才能正确的遍历。然后遍历的时候也要保证正确。
验证效果:

$ ./format.sh -a after -b befor -u value1 "value2 value3" value4
BEFOR_VALUE1_AFTER
BEFOR_VALUE2 VALUE3_AFTER
BEFOR_VALUE4_AFTER
$

完美。

分离参数和选项

这里的参数就是命令行参数中除了定义的选项之外,其他额外的参数。要同时处理参数和选项,就要用特殊字符(双破折线--)将二者分开。双破折线表明选项列表结束,双破折线后面的都是参数。基于这个逻辑,只要在case语句中加一项判断就行了。
把上面的脚本做一些修改:

$ cat format.sh
#!/bin/bash
prefix="" # 前缀
base="test" # 默认字符串
suffix="" # 后缀
upper=off # 是否大写
# 显示声明一下这是个数组变量,其实没有必要
declare -a names # 需要格式化输出的所有原始字符串
# 解析选项
while [ -n "$1" ]
do
case "$1" in
-a) suffix="$2"
shift ;;
-b) prefix="$2"
shift ;;
-s) base="$2"
shift ;;
-u) upper=on ;;
--) shift
break ;;
*) echo "$1 is not an option"
exit ;; # 发现未知参数,直接退出
esac
shift
done
# 解析参数
while [ -n "$1" ]
do
names=("${names[@]}" "$1")
shift
done
names[]=${names[]:-$base}
for name in "${names[@]}"
do
# 添加前缀和后缀
output="${prefix:+${prefix}_}${name}${suffix:+_${suffix}}"
# 判断是否要全大写输出
if [ $upper = on ]
then
output=${output^^}
fi
# 输出结果
echo "$output"
done
$

基于这个版本,在使用的时候,需要先输入选项,然后使用双破折线隔开,再输入参数。当脚本遇到双破折线时,它会停止处理选项,并将剩下的参数都当作参数:

$ ./format.sh -a after -b befor -u value1 value2 value3
value1 is not an option
$ ./format.sh -a after -b befor -u -- value1 value2 value3
BEFOR_VALUE1_AFTER
BEFOR_VALUE2_AFTER
BEFOR_VALUE3_AFTER
$ ./format.sh -a after -b befor -v -u -- value1 value2 value3
-v is not an option
$

第一次没有使用双破折线,所以报错。
第二次正确的用双破折号分隔了参数和选项。
第三次在选项部分出现了未定义的选项,也能发现错误。

小结
这一小节的内容也是为下面的getopt命令做铺垫。getopt就是可以帮我们完成命令行参数的解析,返回一个用双破折线隔开选项和参数的规整的参数列表。
另外这里还不支持选项合并:

$ ls -al

这些问题,用getopt都能解决,而且还支持长选项。

转自https://blog.51cto.com/steed/2443313

shell 命令行参数(基本)的更多相关文章

  1. shell 命令行参数(getopt和getopts)

    getopt 命令 使用getopt命令,可以解析任何命令行选项和参数,但是用法比较复杂.getopt的命令用法如下: $ getopt --help 用法: getopt optstring par ...

  2. Shell特殊变量:Shell $0, $#, $*, $@, $?, $$和命令行参数

    特殊变量列表 变量 含义 $0 当前脚本的文件名 $n 传递给脚本或函数的参数.n 是一个数字,表示第几个参数.例如,第一个参数是$1,第二个参数是$2. $# 传递给脚本或函数的参数个数. $* 传 ...

  3. Linux Shell 05 位置变量(命令行参数)

    在Linux shell 脚本中可能会用到一些命令行参数,常见如下: $0:脚本名称 $#:执行脚本时传入的参数个数,不包括脚本名称 $@:所有参数 $*:所有参数 $1...$9:第1个参数.... ...

  4. 【Shell脚本学习8】Shell特殊变量:Shell $0, $#, $*, $@, $?, $$和命令行参数

    前面已经讲到,变量名只能包含数字.字母和下划线,因为某些包含其他字符的变量有特殊含义,这样的变量被称为特殊变量. 例如,$ 表示当前Shell进程的ID,即pid,看下面的代码: $echo $$ 运 ...

  5. 【转】shell 教程——07 Shell特殊变量:Shell $0, $#, $*, $@, $?, $$和命令行参数

    前面已经讲到,变量名只能包含数字.字母和下划线,因为某些包含其他字符的变量有特殊含义,这样的变量被称为特殊变量. 例如,$ 表示当前Shell进程的ID,即pid,看下面的代码: $echo $$ 运 ...

  6. kettle文件自动化部署(shell脚本执行):命令行参数传入

    shell脚本中调用kitchen 和 pan去执行,job和transformation文件.分 windows和 dos系统两种. 举个简单的小例子 shell脚本: export JAVA_HO ...

  7. Shell特殊变量:$0 $# $* $@ $? $$和命令行参数

    一.特殊变量表 变量 含义 $0 当前脚本的文件名 $n 传递给脚本或函数的参数.n 是一个数字,表示第几个参数.例如,第一个参数是$1,第二个参数是$2. $# 传递给脚本或函数的参数个数. $* ...

  8. linux bash Shell特殊变量:Shell $0, $#, $*, $@, $?, $$和命令行参数

    在linux下配置shell参数说明 前面已经讲到,变量名只能包含数字.字母和下划线,因为某些包含其他字符的变量有特殊含义,这样的变量被称为特殊变量. 例如,$ 表示当前Shell进程的ID,即pid ...

  9. Shell 参数(2) --解析命令行参数工具:getopts/getopt

    getopt 与 getopts 都是 Bash 中用来获取与分析命令行参数的工具,常用在 Shell 脚本中被用来分析脚本参数. 两者的比较 (1)getopts 是 Shell 内建命令,geto ...

随机推荐

  1. CENTOS7-JAVA模拟CPU占用高及排查( 转)

    环境 centos7 1核2GB Java8 模拟cpu占用高 新建一个名为jvm-learn的springboot项目 模拟代码如下 import org.springframework.boot. ...

  2. ajax往后台传值的一些方式

    $('#del1').click(function () { $.ajax({ url: 'http://localhost:8089/test1', data: {a: 1, b: 2}, type ...

  3. 英语rubyspinel红尖晶石rubyspinel单词

    红尖晶石(rubyspinel或Red spinel)其红色是因含铬而致^像红宝石和红色石榴子石一样,红 尖晶石也曾被叫作红玉,这就造成了红色宝石的混乱,因为世界上一些最大的著名“红宝 石”,如英国王 ...

  4. Win10 系统删除文件时提示文件不存在

    Win10系统使用一段时间后用户都会定期进行删除清理系统垃圾,减少系统盘的容量占用,但在删除的过程中许多用户都遇到无法删除的情况,这一次系统提示"文件不存在",这该怎么解决?我们可 ...

  5. Linux之ubuntu下载

    (转载) ubuntu 16.04国内下载地址: 中科大源 http://mirrors.ustc.edu.cn/ubuntu-releases/16.04/ 阿里云开源镜像站 http://mirr ...

  6. CentOS6.7搭建部署DHCP服务 (详解主配置文件)

    DHCP服务 dhcp:动态主机配置协议.从bootp演变而来,引进了租约.续租功能,成为了现在的DHCP. 需要就分配,不需要就回收. 工作过程: 1.当获得地址是,有租约期限,当你关机时,IP地址 ...

  7. ztree的添加、修改、删除及前后台交互

    一.引入资源下载并引入ztree的相关js,css和img等.http://www.treejs.cn/v3/api.php ztree的核心代码jquery.ztree.core.jsztree关于 ...

  8. 基于gin框架搭建的一个简单的web服务

    刚把go编程基础知识学习完了,学习的时间很短,可能还有的没有完全吸收.不过还是在项目中发现知识,然后在去回顾已学的知识,现在利用gin这个web框架做一个简单的CRUD操作. 1.Go Web框架的技 ...

  9. 用JAVA实现找出输入字符串中的出现次数最多的字符及其次数;

    //通过Map 类实现,通过键值对的方式,可以将输入的字符串的每一个字符,作为键,每个字符出现的次数作为值:如下: public class Find { public static void mai ...

  10. js--获取和设置css属性

    在这一章我们讲述一下如何通过js来操作css中的属性 1,首先,我们想获取元素的一些属性.例如innerHTML,value等值时,我们可以 var object=document.getELemen ...