使用bash将行读入数组,每行一个元素

vik*_*mun 2 bash git shell-script

我正在尝试获取目录中文件的所有未暂存修改的 bash 数组(使用 Git)。以下代码用于打印出目录中所有修改过的文件:

git -C $dir/.. status --porcelain | grep "^.\w" | cut -c 4-
Run Code Online (Sandbox Code Playgroud)

这打印

"Directory Name/File B.txt"
"File A.txt"
Run Code Online (Sandbox Code Playgroud)

我尝试使用

arr1=($(git status --porcelain | grep "^.\w" | cut -c 4-))
Run Code Online (Sandbox Code Playgroud)

但是之后

for a in "${arr1[@]}"; do echo "$a"; done
Run Code Online (Sandbox Code Playgroud)

${arr1[@]}印刷品周围有和没有引号

"Directory
Name/File
B.txt"
"File
A.txt"
Run Code Online (Sandbox Code Playgroud)

我也试过

git -C $dir/.. status --porcelain | grep "^.\w" | cut -c 4- | readarray arr2
Run Code Online (Sandbox Code Playgroud)

但是之后

for a in "${arr2[@]}"; do echo "$a"; done
Run Code Online (Sandbox Code Playgroud)

(带和不带引号${arr2[@]})不打印任何内容。declare -a arr2预先使用也绝对没有任何作用。


我的问题是:如何将这些值读入数组?(这被用于我的 argos 插件gitbar,以防万一,所以你可以看到我的所有代码)。

ImH*_*ere 11

TL; 博士

在 bash 中:

readarray -t arr2 < <(git … )
printf '%s\n' "${arr2[@]}"
Run Code Online (Sandbox Code Playgroud)

你的问题有两个不同的问题

  1. 壳分裂。
    当你这样做时:

    arr1=($(git … ))
    
    Run Code Online (Sandbox Code Playgroud)

    “命令扩展”未加引号,因此:它受制于 shell 拆分和 glob。

    确切地看那个shell分裂做了什么,使用printf:

    $ printf '<%s>  '  $(echo word '"one simple sentence"')
    <word>  <"one>  <simple>  <sentence">
    
    Run Code Online (Sandbox Code Playgroud)

    通过引用可以避免这种情况:

    $ printf '<%s>  '  "$(echo word '"one simple sentence"')"
    <word "one simple sentence">
    
    Run Code Online (Sandbox Code Playgroud)

    但这也可以避免在您想要的换行符上进行拆分。

  2. 管道
    执行时:

    git … | … | … | readarray arr2
    
    Run Code Online (Sandbox Code Playgroud)

    数组变量已arr2设置,但在管道 ( |) 关闭时它消失了。

    如果您留在最后一个子shell中,则可以使用该值:

    $ printf '%s\n' "First value." "Second value." | 
            { readarray -t arr2; printf '%s\n' "${arr2[@]}"; }
    First value.
    Second value.
    
    Run Code Online (Sandbox Code Playgroud)

    但是值arr2不会从管道中存活下来。

解决方案

您需要使用 read 在换行符上拆分,而不是使用管道拆分。
从旧到新:

  1. 环形。
    对于没有数组的旧壳(使用位置参数,唯一的准数组):

    set --
    while IFS='' read -r value; do
        set -- "$@" "$value"
    done <<-EOT
    $(printf '%s\n' "First value." "Second value.")
    EOT
    
    printf '%s\n' "$@"
    
    Run Code Online (Sandbox Code Playgroud)

    设置数组(ksh、zsh、bash)

    i=0; arr1=()
    while IFS='' read -r value; do
        arr1+=("$value")
    done <<-EOT
    $(printf '%s\n' "First value." "Second value.")
    EOT
    
    printf '%s\n' "${arr1[@]}"
    
    Run Code Online (Sandbox Code Playgroud)
  2. Here-string我们可以使用 here-string ( )
    代替 here document <<( <<<):

    i=0; arr1=()
    while IFS='' read -r value; do
        arr1+=("$value")
    done <<<"$(printf '%s\n' "First value." "Second value.")"
    
    printf '%s\n' "${arr1[@]}"
    
    Run Code Online (Sandbox Code Playgroud)
  3. 进程替换
    在支持它的 shell (ksh, zsh, bash) 中,您可以<( … )用来替换 here-string:

    i=0; arr1=()
    while IFS='' read -r value; do
        arr1+=("$value")
    done < <(printf '%s\n' "First value." "Second value.")
    
    printf '%s\n' "${arr1[@]}"
    
    Run Code Online (Sandbox Code Playgroud)

    不同之处:<( )能够发出 NUL 字节,而 here-string 可能会删除(或发出警告)NUL。默认情况下,here-string 添加尾随换行符。可能还有其他人AFAIK。

  4. readarray
    使用readarraybash〔A〕(又名mapfile)避免循环:

    readarray -t arr2 < <(printf '%s\n' "First value." "Second value.")
    printf '%s\n' "${arr2[@]}"
    
    Run Code Online (Sandbox Code Playgroud)

[a]在 ksh 中,您需要使用read -A,它会在使用前清除变量,但需要一些“魔法”来拆分换行符并立即读取整个输入。

IFS=$'\n' read -d '' -A arr2 < <(printf '%s\n' "First value." "Second value.")
Run Code Online (Sandbox Code Playgroud)

您需要在 zsh 中加载一个mapfile模块来执行类似的操作。