k0p*_*kus 237 shell bash text-processing newlines
使用版本控制系统时,当差异显示为 时,我对噪音感到恼火No newline at end of file。
所以我想知道:如何在文件末尾添加换行符以摆脱这些消息?
l0b*_*0b0 254
给你:
sed -i -e '$a\' file
Run Code Online (Sandbox Code Playgroud)
或者对于 OS X sed:
sed -i '' -e '$a\' file
Run Code Online (Sandbox Code Playgroud)
仅当\n文件尚未以换行符结尾时,才会在文件末尾添加。所以如果你运行它两次,它不会添加另一个换行符:
$ cd "$(mktemp -d)"
$ printf foo > test.txt
$ sed -e '$a\' test.txt > test-with-eol.txt
$ diff test*
1c1
< foo
\ No newline at end of file
---
> foo
$ echo $?
1
$ sed -e '$a\' test-with-eol.txt > test-still-with-one-eol.txt
$ diff test-with-eol.txt test-still-with-one-eol.txt
$ echo $?
0
Run Code Online (Sandbox Code Playgroud)
Pat*_*ity 74
为了递归地清理一个项目,我使用了这个 oneliner:
git ls-files -z | while IFS= read -rd '' f; do tail -c1 < "$f" | read -r _ || echo >> "$f"; done
Run Code Online (Sandbox Code Playgroud)
解释:
git ls-files -z列出存储库中的文件。它采用可选模式作为附加参数,如果您想将操作限制在某些文件/目录中,这在某些情况下可能很有用。作为替代方案,您可以使用find -print0 ...或类似的程序来列出受影响的文件 - 只需确保它发出NUL-delimited 条目。
while IFS= read -rd '' f; do ... done 遍历条目,安全地处理包含空格和/或换行符的文件名。
tail -c1 < "$f" 从文件中读取最后一个字符。
read -r _ 如果缺少尾随换行符,则以非零退出状态退出。
|| echo >> "$f" 如果前一个命令的退出状态非零,则向文件追加一个换行符。
sr_*_*sr_ 44
看一看:
$ echo -n foo > foo
$ cat foo
foo$
$ echo "" >> foo
$ cat foo
foo
Run Code Online (Sandbox Code Playgroud)
所以echo "" >> noeol-file应该做到这一点。(或者您的意思是要求识别这些文件并修复它们?)
编辑删除""从echo "" >> foo(见@ yuyichao的评论)
EDIT2增加""再次(但看到@Keith汤普森的评论)
enz*_*tib 19
使用ed. 此解决方案仅影响最后一行,并且仅当\n缺少时:
ed -s file <<< w
Run Code Online (Sandbox Code Playgroud)
它基本上可以通过脚本打开文件进行编辑,脚本是w将文件写回磁盘的单个命令。它基于ed(1)手册页中的这句话:
限制
(……)
如果文本(非二进制)文件没有以换行符结尾,
然后 ed 在读/写它时附加一个。在二进制的情况下
文件,ed 不会在读/写时附加换行符。
Bar*_* IO 19
一种简单的、可移植的、符合 POSIX 标准的向文本文件添加不存在的最终换行符的方法是:
[ -n "$(tail -c1 file)" ] && echo >> file
Run Code Online (Sandbox Code Playgroud)
这种方法不需要读取整个文件;它可以简单地寻找 EOF 并从那里开始工作。
这种方法也不需要在背后创建临时文件(例如 sed -i),因此硬链接不受影响。
只有当命令替换的结果是非空字符串时,echo 才会在文件中附加一个换行符。请注意,只有在文件不为空且最后一个字节不是换行符时才会发生这种情况。
如果文件的最后一个字节是换行符,则 tail 返回它,然后命令替换将其剥离;结果是一个空字符串。-n 测试失败并且 echo 不运行。
如果文件为空,命令替换的结果也是一个空字符串,再次 echo 不会运行。这是可取的,因为空文件不是无效的文本文件,也不等同于具有空行的非空文本文件。
Ale*_*der 16
无论如何添加换行符:
echo >> filename
Run Code Online (Sandbox Code Playgroud)
这是一种在添加换行符之前检查末尾是否存在换行符的方法,使用 Python:
f=filename; python -c "import sys; sys.exit(open(\"$f\").read().endswith('\n'))" && echo >> $f
Run Code Online (Sandbox Code Playgroud)
最快的解决方案是:
[ -n "$(tail -c1 file)" ] && printf '\n' >>file
Run Code Online (Sandbox Code Playgroud)
真的很快。
在中等大小的文件上,seq 99999999 >file这需要几毫秒。
其他解决方案需要很长时间:
[ -n "$(tail -c1 file)" ] && printf '\n' >>file 0.013 sec
vi -ecwq file 2.544 sec
paste file 1<> file 31.943 sec
ed -s file <<< w 1m 4.422 sec
sed -i -e '$a\' file 3m 20.931 sec
Run Code Online (Sandbox Code Playgroud)适用于 ash、bash、lksh、mksh、ksh93、attsh 和 zsh,但不适用于 yash。
如果您需要一个可移植到 yash(以及上面列出的所有其他 shell)的解决方案,它可能会变得更加复杂:
f=file
if [ "$(tail -c1 "$f"; echo x)" != "$(printf '\nx')" ]
then printf '\n' >>"$f"
fi
Run Code Online (Sandbox Code Playgroud)
测试文件的最后一个字节是否是换行符的最快方法是只读取最后一个字节。这可以通过tail -c1 file. 但是,当文件中的最后一个字符是 UTF- 8 值。
查找文件的最后一个字节是否为新行的正确的、符合 POSIX 标准的所有(合理的)shell 方法是使用 xxd 或 hexdump:
tail -c1 file | xxd -u -p
tail -c1 file | hexdump -v -e '/1 "%02X"'
Run Code Online (Sandbox Code Playgroud)
然后,比较上面的输出0A将提供一个可靠的测试。
避免在其他空文件中添加新行很有用。
将无法提供最后一个字符的文件0A,当然:
f=file
a=$(tail -c1 "$f" | hexdump -v -e '/1 "%02X"')
[ -s "$f" -a "$a" != "0A" ] && echo >> "$f"
Run Code Online (Sandbox Code Playgroud)
简短而甜蜜。这需要很少的时间,因为它只读取最后一个字节(寻找 EOF)。文件大也没关系。然后只在需要时添加一个字节。
不需要或使用临时文件。硬链接不受影响。
如果此测试运行两次,则不会添加另一个换行符。