使用 awk 将新行替换为空格

使用 awk 将新行替换为空格

我有一个由目录中所有文件生成的文本文件。我想使用此文件作为我拥有的脚本的输入,但我需要以特定方式格式化文本文件才能正确解析。

目前,文本文件(文件名列表)的格式如下:

A1_R1.fastq.gz
A1_R2.fastq.gz
A2_R1.fastq.gz
A2_R2.fastq.gz
A3_R1.fastq.gz
A3_R2.fastq.gz

我需要每个样本的配对读取(具有相同名称但不同值的文件RN)位于同一行,并用制表符分隔:

A1_R1.fastq.gz A1_R2.fastq.gz
A2_R1.fastq.gz A2_R2.fastq.gz
A3_R1.fastq.gz A3_R2.fastq.gz

由于我有 >1000 个条目,我希望有一种使用 awk 或类似方法来修改文件的方法,但我对 awk 没有太多经验。

答案1

假设这些行按照您在问题中显示的方式排序,那么该paste命令可以执行以下操作:

$ paste - - < input_file
A1_R1.fastq.gz  A1_R2.fastq.gz
A2_R1.fastq.gz  A2_R2.fastq.gz
A3_R1.fastq.gz  A3_R2.fastq.gz

如果您希望用默认 TAB 以外的任何内容分隔列,请使用-d选项paste。对于一个简单的空间:

$ paste -d ' ' - -  <input_file

答案2

对于显示的输入,所有成对的行都彼此相邻,任何 awk 所需的就是:

$ awk '{ORS=(NR%2 ? "\t" : RS)} 1' file
A1_R1.fastq.gz  A1_R2.fastq.gz
A2_R1.fastq.gz  A2_R2.fastq.gz
A3_R1.fastq.gz  A3_R2.fastq.gz

或者如果它们尚未配对:

$ shuf file > file1
$ cat file1
A3_R2.fastq.gz
A2_R2.fastq.gz
A1_R1.fastq.gz
A3_R1.fastq.gz
A1_R2.fastq.gz
A2_R1.fastq.gz

因此,如果您不介意添加对以下内容的调用,则需要配对sort

$ awk '{ORS=(NR%2 ? "\t" : RS)} 1' <(sort file1)
A1_R1.fastq.gz  A1_R2.fastq.gz
A2_R1.fastq.gz  A2_R2.fastq.gz
A3_R1.fastq.gz  A3_R2.fastq.gz

或者在 awk 中将它们配对:

$ awk -F'_' -v OFS='\t' '$1 in a{print a[$1], $0; next} {a[$1]=$0}' file1
A3_R2.fastq.gz  A3_R1.fastq.gz
A1_R1.fastq.gz  A1_R2.fastq.gz
A2_R2.fastq.gz  A2_R1.fastq.gz

请注意,在最后一个脚本中,在某些情况下,R2 字段会在 R1 伙伴之前输出。如果这是一个问题,那么您可以在打印时订购它们:

$ awk -F'_' -v OFS='\t' '
    $1 in a { print (a[$1] < $0 ? a[$1] OFS $0 : $0 OFS a[$1]); next }
    { a[$1] = $0 }
' file1
A3_R1.fastq.gz  A3_R2.fastq.gz
A1_R1.fastq.gz  A1_R2.fastq.gz
A2_R1.fastq.gz  A2_R2.fastq.gz

如果您的输入文件实际上有数百万行那么在大多数情况下添加delete a[$1];之前next会加快执行时间,如果它只有几千行,则可能不值得(权衡调用delete a[$1]每对的开销与拥有大的开销哈希表a[])。

如果您需要对输出行进行排序,那么您需要使用 GNU awk PROCINFO["sorted_in"]

$ awk '{a[$0]} END{PROCINFO["sorted_in"]="@ind_str_asc"; for (i in a) printf "%s%s", i, (++n % 2 ? "\t" : RS) }' file1
A1_R1.fastq.gz  A1_R2.fastq.gz
A2_R1.fastq.gz  A2_R2.fastq.gz
A3_R1.fastq.gz  A3_R2.fastq.gz

但是,就像使用 的解决方案一样sort,当输入中的数字可以是多个数字时,它不会产生预期的顺序,因为例如,A11之前会按字母顺序排序A2- 您需要将每个字符串分成单独的字母顺序和数字部分,并分别对每个部分进行排序,或者将它们标准化为在每个位置中始终具有相同的数字字母和数字字符,例如在排序之前映射A1_R1到或类似。000A0001_000R0001

答案3

使用(以前称为 Perl_6)


如果它总是成对读取,并且它们总是处于正确的顺序:

~$ raku -e '.put for lines.rotor(2);'  file 

如果总是成对读取,但文件名乱序:

~$ raku -e '.put for lines.sort.rotor(2);'  file 

Raku 是 Perl 家族的一种编程语言。与@EdMorton 的出色答案类似awk,如果您必须对文件名进行排序,则结果顺序将按字母顺序排列。

https://docs.raku.org/routine/lines
https://docs.raku.org/routine/rotor
https://raku.org


如果有时文件丢失,无论文件名顺序如何:

~$ raku -ne 'BEGIN my %hash;     \  
             %hash.append: .match(/^ (<-[_]>+) _ /).[0]  =>  $_;   \
             END for %hash.sort { .values.put };'   file

#OR

~$ raku -ne 'BEGIN my %hash;     \ 
             %hash.append: m/^ (<-[_]>+) _ /.[0]  =>  $_;    \
             END for %hash.sort { .values.put };'   file 

第二种方法适用于输入数据不原始(缺少文件名等)的情况。与 Perl 本身一样,Raku 有一个awk类似命令行模式,通过-ne(“非自动打印行”)标志来调用。上面,我们BEGIN通过声明一个%hash.在这个“one-liner”的主体中,每一行都是.matched on (这里.match$_.match调用函​​数 on 的缩写$_,即保存行文本的主题变量)。

.match(或)例程m/ … //运算符查找^行首,后跟<-[_]>+一个或多个除下划线之外的任何字符_(即自定义负字符类),后跟_下划线本身。 (仅供参考,自定义积极的字符类看起来像这样<+[ … ]>:)。括号将前导非下划线文本捕获为$0或更简单.[0]

因此,每一行都被解读为.[0]as keywith $_(整行) as value。用于 => 创建键/值对。哈希数据结构保持唯一keys,因此每次key遇到相同的值时,都会简单地获取新值append。在END阅读行的最后,我们sort按下键,然后取出put每个.values键。

https://course.raku.org/essentials/associatives/hashes/
https://docs.raku.org/language/hashmap
https://raku.org


输入示例:

A1_R1.fastq.gz
A1_R2.fastq.gz
A2_R1.fastq.gz
A2_R2.fastq.gz
A3_R1.fastq.gz
A3_R2.fastq.gz

示例输出(任一方法):

A1_R1.fastq.gz A1_R2.fastq.gz
A2_R1.fastq.gz A2_R2.fastq.gz
A3_R1.fastq.gz A3_R2.fastq.gz

答案4

我是 的粉丝sed,它可以很容易地连接相邻的线对:

sed 'N; s/\n/'$'\t''/' < input > output

中间$'\t'的是 bash-ism,传达单个制表符,与 没有直接关系sed。在实践中,我可能会使用文字制表符,但这在这种媒体中并没有清晰地体现出来:sed 'N; s/\n/ /' < input > output

解释:

  • sed从阅读第一行开始
  • 指示读取下一行输入并将其附加到当前输入行,Nsed以换行符分隔。
  • s命令用制表符替换换行符。
  • 如果没有更多命令,则写入前面步骤的结果,然后sed读取下一行并循环回来
  • sed当它耗尽输入时终止。

相关内容