我有一堆具有相同扩展名的文件(假设为 .txt),我想将它们连接起来。我正在使用cat *.txt > concat.txt
,但我想在每个文件之间添加一个新行,以便在 concat.txt 中区分它们。
是否可以使用单个 bash 命令而不是诸如此类的实现来完成此操作这?
谢谢
答案1
不是一个命令,而是一个简单的一行:
for f in *.txt; do cat -- "$f"; printf "\n"; done > newfile.txt
这会给出这个错误:
cat: newfile.txt: input file is output file
但你可以忽略它,至少在 GNU/Linux 系统上是这样。 Stéphane Chazelas 在评论中指出,显然,在其他系统上这可能会导致无限循环,因此为了避免它,请尝试:
for f in *.txt; do
[[ "$f" = newfile.txt ]] || { cat -- "$f"; printf "\n"; }
done > newfile.txt
或者只是不要.txt
向输出文件添加扩展名(无论如何,它是不需要的,并且根本没有任何区别),这样它就不会包含在循环中:
for f in *.txt; do cat -- "$f"; printf "\n"; done > newfile
答案2
使用 GNU sed
:
sed -s -e $'$a\\\n' ./*.txt >concat.out
这会将所有数据连接起来,concat.out
同时在每个处理的文件末尾附加一个空行。
-s
GNU 的选项使sed
地址$
与最后一行匹配每个文件,而不是像往常一样,所有数据的最后一行。该a
命令在给定位置追加一行或多行,添加的数据是换行符。换行符被编码为$'\n'
,即“C 字符串”,这意味着我们使用的 shell 可以理解这些(如bash
或zsh
)。否则必须将其添加为文字换行符:
sed -s -e '$a\
' ./*.txt >concat.out
实际上,'$a\\'
似乎'$a\ '
也有效,但我不完全确定为什么。
如果有人认为该a
命令太麻烦而无法正确执行,那么这也是可行的:
sed -s -e '${p;g;}' ./*.txt >concat.out
这些变体中的任何一个也会在最后一个文件的输出末尾插入一个空行。如果不需要最后的换行符,请在sed '$d'
重定向到输出文件之前通过传递整体结果来删除它:
sed -s -e '${p;g;}' ./*.txt | sed -e '$d' >concat.out
答案3
使用 GNU awk
:
gawk -v RS='^$' -v ORS= '{
print sep $0; sep="\n";
}' ./file*.txt >single.file
文件名中的前缀点斜杠./
用于避免像读取此类文件那样命名的文件file=x.txt
出现awk
问题字符串作为一个多变的当这些出现在awk
代码之后时;
另一种 GNUawk
方法是:
gawk 'BEGINFILE{if (ARGIND>1) print ""};1' ./file*.txt >single.txt
这是更好的,因为即使最后一行不以换行符结尾,它也会添加一个空行,并且可以避免将整个文件加载到内存中。
还有一种sed
替代方法,但要删除最后\n
一条 ewline,您应该添加另一个管道sed ... |
来删除它。
sed -s '$s/$/\n/' file*.txt >single.file
答案4
zsh
有一个P
glob 限定符,用于为由带有任意参数的 glob 生成的每个文件名添加前缀。
虽然它通常用于为cmd *.txt(P[-i])
每个文件名添加给定选项的前缀,但您可以使用此处在每个文件之前插入任何给定的文件。可以使用 来完成包含空行的临时文件=(print)
,因此您可以执行以下操作:
() { cat file*.txt(P[$1]); } =(print)
在 Linux 或 Cygwin 上,您还可以执行以下操作:
cat file*.txt(P[/dev/stdin]) <<< ''
仅在非空文件之间添加空行:
awk 'NR > 1 && FNR == 1 {print ""}; {print}' ./file*.txt