Bash 引用数组扩展

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12985178/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 22:51:16  来源:igfitidea点击:

Bash quoted array expansion

arraysbashquotes

提问by Alex Brown

WHen I write a bash program I typically construct calls like follows:

当我编写一个 bash 程序时,我通常会构造如下调用:

declare -a mycmd=( command.ext "arg1 with space" arg2 thing etc )

"${mycmd[@]}" || echo "Failed: foo"

Where die foois a bash function that prints Error fooand exits.

die foo打印Error foo和退出的 bash 函数在哪里。

But if I want to be clear about the error reason, I want to print the failed command:

但是如果我想清楚错误原因,我想打印失败的命令:

"${mycmd[@]}" || echo "Failed: foo: ${mycmd[*]}"

So the user can run the dead command and find out why. However, quoting is lost on this pass - the Failed message arguments that have spaces or escaped characters are not printed in a way that can be cut-n-pasted and run.

因此用户可以运行 dead 命令并找出原因。但是,在此过程中会丢失引用 - 带有空格或转义字符的 Failed 消息参数不会以可剪切-粘贴和运行的方式打印。

Does anyone have a suggestion for a compact way to fix this problem?

有没有人建议用一种紧凑的方法来解决这个问题?



I think the problem is the way bash deals with argument parsing for commands, and the way (builtin) echo handles arguments. Another way of stating the problem is:

我认为问题在于 bash 处理命令参数解析的方式,以及(内置)echo 处理参数的方式。说明问题的另一种方式是:

How can I printthe quotes around arguments with spaces in the following bash example (which must be run as a script, not in immediate mode):

如何在以下 bash 示例中用空格打印参数周围的引号(必须作为脚本运行,而不是立即模式):

#!/bin/bash
mkdir qatest; cd qatest
declare -a myargs=(1 2 "3 4")
touch "${myargs[@]}"
ls
echo "${myargs[@]}"

actual result:

实际结果:

1  2  3 4
1 2 3 4

desired result

想要的结果

1  2  3 4
1 2 "3 4"

OR

或者

1  2  3 4
"1" "2" "3 4"

In few additional bash code characters.

在几个额外的 bash 代码字符中。



Question closed: @camh answered it magnificently:

问题已结束:@camh 回答得很精彩:

updated script:

更新脚本:

#!/bin/bash
mkdir qatest; cd qatest
declare -a myargs=(1 2 "3 4")
touch "${myargs[@]}"
ls
echo "${myargs[@]}"
echo $(printf "'%s' " "${myargs[@]}")

output:

输出:

1  2  3 4
1 2 3 4
'1' '2' '3 4'

回答by camh

Your problem is with echo. It is getting the correct number of parameters, with some parameters containing spaces, but it's output loses the distinction of spaces between parameters and spaces within parameters.

你的问题是echo. 它正在获得正确数量的参数,其中一些参数包含空格,但它的输出失去了参数之间的空格和参数内的空格的区别。

Instead, you can use printf(1)to output the parameters and always include quotes, making use of printf's feature that applies the format string successively to parameters when there are more parameters than format specifiers in the format string:

相反,您可以使用printf(1)输出参数并始终包含引号,利用 printf 的功能,当格式字符串中的参数多于格式说明符时,将格式字符串连续应用于参数:

echo "Failed: foo:" $(printf "'%s' " "${mycmd[@]}")

That will put single quotes around each argument, even if it is not needed:

这将在每个参数周围加上单引号,即使不需要它:

Failed: foo: 'command.ext' 'arg1 with space' 'arg2' 'thing' 'etc'

I've used single quotes to ensure that other shell metacharacters are not mishandled. This will work for all characters except single quote itself - i.e. if you have a parameter containing a single quote, the output from the above command will not cut and paste correctly. This is likely the closest you will get without getting messy.

我使用了单引号来确保其他 shell 元字符不会被错误处理。这将适用于除单引号本身之外的所有字符 - 即,如果您有一个包含单引号的参数,则上述命令的输出将无法正确剪切和粘贴。这可能是您最接近而不会变得凌乱的。

Edit: Almost 5 years later and since I answered this question, bash 4.4 has been released. This has the "${var@Q}"expansion which quotes the variable such that it can be parsed back by bash.

编辑:差不多 5 年后,自从我回答这个问题以来,bash 4.4 已经发布。这具有"${var@Q}"引用变量的扩展,以便它可以被 bash 解析。

This simplifies this answer to:

这将这个答案简化为:

echo "Failed: foo: " "${mycmd[@]@Q}"

This will correctly handle single quotes in an argument, which my earlier version did not.

这将正确处理参数中的单引号,而我的早期版本没有。

回答by Gordon Davisson

bash's printf command has a %q format that adds appropriate quotes to the strings as they're printed:

bash 的 printf 命令具有 %q 格式,可以在打印字符串时为字符串添加适当的引号:

echo "Failed: foo:$(printf " %q" "${mycmd[@]}")"

Mind you, its idea of the "best" way to quote something isn't always the same as mine, e.g. it tends to prefer escaping funny characters instead of wrapping the string in quotes. For example:

请注意,它引用某事物的“最佳”方式的想法并不总是与我的相同,例如,它倾向于转义有趣的字符而不是将字符串括在引号中。例如:

crlf=$'\r\n'
declare -a mycmd=( command.ext "arg1 with space 'n apostrophe" "arg2 with control${crlf} characters" )
echo "Failed: foo:$(printf " %q" "${mycmd[@]}")"

Prints:

印刷:

Failed: foo: command.ext arg1\ with\ space\ \'n\ apostrophe $'arg2 with control\r\n characters'

回答by doubleDown

A cumbersome method (which only quotes arguments that contain spaces):

一个繁琐的方法(只引用包含空格的参数):

declare -a myargs=(1 2 "3 4")
for arg in "${myargs[@]}"; do
    # testing if the argument contains space(s)
    if [[ $arg =~ \  ]]; then
      # enclose in double quotes if it does 
      arg=\"$arg\"
    fi 
    echo -n "$arg "
done

Output:

输出:

1 2 "3 4"


By the way, with regards to quoting is lost on this pass, note that the quotes are neversaved. " "is a special character that tells the shell to treat whatever is inside as a single field/argument (i.e. not split it). On the other hand, literal quotes (typed like this \") are preserved.

顺便说一下,关于quoting is lost on this pass,请注意引号永远不会被保存。" "是一个特殊字符,它告诉外壳将内部的任何内容视为单个字段/参数(即不拆分它)。另一方面,文字引号(像这样输入\")被保留。

回答by weynhamz

How about declare -p quotedarray?

怎么样declare -p quotedarray

-- edit --

- 编辑 -

Acctually, declare -p quotedarraywill satisfy you purpose well. If you insist on the output format of the result, then I have a small trick would do the work, but just for the indexed array not associative one.

事实上,declare -p quotedarray会很好地满足你的目的。如果您坚持结果的输出格式,那么我有一个小技巧可以完成这项工作,但仅适用于索引数组而不是关联数组。

declare -a quotedarray=(1 2 "3 4")
temparray=( "${quotedarray[@]/#/\"}" ) #the outside double quotes are critical
echo ${temparray[@]/%/\"}

回答by kyb

Another approach

另一种方法

# echo_array.sh

contains_space(){ [[ "" =~ " " ]]; return $?; }
maybe_quote_one(){ contains_space ""  &&  echo \'""\'  ||  echo ""; }
maybe_quote(){ while test ""; do maybe_quote_one ""; shift; done; }

arridx(){ echo '${''['']}'; }
arrindir(){ echo $(eval echo `arridx  `); }
arrsize(){ echo `eval echo '${'#'[@]}'`; }

echo_array()
{ 
    echo -n "=( "
    local i=0
    for (( i=0; i < `arrsize a`; i++ )); do
        echo -n $(maybe_quote "$(arrindir  $i)") ''
    done
    echo ")"
}

Usage:

用法:

source echo_array.sh
a=( foo bar baz "It was hard" curious '67 - 00' 44 'my index is 7th' )
echo_array a
# a=( foo bar baz 'It was hard' curious '67 - 00' 44 'my index is 7th'  )
arrindir a 7
# my index is 7th
arrindir a 0
# foo
arrsize a
# 8

$ bash --version

$ bash --version

GNU bash, version 4.3.48(1)-release (x86_64-pc-linux-gnu)
Copyright (C) 2013 Free Software Foundation, Inc.

回答by Michael Potter

I like to put the code in a function so it is easier to reuse and document:

我喜欢将代码放在一个函数中,这样更容易重用和记录:

function myjoin
{
   local list=("${@}")
   echo $(printf "'%s', " "${list[@]}")
}

declare -a colorlist=()
colorlist+=('blue')
colorlist+=('red')
colorlist+=('orange')

echo "[$(myjoin ${colorlist[@]})]"

Note how I added the comma in the solution because I am generating code with a bash script.

请注意我是如何在解决方案中添加逗号的,因为我正在使用 bash 脚本生成代码。

[EDIT: fix problem that EM0 pointed out that it is returning ['blue',]]

[编辑:修复 EM0 指出它正在返回 ['blue',]] 的问题