每个数组项的过程替换,无评估 [英] Process Substitution For Each Array Entry, Without Eval

查看:81
本文介绍了每个数组项的过程替换,无评估的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个任意字符串数组,例如 a =(1st 2nd string $'3rd\nstring\n'...)。< br>
我想将这些字符串传递给将其参数解释为文件的命令,例如 paste

I have an array of arbitrary strings, for instance a=(1st "2nd string" $'3rd\nstring\n' ...).
I want to pass these strings to a command that interprets its arguments as files, for instance paste.

对于固定数量的变量,我们可以使用过程替换

For a fixed number of variables, we could use process substitution

paste <(printf %s "$var1") <(printf %s "$var2") <(printf %s "$var3")

但这仅在事先知道变量数量的情况下才有效。

对于数组 a ,我们可以编写类似

but that does only work if the number of variables is known beforehand.
For the array a, we could write something fairly safe like

eval paste $(printf '<(printf %%s %q) ' "${a[@]}")

出于兴趣:是否有一种方法可以代替每个 a 的条目不使用 eval ?请记住, a 的条目可以包含任何字符( \0 除外,因为 bash 不支持)。

Out of interest: Is there a way to process-substitute each of a's entries without using eval? Remember that a's entries can contain any character (except for \0 because bash doesn't support it).

推荐答案

此解决方案的灵感来自 rici的答案
它解决了由名称引用引起的可能的名称冲突,但是要求用户指定一个定界符,该定界符不会出现在要执行的命令中。尽管如此,分隔符仍可以毫无问题地出现在数组中。

This solution is inspired by rici's answer. It resolves the possible name collision caused by namerefs, but requires the user to specify a delimiter that does not appear in the command to be executed. Nevertheless, the delimiter can appear in the array without problems.

# Search a string in an array
# and print the 0-based index of the first identical element.
# Usage: indexOf STRING "${ARRAY[@]}"
# Exits with status 1 if the array does not contain such an element.
indexOf() {
    search="$1"
    i=0
    while shift; do
        [[ "$1" = "$search" ]] && echo "$i" && return
        ((++i))
    done
    return 1
}

# Execute a command and replace its last arguments by anonymous files.
# Usage: emulateFiles DELIMITER COMMAND [OPTION]... DELIMITER [ARGUMENT]...
# DELIMITER must differ from COMMAND and its OPTIONS.
# Arguments after the 2nd occurrence of DELIMITER are replaced by anonymous files.
emulateFiles() {
    delim="$1"
    shift
    i="$(indexOf "$delim" "$@")" || return 2
    cmd=("${@:1:i}")
    strings=("${@:i+2}")
    if [[ "${#strings[@]}" = 0 ]]; then
        "${cmd[@]}"
    else
        emulateFiles "$delim" "${cmd[@]}" <(printf %s "${strings[0]}") \
                     "$delim" "${strings[@]:1}"
    fi
}



用法示例



Usage examples

a=($'a b\n c ' $'x\ny\nz\n' : '*')
$ emulateFiles : paste : "${a[@]}"
a b x   :   *
 c  y       
    z       
$ emulateFiles : paste -d: : "${a[@]}"   # works because -d: != :
a b:x:::*
 c :y::
:z::
$ emulateFiles delim paste -d : delim "${a[@]}"
a b:x:::*
 c :y::
:z::

这篇关于每个数组项的过程替换,无评估的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆