【问题标题】:Prefix and postfix elements of a bash arraybash 数组的前缀和后缀元素
【发布时间】:2013-12-20 10:08:34
【问题描述】:

我想在 bash 中为数组添加前缀和后缀,类似于大括号扩展。

假设我有一个 bash 数组

ARRAY=( one two three )

我希望能够像下面的大括号扩展一样对它进行前缀和后缀

echo prefix_{one,two,three}_suffix

我能找到的最好的方法是使用 bash 正则表达式来添加前缀或后缀

echo ${ARRAY[@]/#/prefix_}
echo ${ARRAY[@]/%/_suffix}

但我找不到任何关于如何同时执行这两项操作的信息。可能我可以使用正则表达式捕获并做类似的事情

echo ${ARRAY[@]/.*/prefix_$1_suffix}

但 bash 变量正则表达式替换似乎不支持捕获。我还可以存储一个临时数组变量,例如

PRE=(${ARRAY[@]/#/prefix_})
echo ${PRE[@]/%/_suffix}

这可能是我能想到的最好的,但似乎仍然低于标准。最后一种选择是使用类似于

的for循环
EXPANDED=""
for E in ${ARRAY[@]}; do
    EXPANDED="prefix_${E}_suffix $EXPANDED"
done
echo $EXPANDED

但这太丑了。如果我想在前缀后缀或数组元素的任何地方使用空格,我也不知道如何让它工作。

【问题讨论】:

    标签: regex arrays bash


    【解决方案1】:

    更漂亮但本质上与循环解决方案相同:

    $ ARRAY=(A B C)
    $ mapfile -t -d $'\0' EXPANDED < <(printf "prefix_%s_postfix\0" "${ARRAY[@]}")
    $ echo "${EXPANDED[@]}"
    prefix_A_postfix prefix_B_postfix prefix_C_postfix
    

    mapfile 将行读入数组元素。使用-d $'\0',它改为读取以空分隔的字符串,-t 从结果中省略分隔符。见help mapfile

    【讨论】:

    • mapfile -d '' 也执行 NUL 终止。
    • @usretc 没错,但$'\0' 对读者来说更容易解释
    • 是的。同样,-t 并不是真正需要的,因为BASH 在变量中去除了NULs,但这是一个伪影。
    【解决方案2】:

    Bash 大括号扩展不使用正则表达式。使用的模式只是一些 shell glob,您可以在 bash 手册3.5.8.1 Pattern Matching 中找到它。

    您的两步解决方案很酷,但它需要一些引号以确保空格安全:

    ARR_PRE=("${ARRAY[@]/#/prefix_}")
    echo "${ARR_PRE[@]/%/_suffix}"
    

    你也可以用一些邪恶的方式来做:

    eval "something $(printf 'pre_%q_suf ' "${ARRAY[@]}")"
    

    【讨论】:

    • 注意:如果您有 IFS=$'\n' (处理带有空格的名称),则前缀命令将不会保留原始数组结构。它只会使数组仅包含一项(字符串列表),以及所有已更改的字符串。之后运行:declare -p array。运行后缀命令时,它会将其添加到列表中的最后一项。
    • @Magne 不,它在任何地方都不依赖 IFS。你到底是从哪里得到这个概念的?试试看:IFS='' ARRAY=(p $'\nq' 'r s'); ARR_PRE=("${ARRAY[@]/#/prefix_}"); ARR_POS=("${ARR_PRE[@]/%/_suffix}"); declare -p ARR_POS。甚至 printf 也没有坏掉。
    • 如果您在评论的示例代码中设置IFS=$'\n',您就会明白我的意思。它输出:declare -a ARR_POS='([0]="prefix_p prefix_ q prefix_r s_suffix")'
    • @Magne Holy effing shete。它也打破了 IFS 为\r\t\f,甚至是\a。它发生在 bash 3.2.57(1) (Apple) 上,但不在 5.0.17(1) 上。我认为这是您刚刚遇到的一个旧 bash 错误...
    【解决方案3】:

    我也有完全相同的问题,我使用sed的词边界匹配机制想出了如下解决方案:

    myarray=( one two three )
    newarray=( $(echo ${myarray[*]}|sed "s/\(\b[^ ]\+\)/pre-\1-post/g") )
    echo ${newarray[@]}
    > pre-one-post pre-two-post pre-three-post
    echo ${#newarray[@]}
    > 3
    

    等待更优雅的解决方案...

    【讨论】:

      【解决方案4】:

      对于数组:

      ARRAY=( one two three )
      (IFS=,; eval echo prefix_\{"${ARRAY[*]}"\}_suffix)
      

      对于字符串:

      STRING="one two three"
      eval echo prefix_\{${STRING// /,}\}_suffix
      

      eval 导致其参数被评估两次,在这两种情况下,第一次评估的结果都是

      echo prefix_{one,two,three}_suffix
      

      然后第二次执行它。 对于数组情况,使用 subshel​​l 来避免过度使用 IFS

      你也可以在 zsh 中这样做:

      echo ${${ARRAY[@]/#/prefix_}/%/_suffix}
      

      【讨论】:

        【解决方案5】:

        您的最后一个循环可以通过以下方式以对空格友好的方式完成:

        EXPANDED=()
        for E in "${ARRAY[@]}"; do
            EXPANDED+=("prefix_${E}_suffix")
        done
        echo "${EXPANDED[@]}"
        

        【讨论】:

          猜你喜欢
          • 2018-08-12
          • 2014-10-13
          • 1970-01-01
          • 2016-06-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-08-15
          相关资源
          最近更新 更多