假设我想通过 Bash 运行一个命令,如下所示:
/bin/bash -c "ls -l"
根据 Bash 手册页,我也可以这样运行它:
# don't process arguments after this one
# | pass all unprocessed arguments to command
# | |
# V V
/bin/bash -c ls -- -l
但它似乎不起作用(它似乎被忽略)。我做错了什么,还是我对手册页的解释错误?
男人的相关引用:
如果存在 -c 选项,则从字符串中读取命令。如果字符串后面有参数,它们将被分配给位置参数,从 $0 开始。
和
A -- 表示选项结束并禁用进一步的选项处理。 -- 之后的任何参数都被视为文件名和参数。
答案1
您对手册页的解释是错误的。首先,表示选项结束的部分--
与您要执行的操作无关。-c
从那时起,将覆盖命令行的其余部分,因此它不再经过 bash 的选项处理,这意味着将--
传递给命令,而不是由 bash 作为选项结束标记进行处理。
第二个错误是额外的参数作为位置参数分配给启动的 shell 进程,而不是作为参数传递给命令。因此,您想要做的事情可以作为以下之一来完成:
/bin/bash -c 'echo "$0" "$1"' foo bar
/bin/bash -c 'echo "$@"' bash foo bar
在第一种情况下,显式传递 echo 参数$0
,$1
在第二种情况下,使用"$@"
正常扩展为“除 $0 之外的所有位置参数”。请注意,在这种情况下,我们还必须传递一些要使用的东西$0
;我选择了“bash”,因为这$0
通常是这样,但其他任何东西都可以。
至于这样做的原因,而不是仅仅将您直接提供的任何参数传递给您列出的命令:请注意,文档说“命令s从字符串中读取”,复数。换句话说,这个方案允许您执行以下操作:
/bin/bash -c 'mkdir -p -- "$1" && cd -P -- "$1" && touch -- "$2"' bash dir file
但是,请注意,实现最初目标的更好方法可能是使用env
而不是bash
:
/usr/bin/env -- "ls" "-l"
如果您不需要 shell 提供的任何功能,则没有理由使用它 -env
在这种情况下使用它会更快、更简单并且打字更少。而且您不必费力思考以确保它能够安全地处理包含 shell 元字符或空格的文件名。
答案2
我不确定你的目标是什么,但如果你只是想建立一个鲁布·戈德堡机器– “故意过度设计或过度设计的装置、发明、装置或装置,以非常复杂的方式执行非常简单的任务” – 然后尝试
sh -c 'ls $0' -l
或者
sh -c 'ls $1' supercalifragilisticexpialidocious -l
甚至
sh -c 'ls -$0' l
您应该能够理解这些是如何工作的神极客的回答。作为 德里克·马哈尔评论,需要强调的是,脚本参数(后面的命令字符串 -c
)必须用单引号而不是双引号括起来。否则,父 shell(您在其中键入上述命令行)将解析父 shell 上下文中脚本参数中的任何位置参数。例如,
$ set -- The quick brown fox
$ sh -c 'echo "$0" "$1"' foo bar baz
foo bar
$ sh -c "echo '$0' '$1'" foo bar baz
bash The
答案3
这比这一切都简单,而且你已经很接近了。
转义空格,以便整个命令作为单个字符串传递。您的脚本将如下所示:
bash -c "ls\ -l\ $@"
可以添加其他命令,并用未转义的空格分隔,例如
# ↓ unescaped
bash -c "cd\ ~/junk ls\ -l\ $@"
# ↑↑ ↑↑ ↑↑ escaped
junk
该命令中和之间的空格ls
是唯一未转义的空格,因此它用于分隔两个命令。