我有很多 vcf 文件
HR001.vcf
HR002.vcf
HR003.vcf
HR004.vcf
HR005.vcf
HR006.vcf
HR007.vcf
HR008.vcf
.
.
在里面第10栏每个文件的列标题是 $i。在每个文件中,我想用文件的基本名称替换 $i 。例如,对于文件 HR001.vcf,$i=HR001,对于 HR002.vcf $i=HR002 等...是否有一种简单的方法可以在 unix 中执行此操作。我拥有一台 MacBook Pro,但我对此很陌生。这些实际上是带有制表符分隔字段的 VCF 文件。是的,每个文件有 236 行应该跳过。我对以 #CHROM 开头的行感兴趣,即第 237 行,第 237 行的第 10 列包含 $i
答案1
我会用perl
:
perl -F'\t' -i -lape '
if ($F[0] eq "#CHROM" && $F[9] eq q($i)) {
$F[9] = ($ARGV =~ s/\.vcf$//r);
$_ = join "\t", @F
}' -- *.vcf
答案2
像这样的脚本可以完成工作:
cd /path/to/direcrtory
for i in *.vcf
do
awk '{if (FNR==1) $10=FILENAME; print}' "$i" >"$i.tmp" && mv -f "$i.tmp" "$i"
done
“魔法”位于包含输入文件名FILENAME
的变量中awk
答案3
答案4
假设您的文件是以空格分隔的,这应该可以工作:
for f_name in HR[0-9]*.vcf; do
awk -v f="${f_name%.*}" 'NR == 1 {$10 = f}1' "$f_name" > "$f_name.tmp"
mv "$f_name.tmp" "$f_name"
done
在目录内循环并获取每个vcf
文件。然后从文件名中删除扩展名${f_name%.*}
并将其作为参数传递给awk
.
awk
将使用它作为文件名来进行修改。笔记:这需要在vcf
文件的同一目录中运行,如果您想从另一个路径运行它,请使用以下命令:
for f_name in /some/full/path/HR[0-9]*.vcf; do
# remove the path
f="${f_name##*/}"
awk -v f="${f%.*}" 'NR == 1 {$10 = f}1' "$f_name" > "$f_name.tmp"
mv "$f_name.tmp" "$f_name"
done
如果文件不是空格分隔修复awk FS
。
根据@Ed Morton 的改进,针对新请求进行编辑
我对以 #CHROM 开头的行感兴趣,即第 237 行,第 237 行的第 10 列包含 $i
for f_name in /some/full/path/HR[0-9]*.vcf; do
# remove the path
f="${f_name##*/}"
awk -F'\t' -v f="${f%.*}" 'NR == 237 {$10 = f}1' "$f_name" > "$f_name.tmp" && mv "$f_name.tmp" "$f_name"
done
($10 = f)
这个新版本的脚本仅在您喜欢的字段和您想要的行中替换文件名(NR == 237)
。该awk
参数-F\t
设置如何awk
查看行并在字段中拆分行。
再次感谢@Ed Morton,它改进了原始脚本:正如您所看到的语句:mv "$f_name.tmp" "$f_name"
这是用新文件(由 生成)的内容覆盖旧文件的命令awk
被压缩在一行中:awk '' file > tmp && mv tmp file
这样,如果awk
命令失败,右侧部分&&
不被执行,原始数据将保持安全