用文件名替换给定列的标题

用文件名替换给定列的标题

我有很多 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

在此输入图像描述@YetAnotherUser,请查看有关我的请求的示例文件的图像:“用文件名替换给定列的标题”

答案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命令失败,右侧部分&& 不被执行,原始数据将保持安全

相关内容