从管道命令附加到数组变量

时间:2016-05-14 16:19:20

标签: arrays bash pipeline

我正在编写一个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数组,但我无法在pipelinelocate之外的while之外获取它命令 但是我可以在pipeline命令中获取数组,如果没有注释,注释命令# echo -e ${patharray[@]}运行良好,那么我该如何解决这个问题呢?

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

2 个答案:

答案 0 :(得分:4)

Bash在单独的SubShell中运行管道的所有命令。当包含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
}

答案 1 :(得分:2)

首先,使用array[${#array[*]}]="value"array+=("value1" "value2" "etc")可以更好地附加到数组变量,除非您希望转换整个数组(不需要)。

现在,从pipeline commands are run in subprocesses开始,对管道命令中的变量所做的更改不会传播到它之外。有几种方法可以解决这个问题(大多数都列在Greg's BashFAQ/024中):

  • 通过stdout传递结果

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

    • 完全避免管道

      • 临时文件/ 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 命令(中等:具有全局效果)