我有一组 gzip 压缩文件,我想将它们组合成一个文件。它们各自具有相同的格式。我想仅保留第一个文件的标头信息,并在后续文件中跳过它。
举一个简单的例子,我有四个相同的文件,其内容如下:
$ gzcat file1.gz
# header
1
2
我想结束
# header
1
2
1
2
1
2
1
2
实际上,我可以拥有不同数量的文件,因此我希望能够以编程方式执行此操作。这是我迄今为止拥有的非编程解决方案......
cat <(gzcat file1.gz) <(tail -q -n +2 <(gzcat file2.gz) <(gzcat file3.gz) <(gzcat file4.gz))
该命令有效,但它是“硬编码”的,可以处理四个文件,我需要将其推广到任意数量的文件。如果有帮助的话,我正在使用bash
外壳。我的偏好是性能(实际上文件可能有数百万行长),所以如果速度很快,我可以接受不太优雅的解决方案。
答案1
如果您在问题中显示的命令基本上有效(对于硬编码的文件数量),那么
first=1
for f in file*.gz
do
if [ "$first" ]
then
gzcat "$f"
first=
else
gzcat "$f"| tail -n +2
fi
done > collection_single_file
应该为你工作。我希望逻辑相当清楚。查看所有文件(根据您的文件名更改通配符)。如果它是列表中的第一个,gzcat
那么您将获得整个文件(包括标题)。否则,使用tail
剥离标头。处理完一个文件后,其他文件将不再是第一个。
这会调用tail
氮−1 次,而不是一次(就像你的答案)。除此之外,我的答案应该与你的答案相同。
答案2
一个变体G-Man的解决方案不使用单独的变量来跟踪第一个文件:
set -- file*.gz
{
gzcat "$1"; shift
for file do
gzcat "$file" | sed '1d'
done
} >combined.txt
这将解压缩第一个文件,然后循环遍历其余文件,将每个文件传递给sed
删除第一行的简短脚本。输出被重定向到combined.txt
.
该set -- file*.gz
命令将位置参数($1
、$2
等,统称为数组$@
)设置为与给定模式匹配的文件名。解压缩后将shift
其$1
从数组中删除。循环遍历数组中剩余的文件名,也可以写成
for file in "$@"; do
gzcat "$file" | sed '1d'
done
它{ ... }
允许我们一次性将命令的输出重定向到一个文件。
甚至更短,附加假设“标题行”始终以字符开头#
(如问题中的示例),并且数据中没有其他此类行:
gzcat file*.gz | awk 'NR > 1 && /^#/ { next } 1' >combined.txt
或者,
gzcat file*.gz | sed '2,${ /^#/d; }' >combined.txt
#
如果它出现在未压缩数据的组合内容的第二行或后面,则这两种方法都会跳过从 开始的任何行。