将文件名列添加到数百个bash文件中



我有数百个约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

循环浏览目录中的文件
xargsparallel可以取代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指出内存问题。

最新更新