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

Bash script to concatenate text files with specific substrings in filenames

在某个目录中,我有很多包含一堆文本文件的目录。我正在尝试编写一个脚本,该脚本仅将每个目录中文件名中包含字符串“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 循环,但是遍历了目录并做了一个嵌套循环到 运行,尽管目录中的每个文件都像这样

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

但它给出了关于“)”的意外标记错误。

如果我遗漏了一些基本知识,请提前道歉,我对 bash 还是很陌生。

您的 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

给 Reader

的注释

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

每个输出文件一个 cat

出于手头的目的,您可能只让 shell 通配符完成所有工作(如果 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

...这是因为 OS-dependent 对 command-line 长度的限制;上面给出的 find 命令会在每个 cat 命令上放置尽可能多的参数以提高效率,但仍会将它们分成多个调用,否则会超过限制。


Iterate-And-Test

如果您真的想遍历所有内容,然后测试名称,请考虑使用 case 语句来完成这项工作,这比使用 grep 只检查一行更有效:

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 循环开始时截断 它们恰好一次),并重用这些文件描述符而不是 re-opening 每个 cat 开头的输出文件。 (也就是说,运行 cat 每个文件一次——find -exec {} + 避免了——总的来说可能开销更​​大)。


运营Per-Directory

以上所有内容都可以更新为在 per-directory 基础上非常简单地工作。例如:

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,因此我们绝不会尝试使用同一个文件作为输入和输出。这是先前更改的结果:以前,我们的输出文件不能被视为输入,因为它们不符合最小深度。

在 for 循环中查找可能适合这个:

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