我正在尝试创建一个计算本地git仓库中已更改,添加,新等文件的函数。我正在使用git status -s
,如果文件未跟踪,则返回?? somefile
。 ??
部分是我检查以确定它是什么类型的条目的部分。
但是在使用
if [ "${lPrefix}" == '??' ]; then ...
所有更改uu, m, a, d
都被视为未跟踪。
如何确保检查按预期工作,并且仅在未跟踪文件上触发。
我尝试将??
替换为:
"??"
- >没有用,猜测是因为?
是一个通配符"\?\?"
- >没用,预计它会起作用'\?\?'
- >不希望它起作用,因为'
意味着文字编辑:按照Jack Bracken的要求 lPrefix设置如下(它也是问题所在的所有代码)
while IFS='' read -r line || [[ -n "$line" ]]; do
arr=($line)
lPrefix="${arr[0]}"
if [ "${lPrefix}" == '??' ]; then lPrefix="N"; fi
...
done < <(git status -s)
答案 0 :(得分:4)
这里有两个问题:
?
) ,git
的术语中称为瓷器命令)status
。首先,解决第二个问题:使用git status --porcelain
或更确切地说git status -z
(这意味着--porcelain
),使条目以空字节分隔(这使得解析安全)。然后我们将使用read
和空分隔符-d ''
要解决第一个问题,我会尝试了解您要执行的操作:git status -s
(或者更确切地说git status --porcelain
)给出的每一行都以代码开头;如果此代码为??
,则将lPrefix
设置为N
,否则将lPrefix
设置为找到的代码。
然后应该这样做:
while IFS= read -r -d '' line; do
lPrefix=${line:0:2}
filename=${line:3}
if [[ $lPrefix = '??' ]]; then lPrefix=N; fi
# other stuff...
done < <(git status -z)
为什么你的命令失败了?
很难确切地说出来,但它可能与您用来分割字符串的反模式有关:
arr=($line)
lPrefix="${arr[0]}"
这非常糟糕!它是一个(可悲的)非常常见的反模式,由那些不太了解(或忽略)shell如何执行分词和文件名扩展的人给出:如果扩展$line
包含glob字符(即*
},?
,[...]
和扩展的全局如果extglob
打开),那么shell不仅会执行分词,还会执行文件名扩展,也就是说,它会匹配每个glob找到文件。
在您的情况下,如果您碰巧在当前目录中有2个字符的文件名(例如,名为ab
的文件),则${arr[0]}
将成为此文件!看:
$ mkdir test
$ git init
$ touch a ab abc xy
$ ls
a ab abc xy
$ git status -s
?? a
?? ab
?? abc
?? xy
$ while IFS='' read -r line; do arr=($line); declare -p arr; done < <(git status -s)
declare -a arr='([0]="ab" [1]="xy" [2]="a")'
declare -a arr='([0]="ab" [1]="xy" [2]="ab")'
declare -a arr='([0]="ab" [1]="xy" [2]="abc")'
declare -a arr='([0]="ab" [1]="xy" [2]="xy")'
$ # see the filename expansion?
另请注意,我没有使用
while read state file; do ... ; done < <(git status -s)
或类似的东西(如在接受的答案中)获取状态和文件名,因为这会修剪state
和file
中的前导和尾随换行符。
答案 1 :(得分:2)
这里??
应该不是问题。我猜您的变量${lPrefix}
未正确设置。我尝试了以下代码并且它有效:
git status -s | while read state file ; do
[ "${state}" = '??' ] && echo "${state} new"
done
然而,使用bash并不是很有效。我强烈建议您使用awk
,如下所示:
git status -s | awk '{c[$1]++}END{for(i in c){print i, c[i]}}'
多行版本中的说明:
#!/usr/bin/awk -f
# c(ount) is an associative array, indexed by the values of the first column
# like '??', 'u', 'm' and so on. Each time they appear we
# increment their count by 1
{ c[$1]++ }
# Once the end of input has been reached we iterate trough
# c and print each index along with it's count.
END {
for(i in c) {
print i, c[i]
}
}
答案 2 :(得分:2)
每当使用git
的输出编写脚本时,最好使用-z
来处理空分隔符。
我建议您将脚本更改为:
git status -z | while read -r -d '' status file; do
[ "$status" = '??' ] && printf '%s\n' "$file"
done
如评论中所述(感谢),如果不加引号,??
将扩展为名称中只有两个字符的任何文件的名称。添加引号可确保将问号视为文字字符。无论是使用[
还是使用特定于bash的[[
,添加引号都会产生一致的行为。
当你想要简单的字符串匹配时,我的建议是将[
与=
一起使用(并且不要忘记引用变量,一如既往!)。
我假设您计划为每个??
状态的文件执行一些shell命令。否则,请记住shell不是为文本处理而设计的,因此您应该使用awk / sed等工具。
答案 3 :(得分:-1)
您可以尝试:
cat 1.txt | grep -Po "(?<=\?\?\ ).*"
1.txt是:
?? aaa
!! bb
?? vv
~~ xx
击穿:
grep -Po
Perl表达式,仅显示匹配的行
("?<="
从中开始
\?\?\
转义问号和空格
).*
在初始匹配后发生的任何事情