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 通配符完成所有工作(如果 R1
或 R2
将出现在文件名中,而不是目录中姓名):
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.fastq
和 R2.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.fastq
和 R2.fastq
,因此我们绝不会尝试使用同一个文件作为输入和输出。这是先前更改的结果:以前,我们的输出文件不能被视为输入,因为它们不符合最小深度。
在 for 循环中查找可能适合这个:
for i in R1 R2
do
find . -type f -name "*${i}*" -exec cat '{}' + >"$i.txt"
done
在某个目录中,我有很多包含一堆文本文件的目录。我正在尝试编写一个脚本,该脚本仅将每个目录中文件名中包含字符串“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 通配符完成所有工作(如果 R1
或 R2
将出现在文件名中,而不是目录中姓名):
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.fastq
和 R2.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.fastq
和R2.fastq
,因此我们绝不会尝试使用同一个文件作为输入和输出。这是先前更改的结果:以前,我们的输出文件不能被视为输入,因为它们不符合最小深度。
在 for 循环中查找可能适合这个:
for i in R1 R2
do
find . -type f -name "*${i}*" -exec cat '{}' + >"$i.txt"
done