![长话短说](https://linux22.com/image/142873/%E9%95%BF%E8%AF%9D%E7%9F%AD%E8%AF%B4.png)
我正在尝试获取目录中文件的所有未暂存修改的 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[@]}"
您的问题有两个不同的问题
壳裂。
当你这样做时: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">
但这也可以避免您想要的换行符分割。
管道
当你执行时: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 来分割换行符,但不能使用管道。
从旧到新:
环形。
对于没有数组的旧 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[@]}"
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[@]}"
进程替换
在支持它的 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 可能还有其他人。readarray
使用readarray
于bash
[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-)")