小学做试卷的网站,泉州电商网站建设,免费做网站站标,嘉兴免费网站制作文章目录 使用getopt处理参数1. shift 命令1.1 删除一个参数1.2 删除多个参数1.3 多次执行 shift 参数1.4 参数解析示例1.5 优化处理1.6 问题处理 2. getopt 命令2.1 常用参数及示例2.2 脚本参数优化示例2.3 参数校验 3. 示例展示4. eval 命令4.1 示例示例 1示例 2示例 3示例 4… 文章目录 使用getopt处理参数1. shift 命令1.1 删除一个参数1.2 删除多个参数1.3 多次执行 shift 参数1.4 参数解析示例1.5 优化处理1.6 问题处理 2. getopt 命令2.1 常用参数及示例2.2 脚本参数优化示例2.3 参数校验 3. 示例展示4. eval 命令4.1 示例示例 1示例 2示例 3示例 4示例 5 使用getopt处理参数 getopt是用来解析整理传入shell的命令行参数的命令 参考地址B站传送门 1. shift 命令 shift 的作用就是从头部删除参数它可以在后面各跟一个数字参数表示删除几个参数 示例 shift 2 表示删除两个参数 常见参数使用可以使用 $ 输入所有参数 [root105 dongxx]# cat a.sh
#!/bin/bash# 输入所有参数
echo $[root105 dongxx]# sh a.sh a b c
a b c1.1 删除一个参数 使用 shift 命令之后会发现第一个参数 a 没了 [root105 dongxx]# cat a.sh
#!/bin/bashshift# 输入所有参数
echo $
[root105 dongxx]# sh a.sh a b c
b c1.2 删除多个参数 使用 shift 2 删除两个参数 [root105 dongxx]# cat a.sh
#!/bin/bashshift 2# 输入所有参数
echo $
[root105 dongxx]# sh a.sh a b c d
c d1.3 多次执行 shift 参数 shift 参数还可以多次执行可以多次执行删除多个参数 [root105 dongxx]# cat a.sh
#!/bin/bashshift 2
shift# 输入所有参数
echo $
[root105 dongxx]# sh a.sh a b c d e
d e1.4 参数解析示例 需求处理参数 -a -name zhangsan -age 18需要分开输出为 -a -name zhangsan -age 18 [root105 dongxx]# cat a.sh
#!/bin/bash
# 参数-a -name zhangsan -age 18# -a 为第一个参数可以直接使用 $1
echo $1# 输出 $1 之后使用 shift 删除第一个参数那么后面的参数就是从 $1 开始了所以这里的 $1 和 $2 就是 -name 和 zhangsan
shift
echo $1 $2# 因为 -name 选项和它的参数 zhangsan 占了两个位置那么就需要 shift 两次了同理这里的 $1 就是 -age 选项 $2 就是 18 了
shift 2
echo $1 $2
[root105 dongxx]# sh a.sh -a -name zhangsan -age 18
-a
-name zhangsan
-age 181.5 优化处理 上述案例中没有考虑参数顺序问题如果参数顺序有不一样输出结果也就乱了所以在脚本中不仅要能挨个拿到选项和参数还要根据选项是有参还是无参来控制 shift 的数量而且也并不是每个选项参数一定要用也就是说选项参数数量是不固定的。这样我们可以使用循环来处理。 示例 [root105 dongxx]# cat a.sh
#!/bin/bash
# 参数-a -name zhangsan -age 18while true; docase $1 in-a)echo -a 选项shift;;-name)echo -name 选项参数为 $2shift 2;;-age)echo -age 选项参数为 $2shift 2;;*)echo 非法参数exit 1esac
done
[root105 dongxx]# sh a.sh -a -name zhangsan -age 18
-a 选项
-name 选项参数为 zhangsan
-age 选项参数为 18
非法参数上述脚本中有个问题在没有参数时会提示非法参数且参数为-a -name zhangsan -age 18时仍提示非法参数 [root105 dongxx]# sh a.sh
非法参数问题解析 #!/bin/bash
# 参数-a -name zhangsan -age 18while true; docase $1 in-a)echo -a 选项shift;;-name)echo -name 选项参数为 $2shift 2;;-age)echo -age 选项参数为 $2shift 2;;*)echo 非法参数exit 1esac
done# 当前脚本在匹配完成会后删除了所有参数最后 $1 匹配为空会走到最后的 * 匹配所以就会输出“非法参数”问题处理 #!/bin/bash
# 参数-a -name zhangsan -age 18# 这里我们可以使用 set 命令来处理set 命令可以用来指定一个结束标记
set -- $ --
# 参数说明
# set -- 为删除所有参数在 -- 后面可以设置自定义参数最后在添加一个 -- 作为结束标记
# 输出查看
echo \$: $while true; docase $1 in-a)echo -a 选项shift;;-name)echo -name 选项参数为 $2shift 2;;-age)echo -age 选项参数为 $2shift 2;;--)break;;*)echo 非法参数exit 1esac
done测试 [root105 dongxx]# cat a.sh
#!/bin/bash
# 参数-a -name zhangsan -age 18# 这里我们可以使用 set 命令来处理set 命令可以用来指定一个结束标记
set -- $ --
# 参数说明
# set -- 为删除所有参数在 -- 后面可以设置自定义参数最后在添加一个 -- 作为结束标记
# 输出查看
echo \$: $while true; docase $1 in-a)echo -a 选项shift;;-name)echo -name 选项参数为 $2shift 2;;-age)echo -age 选项参数为 $2shift 2;;--)break;;*)echo 非法参数exit 1esac
done
[root105 dongxx]# sh a.sh
$: --
[root105 dongxx]# sh a.sh -a -name zhangsan -age 18
$: -a -name zhangsan -age 18 --
-a 选项
-name 选项参数为 zhangsan
-age 选项参数为 181.6 问题处理 上述测试脚本参数都是约定好的但是在实际使用过程中可能会出现没有安装约定的方式传参的问题 示例-a aaa -name zhangsan -age 18那这时候参数的顺序位置就会有问题。这时我们就可以使用 getopt 来处理了 2. getopt 命令
2.1 常用参数及示例 常用命令参数 参数说明-o指定解析段格式选项-l指定要解析的长格式选项–分割真正需要解析的参数 示例 [root105 dongxx]# getopt -o a -l name:,age: -- -a --name zhangsan --age 18-a --name zhangsan --age 18 --# 参数说明-o 后跟上短格式选项-l 后跟上长格式选择对于有参数的选项需要再参数后加个冒号多个长格式选项用逗号隔开-- 后跟上真正需要解析的参数而且 getopt 要求长格式选项需要使用 -- 所以需要在选项前加上 --解析成功后会在参数最后默认加上 -- 的结束标记测试给段格式选项 -a 增加参数 aaa结果是没有参数输出因为 getopt 知道 -a 选项是无参选项所以它将跟在 a 后面的参数移动到了结束标记之后在之前的脚步中是需要 -- 则跳出循环所以结束标记之后的参数是不会被处理的 [root105 dongxx]# getopt -o a -l name:,age: -- -a aaa --name zhangsan --age 18-a --name zhangsan --age 18 -- aaa2.2 脚本参数优化示例 使用 getopt 处理一下就能解决参数位置错误导致的参数解析错乱的问题 [root105 dongxx]# cat a.sh
#!/bin/bash
# 参数-a -name zhangsan -age 18# 使用 $(getopt -o a -l name:,age: -- $) 获取 getopt 处理后的参数信息在使用 set -- 把这个结果设置后脚本的参数由于 getopt 本身就有 -- 的结束标记所以下面的 set -- $ -- 就不需要了
set -- $(getopt -o a -l name:,age: -- $)# 这里我们可以使用 set 命令来处理set 命令可以用来指定一个结束标记
# set -- $ --
# 参数说明
# set -- 为删除所有参数在 -- 后面可以设置自定义参数最后在添加一个 -- 作为结束标记
# 输出查看
echo \$: $while true; docase $1 in-a)echo -a 选项shift;;# 为了兼容 getopt 长格式的设置这里需要改成 --name--name)echo -name 选项参数为 $2shift 2;;--age)echo -age 选项参数为 $2shift 2;;--)break;;*)echo 非法参数exit 1esac
done
[root105 dongxx]# sh a.sh -a --name zhangsan --age 18
$: -a --name zhangsan --age 18 --
-a 选项
-name 选项参数为 zhangsan
-age 选项参数为 18
[root105 dongxx]# sh a.sh -a aa --name zhangsan --age 18
$: -a --name zhangsan --age 18 -- aa
-a 选项
-name 选项参数为 zhangsan
-age 选项参数为 18执行测试 2.3 参数校验 当我们正常传参时示例sh a.sh -a --name zhangsan --age 18 没有问题。如果我们将有参的选项的参数去掉 sh a.sh -a --name zhangsan --age 那么就会报错 [root105 dongxx]# sh a.sh -a --name zhangsan --age
getopt: option --age requires an argument
$: -a --name zhangsan --
-a 选项
-name 选项参数为 zhangsan问题 上述脚本中也有个问题就是报错之后仍然会继续执行。 解决 修改脚本在开头设置一个 set -e 让他需要非 0 状态吗自动退出。如果单纯只增加 set -e 命令脚本同样会继续往下执行因为在上述脚本中 getopt 是在 set -- 中执行的getopt 报错但是 set -- 是正常执行的所以结果就不是一个非 0 状态。所以需要将 getopt 提取出来单独处理。 [root105 dongxx]# cat a.sh
#!/bin/bash
# 参数-a -name zhangsan -age 18set -e# 将 getopt 提取出来赋值变量那么校验失败后set -e 就会检测到非 0 状态从而退出脚本
args$(getopt -o a -l name:,age: -- $)
set -- $args# 使用 $(getopt -o a -l name:,age: -- $) 获取 getopt 处理后的参数信息在使用 set -- 把这个结果设置后脚本的参数由于 getopt 本身就有 -- 的结束标记所以下面的 set -- $ -- 就不需要了
# set -- $(getopt -o a -l name:,age: -- $)# 这里我们可以使用 set 命令来处理set 命令可以用来指定一个结束标记
# set -- $ --
# 参数说明
# set -- 为删除所有参数在 -- 后面可以设置自定义参数最后在添加一个 -- 作为结束标记
# 输出查看
echo \$: $while true; docase $1 in-a)echo -a 选项shift;;# 为了兼容 getopt 长格式的设置这里需要改成 --name--name)echo -name 选项参数为 $2shift 2;;--age)echo -age 选项参数为 $2shift 2;;--)break;;*)echo 非法参数exit 1esac
done
[root105 dongxx]# sh a.sh -a --name zhangsan --age
getopt: option --age requires an argument3. 示例展示 命令展示示例 [root105 dongxx]# getopt -o a:bc: -l name:,age:,man -- -a 1 -b -c 2 --name zhangsan --age 18 --man-a 1 -b -c 2 --name zhangsan --age 18 --man --脚本展示示例 #!/bin/bash# 问题在没有 -o 参数时会报错这是为什么
# args$(getopt -l name:,age:,address:,user:,passwd: -- $)args$(getopt -o -a: -l name:,age:,address:,user:,passwd: -- $)if [[ $? ! 0 ]]; thenecho 请输出正确参数exit 1
fiecho args: $args
# 问题这里为什么需要使用 eval 暂时还不知道
eval set -- $args# set -- $argswhile true ;doecho \$1: $1case $1 in--name)if [[ -z $NAME ]]; thenNAME$2fishift 2;;--age)if [[ -z $AGE ]]; thenAGE$2fishift 2;;--address)if [[ -z $ADDRESS ]]; thenADDRESS$2fishift 2;;--user)if [[ -z $USER ]]; thenUSER$2fishift 2;;--passwd)if [[ -z $PASSWD ]]; thenPASSWD$2fishift 2;;--)break;;*)echo 参数错误请检查exit 1;;esac
doneecho name: $NAME , age: $AGE , address: $ADDRESS , user: $USER , passwd: $PASSWD4. eval 命令 参考地址1 参考地址2 eval内置命令 功能当Shell程序执行到eval语句的时候Shell读入参数args并将它们组合成一个新的命令然后执行。也就是重新运算求出参数的内容。eval可以读取一连串的参数然后依据参数本身的特性来执行。参数不限数目彼此之间用分号分开。 eval会对后面的命令进行两遍的扫描如果第一遍扫描后命令是普通命令则执行此命令如果命令中含有变量的间接引用则保证间接引用的语义。也就是说eval语句将会首先扫描命令行进行所有的置换然后再进行该命令。因此eval命令适合用于那些一次扫描无法实现其功能的变量。 eval执行分两个步骤 第一步执行变量的替换。 第二步执行替换后的命令 4.1 示例
示例 1
[root105 1]# cat a.sh
#!/bin/bashecho 111 \$$#
echo -e \n
echo
echo -e \n
eval echo 2222 \$$#
[root105 1]# sh a.sh aa bb
111 $22222 bb脚本说明 \$$#$# 是表示传参个数\$ 表示转义显示为普通字符 $ 所以第一次输出 \$$# 只进行了第一步的变量替换 结果为 $2 使用 eval 之后则进行了两次扫描第一次是 $# 变量的替换结果为 $2 然后再执行替换后的命令 $2则结果显示 $2 的值 bb 如果我们知道参数的个数输入两个参数 aa bb我们可以使用 $2 来查看最后一个参数 bb。但是如果我们不知道参数个数还想查看最后一个参数怎么办呢我们想到 $#传给Shell脚本的个数echo $# 显示的其实是参数个数而使用 eval echo $$# 才显示最后一个参数。和上述示例一样。
示例 2
[root105 1]# cat test
Hello World
[root105 1]# aacat test
[root105 1]# echo $aa
cat test
[root105 1]# eval $aa
Hello World脚本说明 eval 命令对后面的命令进行了两次扫描第一次将 $aa 替换为 cat test第二次执行 cat test。这些需要进行两次扫描的变量有时也称为复杂变量。不过这些变量并不复杂。 示例 3 在 file 文件中有两列数据第一列对应 KEY 第二列对应 VALUE 使用 eval 命令将 KEY 和 VALUE 的值对应起来从文件中读取。 [root105 1]# cat file
NAME chang
AGE 28
SEX nan
[root105 1]# cat a.sh
#!/bin/bash
while read KEY VALUE
doeval ${KEY}${VALUE}
done file
echo NAME: $NAME, AGE: $AGE, SEX: $SEX
[root105 1]# sh a.sh
NAME: chang, AGE: 28, SEX: nan
[root105 1]#
[root105 1]# sh -x a.sh read KEY VALUEeval NAMEchangNAMEchangread KEY VALUEeval AGE28AGE28read KEY VALUEeval SEXnanSEXnanread KEY VALUEecho NAME: chang, AGE: 28, SEX: nan
NAME: chang, AGE: 28, SEX: nan脚本说明 eval ${KEY}${VALUE} 中 eval 第一次扫描获取变量 ${KEY}${VALUE} 的值第二次进行赋值操作 示例 4
[root105 1]# cat a.shEOF#!/bin/bashx100yxeval echo \$$yeval $y50echo $xeval echo \$$yEOF
[root105 1]# cat a.sh
#!/bin/bash
x100
yx
eval echo \$$y
eval $y50
echo $x
eval echo \$$y
[root105 1]#
[root105 1]# sh a.sh
100
50
50上面例子中的eval echo \$$y首先被读取然后被执行在读取的过程中$y会被替换成x所以读取的结果是echo $x执行echo $x的输出就是打印了变量x的值。同理eval $y50会被解析成x50然后执行x50的结果就是为变量x赋值。 示例 5 执行复杂的字符串形式的命令 [root105 1]# cat a.sh
#!/bin/bash
dirpath/root/1
simple_cmdls -l $dirpath
complex_cmdls -l $dirpath | awk -F {print \$9}
echo
echo Simple Cmd
echo
eval $simple_cmd
echo -----------------------------------
$simple_cmd
echo
echo Complex Cmd
echo
eval $complex_cmd
echo -----------------------------------
$complex_cmd
[root105 1]#
[root105 1]# sh a.sh Simple Cmdtotal 8
-rw-r--r-- 1 root root 467 Jul 11 21:28 a.sh
-rw-r--r-- 1 root root 5 Jul 11 21:28 test
-----------------------------------
total 8
-rw-r--r-- 1 root root 467 Jul 11 21:28 a.sh
-rw-r--r-- 1 root root 5 Jul 11 21:28 testComplex Cmd
a.sh
test
-----------------------------------
ls: cannot access |: No such file or directory
ls: cannot access awk: No such file or directory
ls: cannot access : No such file or directory
ls: cannot access : No such file or directory
ls: cannot access {print: No such file or directory
ls: cannot access $9}: No such file or directory
/root/1:
total 8
-rw-r--r-- 1 root root 467 Jul 11 21:28 a.sh
-rw-r--r-- 1 root root 5 Jul 11 21:28 test可以看到在执行$simple_cmd时是否使用eval的效果是相同的。但是当我们执行一个稍微复杂一点(比如包含管道(Pipe))的字符串形式的命令时如果不使用eval执行会报错