Bash脚本用于将文本文件与文件名中的特定子字符串连接起来

时间:2017-01-12 21:57:45

标签: bash fastq

在某个目录中,我有许多包含一堆文本文件的目录。我正在尝试编写一个脚本,该脚本只将每个目录中文件名为“R1”的文件连接到该特定目录中的一个文件中,另一个文件中包含“R2”的文件。这是我写的,但它没有用。

#!/bin/bash

for f in */*.fastq; do

    if grep 'R1' $f ; then
        cat "$f" >> R1.fastq
    fi

    if grep 'R2' $f ; then
        cat "$f" >> R2.fastq
    fi

done

我没有错误,文件按预期创建,但它们是空文件。谁能告诉我我做错了什么?

感谢大家的快速而详细的回复!我想我的问题并不是很清楚,但我需要脚本只连接每个特定目录中的文件,以便每个目录都有一个新文件(R1和R2)。我试着做了

cat /*R1*.fastq >*/R1.fastq 

但它给了我一个模棱两可的重定向错误。我也试过了Charles Duffy的循环,但循环遍历这些目录并做了一个嵌套循环来运行目录中的每个文件,如此

for f in */; do
   for d in "$f"/*.fastq;do
     case "$d" in
       *R1*) cat "$d" >&3
       *R2*) cat "$d" >&4
     esac
   done 3>R1.fastq 4>R2.fastq
done

但它给出了关于')'的意外令牌错误。

如果我错过了一些基本的东西,我提前抱歉,我仍然很吵。

3 个答案:

答案 0 :(得分:4)

读者注意事项

请在考虑此答案时查看问题的编辑记录;通过问题编辑,几个部分的相关性降低了。

每个输出文件一个cat

出于目的,您可以让shell globbing完成所有工作(如果R1R2将在文件名中,而不是目录名称:

set -x # log what's happening!
cat */*R1*.fastq >R1.fastq
cat */*R2*.fastq >R2.fastq

每个输出文件一个find

相比之下,如果文件数量非常大,则可能需要find

find . -mindepth 2 -maxdepth 2 -type f -name '*R1*.fastq' -exec cat '{}' + >R1.fastq
find . -mindepth 2 -maxdepth 2 -type f -name '*R2*.fastq' -exec cat '{}' + >R2.fastq

...这是因为依赖于操作系统的命令行长度限制;上面给出的find命令会尽可能多地为每个cat命令添加参数以提高效率,但仍会将它们分成多个调用,否则将超出限制。

迭代与测试

如果您确实想要迭代所有内容,然后测试名称,请考虑作业的case语句,这比使用grep检查一行更有效:< / p>

for f in */*.fastq; do
  case $f in
    *R1*) cat "$f" >&3
    *R2*) cat "$f" >&4
  esac
done 3>R1.fastq 4>R2.fastq

请注意使用文件描述符3和4分别写入R1.fastqR2.fastq - 这样我们只打开输出文件一次(因此截断< / {>他们恰好一次)当for循环开始时,重用这些文件描述符而不是在每个cat开头重新打开输出文件。 (也就是说,每个文件运行一次cat - find -exec {} +避免 - 可能会增加平衡开销。

按目录操作

以上所有内容都可以更新,以便在每个目录的基础上非常简单地工作。例如:

for d in */; do
  find "$d" -name R1.fastq -prune -o -name '*R1*.fastq' -exec cat '{}' + >"$d/R1.fastq"
  find "$d" -name R2.fastq -prune -o -name '*R2*.fastq' -exec cat '{}' + >"$d/R2.fastq"
done

只有两个重大变化:

  • 我们不再指定-mindepth,以确保我们的输入文件仅来自子目录。
  • 我们从输入文件中排除R1.fastqR2.fastq,因此我们绝不会尝试将同一个文件用作输入和输出。这是先前更改的结果:以前,我们的输出文件不能被视为输入,因为它们没有达到最小深度。

答案 1 :(得分:1)

您的grep正在搜索文件内容而不是文件名。你可以用这种方式重写它:

for f in */*.fastq; do
  [[ -f $f ]] || continue
  if [[ $f = *R1* ]]; then
    cat "$f" >> R1.fastq
  elif [[ $f = *R2* ]]; then
    cat "$f" >> R2.fastq
  fi
done

答案 2 :(得分:1)

在forloop中查找可能适合这个:

  for i in R1 R2 
    do 
      find . -type f -name "*${i}*" -exec cat '{}' + >"$i.txt"
   done