写过一些shell的人都知道$0
, $1
, $2
的含义,$0
指文件名本身,$1
表示第1个参数,$2
表示第二个参数。使用以下脚本验证
#!/usr/bin/env bash
# filename t.sh
echo '$0: ' $0
echo '$1: ' $1
echo '$2: ' $2
执行该脚本,./t.sh a b
或者bash t.sh a b
$0: ./t.sh
$1: a
$2: b
这应该在大家的正常认知范围内。
但是当通过sh -c
的方式执行脚本时,$0
就不再表示文件名本身了,-c
参数是告诉sh
命令使用字符串执行shell,此时,$0
表示脚本的第1一个参数,$2
表示脚本的第2个参数。
测试命令如下
sh -c 'echo $0 $1' a b
其输出为
a b
这个规则在sh的文档有说明
($0) Expands to the name of the shell or shell script. This is set at shell initialization. If Bash is invoked with a file of commands (see Shell Scripts), $0 is set to the name of that file. * If Bash is started with the -c option (see Invoking Bash), then $0 is set to the first argument after the string to be executed, if one is present. Otherwise, it is set to the filename used to invoke Bash, as given by argument zero.
这样的设计其实很有道理,因为通过-c
执行命令时是没有对应的文件的。
这个$0
的问题是在一段python代码中发现的,当时十分不理解代码含义,弄明白了$0
的问题才看懂代码。代码片段如下
subprocess.Popen([cmd + ' $0', args], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
代码中设置了shell=True
,python会使用sh -c
的方式来执行cmd命令,此时$0
恰好代表了args
所表示的字符串,而不是可执行文件本身。
为什么不加$0
就不行呢?
因为args
是个字符串,并不符合shell标准的传参的规则,标准的调用方法应该是
subprocess.Popen(['ls', '-a', '-l', '-h'], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
如果写成
subprocess.Popen(['ls', '-a -l -h'], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
则无法识别-l -h
,只能识别-a
当然,写成
subprocess.Popen(['ls -a -l -h'], shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
也是可以的
参考文档
https://www.gnu.org/savannah-checkouts/gnu/bash/manual/bash.html#Special-Parameters
网友评论