连接文件并在文件之间插入新行

188

我有多个文件,想要使用 cat 命令将它们拼接起来。 假设这些文件是:

File1.txt 
foo

File2.txt
bar

File3.txt
qux

我想要拼接文件,最终的文件应该像这样:

foo

bar

qux

不要使用常规的cat File*.txt > finalfile.txt,而是考虑使用:

foo
bar 
qux

如何正确地做到这一点?


3
可能是如何在使用“cat”连接文件时包含空行?的重复问题。 - legoscia
9个回答

187

你可以这样做:

for f in *.txt; do (cat "${f}"; echo) >> finalfile.txt; done

在运行上述命令之前,请确保文件finalfile.txt不存在。

如果您被允许使用awk,可以执行以下操作:

awk 'FNR==1{print ""}1' *.txt > finalfile.txt

24
AWK '{print $0}' *.txt - timger
9
这种方法有一明显缺陷,即在结尾(第一种替代方案)或开头(第二种替代方案)会有空行。你可以使用awk 'FNR==1 && NR > 1 ...'来避免这个问题。 - tripleee
6
如果在 done 之后加上 >finalfile.txt,则可以覆盖写入而不是追加写入,这将消除需要在循环之前确保文件不存在或为空的要求。 - tripleee
2
受@timger的启发,awk '1' *.txt :smile: - hustnzj

79

如果您的文件数量足够少,可以将每个文件列出来并在Bash中使用进程替换,在每对文件之间插入一个换行符:

cat File1.txt <(echo) File2.txt <(echo) File3.txt > finalfile.txt

2
太棒了!谢谢。 - Bob Kocisko
这对我来说非常有效,用于创建.pem文件的Letsencrypt证书。 - leeman24
我正在尝试在 xargs 内部执行此操作,但失败了,有什么提示吗? … | xargs -I{} kubectl -n alex exec {} -- cat blah.log <(echo) >> blahblah.logs cat: /dev/fd/63: 没有那个文件或目录,命令以退出码 1 终止。 - tuxErrante
1
@tuxErrante 我认为问题不在于 xargs,而是 kubectl,它正在接受一个远程运行的命令,而 <(echo) 是在本地运行的。也许可以尝试使用 -- bash -c "cat blah.log <(echo)" - Robert Tupelo-Schneck
它在阿尔卑斯山Shell上无法运行,但以下命令可以,感谢提示!sh -c“echo -e'\ n \ n'| cat - /.. /logs/a.log" - tuxErrante
是的,我正在处理具有不同扩展名的文件,所以这个正好合适! - undefined

41

如果是我在做的话,我会使用sed:

sed -e '$s/$/\n/' -s *.txt > finalfile.txt

这个sed模式中“$”有两个含义,第一,“$”仅匹配最后一行号(作为一个应用模式的行范围),第二,它在替换模式中匹配行结尾。

如果你的sed版本没有“-s”选项(分别处理输入文件),你可以使用循环来完成所有操作:

for f in *.txt ; do sed -e '$s/$/\n/' $f ; done > finalfile.txt

4
使用GNU sed命令:sed -s '$G' *.txt > finalfile.txt ,将所有.txt文件的末尾添加一个空白行,并将结果保存到finalfile.txt中。 - Ruud Helderman
1
只有一个流!这应该是被接受的答案! - Yassine ElBadaoui
1
小伙伴们要小心啊,我刚刚因为在 find 中使用了 *.txt 的位置而崩溃了我的电脑,这意味着该文件被追加到了自己上面! - xeruf

17

这在Bash中有效:

for f in *.txt; do cat $f; echo; done

与使用 >>(追加)的答案不同,此命令的输出可以被导入其他程序。

示例:

  • for f in File*.txt; do cat $f; echo; done > finalfile.txt
  • (for ... done) > finalfile.txt(括号是可选的)
  • for ... done | less(导入 less)
  • for ... done | head -n -1(这将去除尾随的空行)

对于每个文件f,执行以下操作:打印“# ------ $f ------”并显示文件内容,然后打印一个空行。同时也要打印文件名。 - Eric

14

如果你喜欢的话,可以使用xargs来完成它,但主要思想仍然是相同的:

find *.txt | xargs -I{} sh -c "cat {}; echo ''" > finalfile.txt

3
谢谢。我发现在Bash中使用xargs比循环要容易得多。 - RawwrBag

10

这就是我在 OsX 10.10.3 上刚刚完成的方式。

for f in *.txt; do (cat $f; echo '') >> fullData.txt; done

由于简单的“echo”命令没有参数,因此不会插入新行。


这将字符串放在文件末尾;我该如何将其插入到每个文件之间? - onassar

6
你可以使用 grep 命令并带上参数 -h 来避免输出文件名。
grep -h "" File*.txt

将提供:

foo
bar 
qux

这个命令是最简单和容易的。谢谢。 - Shezan
这个命令是最简单和容易的。谢谢。 - undefined

3
在Python中,这将使用空行在文件之间进行连接(,可以抑制添加一个额外的尾随空行):
print '\n'.join(open(f).read() for f in filenames),

这是一个丑陋的Python单行程序,在shell中调用并将输出打印到文件中:

python -c "from sys import argv; print '\n'.join(open(f).read() for f in argv[1:])," File*.txt > finalfile.txt

0
一个与POSIX兼容的解决方案是使用cat命令,但在每个文件之间插入一个只包含空行的文件。
nl=`mktemp`
printf '\n' > $nl
cat file1 $nl file2 $nl file3
rm $nl

一个更高级的版本可能是这样的。

nl=`mktemp`
printf '\n' > $nl
find file1 file2 file3 -print0 |
  xargs -0 printf "%s\0$nl\0" |
  tr '\0' '\n' |
  sed -n '$!p;$q' |
  tr '\n' '\0' |
  xargs -0 cat
rm $nl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接