长话短说

长话短说

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

git -C $dir/.. status --porcelain | grep "^.\w" | cut -c 4-

这打印

"Directory Name/File B.txt"
"File A.txt"

我尝试使用

arr1=($(git status --porcelain | grep "^.\w" | cut -c 4-))

但是之后

for a in "${arr1[@]}"; do echo "$a"; done

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

"Directory
Name/File
B.txt"
"File
A.txt"

我也尝试过

git -C $dir/.. status --porcelain | grep "^.\w" | cut -c 4- | readarray arr2

但是之后

for a in "${arr2[@]}"; do echo "$a"; done

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


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

答案1

长话短说

在bash中:

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

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

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

    arr1=($(git … ))
    

    “命令扩展”未加引号,因此:它受 shell split 和 glob 的影响。

    要确切了解 shell 分割的作用,请使用 printf:

    $ printf '<%s>  '  $(echo word '"one simple sentence"')
    <word>  <"one>  <simple>  <sentence">
    

    这可以通过以下方式避免引用:

    $ printf '<%s>  '  "$(echo word '"one simple sentence"')"
    <word "one simple sentence">
    

    但这也可以避免您想要的换行符分割。

  2. 管道
    当你执行时:

    git … | … | … | readarray arr2
    

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

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

    $ printf '%s\n' "First value." "Second value." | 
            { readarray -t arr2; printf '%s\n' "${arr2[@]}"; }
    First value.
    Second value.
    

    arr2但离开管道后,其价值将无法存续。

解决方案

您需要使用 read 来分割换行符,但不能使用管道。
从旧到新:

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

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

    设置数组(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[@]}"
    
  2. Here-string我们可以使用here-string
    ()来代替here文档( ):<<<<<

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

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

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

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

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

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

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

您将需要mapfile在 zsh 中加载模块做类似的事情。

答案2

当您通过管道传输到 readarray 时,您启动了一个子 shell,该子 shell 正确填充了 arr2 数组,但随后退出了。使用进程替换作为 readarray 的输入:

readarray -t arr2 < <(git ...)

答案3

你很接近

这是一个“带有空格字符的文件名”问题。

默认情况下,分隔符是空格字符。这是IFS环境变量设置的。

要临时更改环境变量,请使用以下命令:

ifs_backup=$IFS
IFS=$(echo -en "\n\b")

然后这个命令的输出:

for a in "${arr1[@]}"; do echo "$a"; done

将:

"Directory Name/File B.txt"
"File A.txt"

恢复IFS

IFS=$ifs_backup

答案4

在代码的内部部分添加引号:

arr1=("$(git status --porcelain | grep "^.\w" | cut -c 4-)")

相关内容