我目前使用以下简化命令到删除尾随空格和在文件末尾添加换行符在需要的地方:
find . -type f -exec sed -i -e 's/[ \t]\+\(\r\?\)$/\1/;$a\' {} \+
正如您很快就会看到的,这有两个问题:它会改变二进制文件它会在文件末尾添加一个换行符␍␊ 行分隔符。在提交或类似操作时,这些修改很容易撤消或跳过git gui
,但我想最大限度地减少*恢复量。为此:
有没有办法跳过所有的文件如果任何行与sed
?中的正则表达式匹配
* 我知道可能存在没有 ␀ 字符的二进制文件,并且可能存在故意混合换行符或 ␀ 的文件。但我正在寻找需要最少人工干预的解决方案。我可以可以想象列出我想要操作的所有文件扩展名,但这将是一个非常长的列表,必须不断检查,并且由于名称冲突,二进制文件仍然有可能漏掉。
复杂的解决方法:
while IFS= read -r -d '' -u 9
do
if [[ "$(file -bs --mime-type -- "$REPLY")" = text/* ]]
then
sed -i -e 's/[ \t]\+\(\r\?\)$/\1/;$a\' -- "$REPLY"
else
echo "Skipping $REPLY" >&2
fi
done 9< <(find . -type f -print0)
答案1
如果您相信 的git
关于什么是二进制文件的观点,您可以使用git grep
来获取非二进制文件的列表。假设t.cpp
是一个文本文件,并且ls
是一个二进制文件,两者都已签入:
$ ls
t.cpp ls
$ git grep -I --name-only -e ''
t.cpp
该-I
选项的含义是:
-I
不匹配二进制文件中的模式。
将其与您的sed
表达式结合起来:
$ git grep -I --name-only -z -e '' | \
xargs -0 sed -i.bk -e 's/[ \t]\+\(\r\?\)$/\1/;$a\'
(-z
/xargs -0
帮助处理奇怪的文件名。)
查看git grep
手册页以获取其他有用的选项 ---no-index
或者--cached
可能会有所帮助,具体取决于您想要操作的文件集。
答案2
如果任何行与 sed 中的正则表达式匹配,有没有办法跳过整个文件?
就在这里。
# test case for skipping file if a sed regex match succeeds
echo 'Hello, world!' > hello_world.txt
cat hello_world.txt
ls -li hello_world.txt
sed -i -e '/.*Hello.*/{q;}; s/world/WORLD/g' hello_world.txt # skips file
sed -i -e '/.*HeLLo.*/{q;}; s/world/WORLD/g' hello_world.txt
答案3
下面是一个 Perl 脚本,它迭代其参数(必须是文件名)并向每个不以换行符结尾的文件附加换行符。包含空字节的文件将被跳过。已经以换行符结尾的文件不会被修改。包含 CR 的文件会附加 CRLF,其他文件则仅附加 LF。未经测试。
#!/usr/bin/env perl
foreach my $f (@ARGV) {
open F, "<", $f or die;
my $last = undef;
my $cr = 0;
while (<>) {if (/\0/) {undef $last; break} $last = $_; ++$cr if /\r$/}
close F;
if (defined $last && $last !~ /\n\Z/) {
open F, ">>", $f or die;
print($cr ? "\r\n" : "\n");
close F or die;
}
}