Old*_*Pro 2 bash bash-scripting
我有一个命令,它输出一些我想传递给另一个命令的参数。但是,当我在子 shell 中运行命令时,输出会受到分词,除非我引用整个内容,在这种情况下,它是单个词。
我希望 subshell 输出拆分单词,但我希望单词拆分尊重引号,但事实并非如此。有什么方法(除了eval)可以将子shell输出拆分为单词但尊重引号?
鉴于args命令定义为
#!/bin/sh -
printf "%d args:" "$#"
printf " <%s>" "$@"
echo
Run Code Online (Sandbox Code Playgroud)
我可以跑
$ args 'foo bar' 'one two'
2 args: <foo bar> <one two>
Run Code Online (Sandbox Code Playgroud)
但我找不到一种方法让子shell 传递 2 个参数。
$ echo "\"'foo bar'\" 'one two'"
"'foo bar'" 'one two'
$ args $(echo "\"'foo bar'\" 'one two'")
4 args: <"'foo> <bar'"> <'one> <two'>
$ args "$(echo "\"'foo bar'\" 'one two'")"
1 args: <"'foo bar'" 'one two'>
Run Code Online (Sandbox Code Playgroud)
当然,我可以使用 eval
$ eval args $(echo "'foo bar' 'one two'")
2 args: <foo bar> <one two>
Run Code Online (Sandbox Code Playgroud)
但它eval是危险的,并引入了各种其他可怕的事情出错的可能性。我不希望再次发生参数扩展或通配符等。我只希望分词以尊重引号。真的没有办法做到这一点bash吗?
Bash 确实没有一个很好的方法来将字符串解析为子字符串,同时尊重引号。无论它是来自命令扩展(也就是说,$( )我认为你称之为子shell)还是普通的变量扩展($varname)。
eval在双引号扩展上使用,它会解析所有shell 语法,包括其他命令和变量扩展、重定向、多个带有;or 的命令&等。这里有很多机会导致错误的结果。eval在非引用扩展上使用,则会获得拆分空白和扩展通配符的效果,然后是常规的完整解析。在这里,几乎一切都可能出错。read -a是最好的一个坏很多。它在尊重引号方面完全失败,但至少它没有扩展文件名通配符。所以bash本身无法做到这一点。但是xargs可以 - 它的默认分词解析尊重引号和转义符,因此根据情况您可以直接使用它:
$ echo "\"'foo bar'\" 'one two'" | xargs args
2 args: <'foo bar'> <one two>
Run Code Online (Sandbox Code Playgroud)
这有几个潜在的问题:一方面,根据有多少输出,xargs可能决定在命令的多次运行之间拆分它们。您可以调整这与它在一定程度上-n,-s和-x选项,但它并不完全令人满意。
另一个可能的问题是,如果命令实际上是要在当前 shell 中执行的 shell 函数、复杂命令或内置命令,这将不起作用。你可以适应它,但它很乱;您需要使用xargs printf '%s\0'转换为以空分隔的字符串序列,然后使用while IFS= read -r -d ''循环将其转换为 bash 数组,最后您可以对数组执行某些操作:
$ argarray=()
$ while IFS= read -r -d '' arg; do argarray+=("$arg"); done < <(echo "\"'foo bar'\" 'one two'" | xargs printf '%s\0')
$ args "${argarray[@]}"
2 args: <'foo bar'> <one two>
Run Code Online (Sandbox Code Playgroud)
请注意,这使用了一个进程替换<( )。这是一个仅限 bash 的功能,当 bash 处于 sh-emulation 模式时甚至不起作用。因此,您必须使用显式 bash shebang (#!/bin/bash或#!/usr/bin/env bash)启动脚本(并且不要通过使用 运行脚本来覆盖 shebang sh scriptname)。
| 归档时间: |
|
| 查看次数: |
596 次 |
| 最近记录: |