我正在尝试解析 from 的输出git status --porcelain -b
以在提示中使用,但在执行参数扩展时遇到奇怪的行为。
这段代码应该可以说明这个问题:
#!/bin/bash
IFS=$'\n'
touch ab
status_arr=( $(git status --porcelain -b) )
for (( i=0; i<${#status_arr[@]}; i++ )); do
echo ${status_arr[$i]}
echo ${status_arr[$i]:0:2}
done
当在干净的 git 目录中运行时,我得到以下输出:
$ bash sandbox/statusline/issue.sh
## master...origin/master
##
?? ab
ab
我希望??
在输出的第 4 行得到回显,实际上,如果我将脚本更改为touch abc
或touch a
,这就是我得到的。
我对此感到非常困惑,我想我一定在 bash 中遗漏了一些明显的东西,但谷歌没有产生任何有用的东西。
如果这是一个已知的“事情”,有没有办法我可以绕过它/完全避免它?
答案1
是?
一个用于匹配文件名的 shell 全局字符。它匹配单个字符。因此,由于您有一个名为 的文件ab
,因此该??
模式与其匹配。
发生这种情况的原因是因为你的参数扩展是不是引。
答案2
不带引号的变量或命令替换不会解释为字符串,而是解释为文件名通配符模式的列表。即,将变量的值或命令的输出分割成由 in 中的字符分隔的单独部分IFS
(此步骤称为字段分割);然后每个部分都被解释为通配符模式,如果该模式与某些文件匹配,则将其替换为匹配文件名的列表,否则该模式保持不变(此步骤称为文件名生成)。
例如,status_arr=( $(git status --porcelain -b) )
设置status_arr
为包含 5 个字符字符串的单元素数组,?? ab'
因为IFS
仅包含换行符并且没有与模式匹配的文件?? ab
。如果IFS
其默认值包含空格,status_arr
则将设置为包含两次出现的 2 字符 string 的二元素数组ab
。
如果变量或命令替换用双引号引起来,则按原样使用生成的字符串:字段分割和文件名生成仅适用于不带引号的替换。
您可以通过运行来完全禁用文件名生成set -f
。如果您想利用IFS
拆分,这非常有用。请注意,set -f
完全禁用文件名生成,而不仅仅是在替换的输出上:set -f; echo *
始终打印*
.
#!/bin/bash
IFS=$'\n'
set -f
touch ab
status_arr=( $(git status --porcelain -b) )
for (( i=0; i<${#status_arr[@]}; i++ )); do
echo "${status_arr[$i]}"
echo "${status_arr[$i]:0:2}"
done
(在这里,文件名生成仍然处于禁用状态,并且考虑到 的元素status_arr
不能包含IFS
构造中的字符,因此可以安全地在echo
语句中省略双引号。但是,这是非常脆弱的 - 它严重依赖于数组的方式事实上,从那时起, 的状态set -f
和 的值IFS
就没有改变。 总是在变量和命令替换周围使用双引号,除非你有充分的理由将它们排除在外,并且你知道这样做是可以的。)