如何在bash函数参数中加引号?

发布于 2024-09-10 02:41:20 字数 928 浏览 1 评论 0原文

我想做的是将可能包含引号(单引号或双引号)的行作为函数的输入,并完全按照提供给函数的方式回显该行。例如:

function doit {
   printf "%s " ${@} 
   eval "${@}"
   printf " # [%3d]\n" ${?}
}

给出以下输入

doit VAR=42
doit echo 'single quote $VAR'
doit echo "double quote $VAR"

会产生以下结果:

VAR=42  # [  0]
echo single quote $VAR  # [  0]
echo double quote 42  # [  0]

因此,变量扩展的语义按照我的预期保留,但我无法获得提供给函数的行的确切格式。我想要的是 doit echo 'single quote $VAR' 结果 echo 'single quote $VAR'

我确信这与 bash 在将参数传递给函数之前对其进行处理有关;我只是在寻找解决方法(如果可能的话)。

编辑

所以我的目的是隐藏脚本的执行,同时提供执行的精确副本,该副本可用作诊断工具,包括每个步骤的退出状态。

虽然我可以通过执行类似的操作来获得上述所需的行为,但

while read line ; do 
   doit ${line}
done < ${INPUT}

面对控制结构(即 ifwhile 等),该方法会失败)。我考虑过使用 set -x 但它也有其局限性:" 变为 ' 并且对于失败的命令来说退出状态不可见。

What I'd like to do is take, as an input to a function, a line that may include quotes (single or double) and echo that line exactly as it was provided to the function. For instance:

function doit {
   printf "%s " ${@} 
   eval "${@}"
   printf " # [%3d]\n" ${?}
}

Which, given the following input

doit VAR=42
doit echo 'single quote $VAR'
doit echo "double quote $VAR"

Yields the following:

VAR=42  # [  0]
echo single quote $VAR  # [  0]
echo double quote 42  # [  0]

So the semantics of the variable expansion are preserved as I'd expect, but I can not get the exact format of the line as it was provided to the function. What I'd like is to have doit echo 'single quote $VAR' result in echo 'single quote $VAR'.

I'm sure this has to do with bash processing the arguments before they are passed to the function; I'm just looking for a way around that (if possible).

Edit

So what I had intended was to shadow the execution of a script while providing an exact replica of the execution that could be used as a diagnostic tool including exit status of each step.

While I can get the desired behavior described above by doing something like

while read line ; do 
   doit ${line}
done < ${INPUT}

That approach fails in the face of control structures (i.e. if, while, etc). I thought about using set -x but that has it's limitations as well: " becomes ' and exit status is not visible for commands that fail.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

逆流 2024-09-17 02:41:20

我的情况与您类似,我需要一个脚本来包装现有命令并传递保留引用的参数。

我想出了一些方法,它不能完全保留命令行的输入,但可以正确传递参数并向您显示它们是什么。

这是我设置为 Shadow ls 的脚本:

CMD=ls
PARAMS=""

for PARAM in "$@"
do
  PARAMS="${PARAMS} \"${PARAM}\""
done

echo Running: ${CMD} ${PARAMS}
bash -c "${CMD} ${PARAMS}"
echo Exit Code: $?

这是一些示例输出:

$ ./shadow.sh missing-file "not a file"
Running: ls "missing-file" "not a file"
ls: missing-file: No such file or directory
ls: not a file: No such file or directory
Exit Code: 1

正如您所看到的,它添加了原本不存在的引号,但它确实保留了带有空格的参数,其中正是我所需要的。

I was in a similar position to you in that I needed a script to wrap around an existing command and pass arguments preserving quoting.

I came up with something that doesn't preserve the command line exactly as typed but does pass the arguments correctly and show you what they were.

Here's my script set up to shadow ls:

CMD=ls
PARAMS=""

for PARAM in "$@"
do
  PARAMS="${PARAMS} \"${PARAM}\""
done

echo Running: ${CMD} ${PARAMS}
bash -c "${CMD} ${PARAMS}"
echo Exit Code: $?

And this is some sample output:

$ ./shadow.sh missing-file "not a file"
Running: ls "missing-file" "not a file"
ls: missing-file: No such file or directory
ls: not a file: No such file or directory
Exit Code: 1

So as you can see it adds quotes which weren't originally there but it does preserve arguments with spaces in which is what I needed.

苏别ゝ 2024-09-17 02:41:20

发生这种情况的原因是因为 bash 解释了参数,正如您所想的那样。当它调用该函数时,引号就不再存在了,所以这是不可能的。它在 DOS 中起作用是因为程序可以自行解释命令行,而不是它可以帮助您!

The reason this happens is because bash interprets the arguments, as you thought. The quotes simply aren't there any more when it calls the function, so this isn't possible. It worked in DOS because programs could interpret the command line themselves, not that it helps you!

美男兮 2024-09-17 02:41:20

虽然@Peter Westlake答案是正确的,并且没有要保留的引号可以尝试推断是否需要引号并因此最初传入。就个人而言,当我需要在日志中证明命令以正确的引用运行时,我使用了这个 requote 函数:

function requote() {
    local res=""
    for x in "${@}" ; do
        # try to figure out if quoting was required for the $x:
        grep -q "[[:space:]]" <<< "$x" && res="${res} '${x}'" || res="${res} ${x}"
    done
    # remove first space and print:
    sed -e 's/^ //' <<< "${res}"
}

以下是我如何使用它:

CMD=$(requote "${@}")
# ...
echo "${CMD}"

Although @Peter Westlake's answer is correct, and there are no quotes to preserve one can try to deduce if the quotes where required and thus passed in originally. Personally I used this requote function when I needed a proof in my logs that a command ran with the correct quoting:

function requote() {
    local res=""
    for x in "${@}" ; do
        # try to figure out if quoting was required for the $x:
        grep -q "[[:space:]]" <<< "$x" && res="${res} '${x}'" || res="${res} ${x}"
    done
    # remove first space and print:
    sed -e 's/^ //' <<< "${res}"
}

And here is how I use it:

CMD=$(requote "${@}")
# ...
echo "${CMD}"
大海や 2024-09-17 02:41:20
doit echo "'single quote $VAR'"
doit echo '"double quote $VAR"'

两者都会起作用。

bash 在进入函数时只会去掉外面的引号。

doit echo "'single quote $VAR'"
doit echo '"double quote $VAR"'

Both will work.

bash will only strip the outside set of quotes when entering the function.

北城半夏 2024-09-17 02:41:20

当您将带有引号的字符串作为命令行参数传递时,Bash 将删除引号。当字符串传递到脚本时,引号就不再存在了。你无法知道是单引号还是双引号。

你可能可以做的是这样的:

doit VAR=42
doit echo \'single quote $VAR\'
doit echo \"double quote $VAR\"

在你的脚本中你得到

echo 'single quote $VAR'
echo "double quote $VAR"

或者这样做

doit VAR=42
doit echo 'single quote $VAR'
doit echo '"double quote $VAR"'

在你的脚本中你得到

echo single quote $VAR
echo "double quote $VAR"

Bash will remove the quote when you pass a string with quote in as command line argument. The quote is simply not there anymore when the string is pass to your script. You have no way to know there is a single quote or double quote.

What you probably can do is sth like this:

doit VAR=42
doit echo \'single quote $VAR\'
doit echo \"double quote $VAR\"

In your script you get

echo 'single quote $VAR'
echo "double quote $VAR"

Or do this

doit VAR=42
doit echo 'single quote $VAR'
doit echo '"double quote $VAR"'

In your script you get

echo single quote $VAR
echo "double quote $VAR"
(り薆情海 2024-09-17 02:41:20

例如:

ponerApostrofes1 () 
{
    for (( i=1; i<=$#; i++ ));
    do
        eval VAR="\${$i}"; 
        echo \'"${VAR}"\';
    done; 
    return; 
}

当参数带有撇号时就会出现问题。

这个函数:

ponerApostrofes2 () 
{ 
    for ((i=1; i<=$#; i++ ))
    do
        eval PARAM="\${$i}";
        echo -n \'${PARAM//\'/\'\\\'\'}\'' ';
    done;
    return
}

解决了上面提到的问题,你可以使用内部包含撇号的参数,比如“Porky's”,并且当每个参数被引用时,显然(?)返回相同的参数字符串;如果没有,则引用它。令人惊讶的是,我不明白为什么,如果您递归地使用它,它不会返回相同的列表,但每个参数都会被再次引用。但如果你对每个参数进行回显,你就会恢复原始参数。

示例:

$ ponerApostrofes2 'aa aaa' 'bbbb b' 'c' 
'aa aaa' 'bbbb b' 'c'

$ ponerApostrofes2 $(ponerApostrofes2 'aa aaa' 'bbbb b' 'c' )
''\''aa' 'aaa'\''' ''\''bbbb' 'b'\''' ''\''c'\''' 

和:

$ echo ''\''bbbb' 'b'\'''
'bbbb b'
$ echo ''\''aa' 'aaa'\'''
'aa aaa'
$ echo ''\''c'\''' 
'c'

和这个:

ponerApostrofes3 () 
{ 
    for ((i=1; i<=$#; i++ ))
    do
        eval PARAM="\${$i}";
        echo -n ${PARAM//\'/\'\\\'\'} ' ';
    done;
    return
}

返回少一级报价,
也不起作用,也不递归地交替两者。

This:

ponerApostrofes1 () 
{
    for (( i=1; i<=$#; i++ ));
    do
        eval VAR="\${$i}"; 
        echo \'"${VAR}"\';
    done; 
    return; 
}

As an example has problems when the parameters have apostrophes.

This function:

ponerApostrofes2 () 
{ 
    for ((i=1; i<=$#; i++ ))
    do
        eval PARAM="\${$i}";
        echo -n \'${PARAM//\'/\'\\\'\'}\'' ';
    done;
    return
}

solves the mentioned problem and you can use parameters including apostrophes inside, like "Porky's", and returns, apparently(?), the same string of parameters when each parameter is quoted; if not, it quotes it. Surprisingly, I don't understand why, if you use it recursively, it doesn't return the same list but each parameter is quoted again. But if you do echo of each one you recover the original parameter.

Example:

$ ponerApostrofes2 'aa aaa' 'bbbb b' 'c' 
'aa aaa' 'bbbb b' 'c'

$ ponerApostrofes2 $(ponerApostrofes2 'aa aaa' 'bbbb b' 'c' )
''\''aa' 'aaa'\''' ''\''bbbb' 'b'\''' ''\''c'\''' 

And:

$ echo ''\''bbbb' 'b'\'''
'bbbb b'
$ echo ''\''aa' 'aaa'\'''
'aa aaa'
$ echo ''\''c'\''' 
'c'

And this one:

ponerApostrofes3 () 
{ 
    for ((i=1; i<=$#; i++ ))
    do
        eval PARAM="\${$i}";
        echo -n ${PARAM//\'/\'\\\'\'} ' ';
    done;
    return
}

returning one level of quotation less,
doesn't work either, neither alternating both recursively.

鸵鸟症 2024-09-17 02:41:20

如果您的 shell 不支持模式替换,即 ${param/pattern/string},则可以使用以下 sed 表达式来安全地引用任何字符串,使其 < code>eval 再次变成单个参数:

sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/"

将其与 printf 组合,可以编写一个小函数,该函数将采用文件名扩展或 "$@ 生成的任何字符串列表" 并将其转换为可以安全地传递给 eval 的内容,以将其扩展为另一个命令的参数,同时安全地保留参数分隔。

# Usage: quotedlist=$(shell_quote args...)
#
# e.g.:  quotedlist=$(shell_quote *.pdf)    # filenames with spaces
#
# or:    quotedlist=$(shell_quote "$@")
#
# After building up a quoted list, use it by evaling it inside
# double quotes, like this:
#
#   eval "set -- $quotedlist"
#   for str in "$@"; do
#       # fiddle "${str}"
#   done
#
# or like this:
#
#   eval "\$a_command $quotedlist \$another_parameter"
#
shell_quote()
{
    local result=''
    local arg
    for arg in "$@" ; do

        # Append a space to our result, if necessary
        #
        result=${result}${result:+ }

        # Convert each embedded ' to \' , then insert ' at the
        # beginning of the line, and append ' at the end of
        # the line.
        #
        result=${result}$(printf "%s\n" "$arg" | \
            sed -e "s/'/'\\\\''/g" -e "1s/^/'/" -e "\$s/\$/'/")
    done

    # use printf(1) instead of echo to avoid weird "echo"
    # implementations.
    #
    printf "%s\n" "$result"
}

在某些情况下,使用“不可能”字符作为字段分隔符,然后使用 IFS 来控制扩展,可能会更容易(也可能更安全,即避免 eval)。再次值。

If one's shell does not support pattern substitution, i.e. ${param/pattern/string} then the following sed expression can be used to safely quote any string such that it will eval into a single parameter again:

sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/"

Combining this with printf it is possible to write a little function that will take any list of strings produced by filename expansion or "$@" and turn it into something that can be safely passed to eval to expand it into arguments for another command while safely preserving parameter separation.

# Usage: quotedlist=$(shell_quote args...)
#
# e.g.:  quotedlist=$(shell_quote *.pdf)    # filenames with spaces
#
# or:    quotedlist=$(shell_quote "$@")
#
# After building up a quoted list, use it by evaling it inside
# double quotes, like this:
#
#   eval "set -- $quotedlist"
#   for str in "$@"; do
#       # fiddle "${str}"
#   done
#
# or like this:
#
#   eval "\$a_command $quotedlist \$another_parameter"
#
shell_quote()
{
    local result=''
    local arg
    for arg in "$@" ; do

        # Append a space to our result, if necessary
        #
        result=${result}${result:+ }

        # Convert each embedded ' to \' , then insert ' at the
        # beginning of the line, and append ' at the end of
        # the line.
        #
        result=${result}$(printf "%s\n" "$arg" | \
            sed -e "s/'/'\\\\''/g" -e "1s/^/'/" -e "\$s/\$/'/")
    done

    # use printf(1) instead of echo to avoid weird "echo"
    # implementations.
    #
    printf "%s\n" "$result"
}

It may be easier (and maybe safer, i.e. avoid eval) in some situations to use an "impossible" character as the field separator and then use IFS to control expansion of the value again.

月隐月明月朦胧 2024-09-17 02:41:20

shell 将在将引号和 $ 传递给您的函数之前对其进行解释。您的函数无法执行太多操作来取回特殊字符,因为它无法知道(在双引号示例中) 42 是硬编码的还是来自变量。如果您希望特殊字符存活足够长的时间以完成您的功能,则必须转义这些特殊字符。

The shell is going to interpret the quotes and the $ before it passes it to your function. There's not a lot your function can do to get the special characters back, because it has no way of knowing (in the double-quote example) whether 42 was hard-coded or if it came from a variable. You will have to escape the special characters if you want them to survive long enough to make it to your function.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文