【问题标题】:Append to an array variable from a pipeline command从管道命令附加到数组变量
【发布时间】:2016-09-10 18:36:44
【问题描述】:

我正在编写一个 bash 函数来获取所有 git 存储库,但是当我想将所有 git 存储库路径名存储到数组 patharray 时遇到了问题。代码如下:

gitrepo() {
    local opt

    declare -a patharray
    locate -b '\.git' | \
        while read pathname
        do
            pathname="$(dirname ${pathname})"
            if [[ "${pathname}" != *.* ]]; then
            # Note: how to add an element to an existing Bash Array
                patharray=("${patharray[@]}" '\n' "${pathname}")
                # echo -e ${patharray[@]}
            fi
        done
    echo -e ${patharray[@]}
}

我想将所有存储库路径保存到 patharray 数组,但我无法在由 locatewhile 命令组成的 pipeline 之外获取它。
但是我可以在pipeline 命令中获取数组,注释命令# echo -e ${patharray[@]} 如果不注释的话效果很好,那么我该如何解决这个问题呢?

我已经尝试了export 命令,但是它似乎无法将patharray 传递给管道。

【问题讨论】:

  • echo -e 还将扩展路径中的反斜杠(并不是说它们很可能存在...)。
  • 感谢提醒,最好使用printf

标签: arrays bash pipeline


【解决方案1】:

Bash 在单独的SubShells 中运行管道的所有命令。当包含while 循环的子shell 结束时,您对patharray 变量所做的所有更改都将丢失。

您可以简单地将while 循环和echo 语句组合在一起,以便它们都包含在同一个子shell 中:

gitrepo() {
    local pathname dir
    local -a patharray

    locate -b '\.git' | {                      # the grouping begins here
        while read pathname; do
            pathname=$(dirname "$pathname")
            if [[ "$pathname" != *.* ]]; then
                patharray+=( "$pathname" )     # add the element to the array
            fi
        done
        printf "%s\n" "${patharray[@]}"        # all those quotes are needed
    }                                          # the grouping ends here
}

或者,您可以构建不需要管道的代码:使用ProcessSubstitution (有关详细信息,另请参阅 Bash 手册 - man bash | less +/Process\ Substitution):

gitrepo() {
    local pathname dir
    local -a patharray

    while read pathname; do
        pathname=$(dirname "$pathname")
        if [[ "$pathname" != *.* ]]; then
            patharray+=( "$pathname" )     # add the element to the array
        fi
    done < <(locate -b '\.git')

    printf "%s\n" "${patharray[@]}"        # all those quotes are needed
}

【讨论】:

    【解决方案2】:

    首先,附加到数组变量最好使用array[${#array[*]}]="value"array+=("value1" "value2" "etc") 完成,除非您希望转换整个数组(您不这样做)。

    现在,由于pipeline commands are run in subprocesses,对管道命令内的变量所做的更改将不会传播到它外部。有几个选项可以解决这个问题(大多数都列在Greg's BashFAQ/024 中):

    • 改为通过标准输出传递结果

    • 避免在子进程中运行循环

      • 完全避免管道

        • 临时文件/FIFO(不好:需要手动清理,其他人可以访问)
        • 临时变量(平庸:不必要的内存开销)
        • 进程替换(FIFO 的一种特殊的、受语法支持的情况,不需要手动清理;代码改编自 Greg's BashFAQ/020):

          i=0    #`unset i` will error on `i' usage if the `nounset` option is set
          while IFS= read -r -d $'\0' file; do
            patharray[i++]="$(dirname "$file")"    # or however you want to process each file
          done < <(locate -b0 '\.git')
          
      • 使用lastpipe 选项(Bash 4.2 中的新选项)- 不在子进程中运行管道的 last 命令(平庸:具有全局效果)

    【讨论】:

    • 最后,xargs-0 结合。很好的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-12
    • 2018-03-20
    • 2011-01-08
    • 1970-01-01
    • 2019-11-06
    • 1970-01-01
    相关资源
    最近更新 更多