我有数百个约1000万行的全基因组关联研究文件。对于file1.txt
:
SNP BP B SE P
123 12 0.1 0.01 0.1
...
我想在每个文件的末尾添加一列,该列只包含该文件的名称,这样file1.txt
的最终结果就是:
SNP BP B SE P name
123 12 0.1 0.01 0.1 file1.txt
...
我想为数百个文件这样做。目前我可以添加一个列使用:
for file in *.txt; do awk 'BEGIN{OFS="t"}{print $0, FILENAME}' $file; done
这将打印整个文件,但不会覆盖现有文件。我只想在现有的数据帧中添加一列。非常感谢任何建议!
tmp=$(mktemp) || { ret="$?"; printf 'Failed to create temp filen'; exit "$ret"; }
for file in *.txt; do
awk 'BEGIN{OFS="t"} {print $0, (FNR>1 ? FILENAME : "name")}' "$file" > "$tmp" &&
mv -- "$tmp" "$file" || exit
done
如果你有GNU awk,并且没有太多超过shell参数限制的文件,你可以只使用对awk的调用,而不使用周围的shell循环和显式创建的临时文件(它仍然会在后台使用临时文件,就像所有有"就地"编辑选项的工具一样(:
awk -i inplace 'BEGIN{OFS="t"} {print $0, (FNR>1 ? FILENAME : "name")}' *.txt
sed
溶液
建议的数据文件内容:
SNP BP B SE P
123 12 0.1 0.01 0.1
用不同的文件名制作几个副本
ls
file1.dat file2.dat
cat file*
SNP BP B SE P
123 12 0.1 0.01 0.1
SNP BP B SE P
123 12 0.1 0.01 0.1
循环浏览目录中的文件xargs
或parallel
可以取代for
以更快地完成,但如果您有多余的资源,则会使用更多的资源。
for f in *.dat ; do
sed -i "s/(^SNP.*)/1tname/;s/(^[0-9].*)/1t$f/g" "$f";
done
文件现在应该有附加的列
cat file*
SNP BP B SE P name
123 12 0.1 0.01 0.1 file1.dat
SNP BP B SE P name
123 12 0.1 0.01 0.1 file2.dat
这可能适用于您(GNU sed和并行(:
parallel sed -i ''s/$/t{}/'' {} ::: *.txt
这将用该文件的名称标记每个文件的每一行,同时并行处理每个文件。
要添加标题行,请使用:
parallel sed -i ''1s/$/tname/;1!s/$/t{}/'' {} ::: *.txt
注意:当使用并行时,使用环绕的'...'
而不是''...''
,以避免外壳中具有特殊含义的字符。
如果ed
可用/可接受。
for file in *.txt; do
printf '%sn' "2,$s|^(.*)$|1 $file|" '1s/$/ name/' 'w' 'q' | ed -s "$file"
done
上述代码需要手动调整最后一个字段的name
列的空格。这个需要一个像@Ed_Morton所做的那样的临时文件和column
命令。
tmp=$(mktemp)
for file in *.txt; do
ed -s "$file" <<-EOF
H
2,$s|^(.{1,})$|1 $file|
1s/$/ name/
w $tmp
,d
0r !column -t $tmp
w
Q
EOF
done
注意事项:ed
会将整个文件读入内存。
对OP的样本数据中的1400万行进行了测试,一切都很好,但更多的是一个错误。(YMMV(
?
Memory exhausted
感谢@EdMorton指出内存问题。