【问题标题】:Expanding an Index in a Bash Loop Script in a Combination with a Wildcard and an Underscore [duplicate]结合通配符和下划线扩展 Bash 循环脚本中的索引 [重复]
【发布时间】:2017-05-08 16:27:58
【问题描述】:

我有 80 对以下类型的文件:

170309-N701-S517_S1_L001_R1_001.fastq.gz
170309-N701-S517_S1_L001_R2_001.fastq.gz
170309-N701-S502_S2_L001_R1_001.fastq.gz
170309-N701-S502_S2_L001_R2_001.fastq.gz
170309-N701-S503_S3_L001_R1_001.fastq.gz
170309-N701-S503_S3_L001_R2_001.fastq.gz
..
170309-N710-S507_S79_L001_R1_001.fastq.gz
170309-N710-S507_S79_L001_R2_001.fastq.gz
170309-N710-S508_S80_L001_R1_001.fastq.gz
170309-N710-S508_S80_L001_R2_001.fastq.gz

基本上,一对由以下文件组成:

170309-N701-S517_S<ID>_L001_R1_001.fastq.gz
170309-N701-S517_S<ID>_L001_R2_001.fastq.gz

其中 ID 从 1 到 80 不等。

我想创建 80 个名为 S1..S80 的子文件夹,并将每一对放在相应的文件夹中,例如

170309-N701-S517_S1_L001_R1_001.fastq.gz
170309-N701-S517_S1_L001_R2_001.fastq.gz

转到子文件夹 S1

170309-N701-S502_S2_L001_R1_001.fastq.gz
170309-N701-S502_S2_L001_R2_001.fastq.gz

转到子文件夹 S2

等等。

我写了以下脚本:

#!/bin/bash
for i in {1..80}
do
   mkdir S$i
   mv "*_S"$i"_*" S$i
done

但它没有按预期工作。它创建了子文件夹 S1..S80。但是,它没有移动任何文件。而是产生了

mv: cannot stat `*_S1_*': No such file or directory
mv: cannot stat `*_S2_*': No such file or directory

等等。

我做错了什么?能改一下剧本吗?

【问题讨论】:

  • Globs 不在引号中展开,请去掉引号。
  • 您的下划线导致搜索出现问题,因为它们不会在引号内并且需要转义。该行应该mv *\_$i\_*
  • 去掉引号后,你需要像${i}那样用{}包围变量,否则它会寻找一个名为$i_的变量。
  • @JNevill 下划线不需要转义,问题是,正如 ccarton 提到的,它试图扩展 $i_。使用{} 正确扩展变量是正确的解决方案。
  • @CharlesDuffy:我认为这个问题不应该被当作一个骗子来解决。当然,这里的答案类似于链接中的答案,但问题不是,因此standard SO guidelines 不是骗子。据我所知,这个问题最大的罪过是标题太不具体了。

标签: bash loops escaping wildcard


【解决方案1】:

您尝试对 mv "*_S"$i"_*" S$i 行执行的操作称为 variable expansion,如果您作为该扩展的一部分想要将变量与其他字符连接起来,您需要让 BASH 知道什么是变量什么是朴素的性格。为此,您可以将变量名括在花括号中。例如:

var=FOO
echo "BAR ${var} BAZ"
# BAR FOO BAZ

在你的循环中:

touch \
  170309-N701-S517_S1_L001_R1_001.fastq.gz\
  170309-N701-S517_S1_L001_R2_001.fastq.gz\
  170309-N701-S502_S2_L001_R1_001.fastq.gz\
  170309-N701-S502_S2_L001_R2_001.fastq.gz\
  170309-N701-S503_S3_L001_R1_001.fastq.gz\
  170309-N701-S503_S3_L001_R2_001.fastq.gz

for i in {1..80}
do
if test -n "$(find . -maxdepth 1 -name "*_S${i}_*" -print -quit)"
then
   mkdir "S${i}"
   mv *"_S${i}_"* "S$i"
fi
done

if-then-fi 位只是为了避免为不存在的文件创建目录。完全可选。

还请注意,必须使用通配符*不加引号才能与mv一起使用,因为在"..."'...'中,*被视为文字
但是在find 命令中可以看到一个例外,其中双引号之间的内容将在传递给find 时被扩展。为了避免在这种情况下扩展,参数可以用引号括起来('...'

【讨论】:

  • 要清楚——当且仅当紧随其后的字符——如下划线——在 shell 变量名中有效时,这是必要的。
  • find 在性能方面有点昂贵,这意味着您正在使用两种不同的 glob 机制(find 在那里,shell 后面几行用于mv - - 如果您启用了 shell extglobs,以及更有趣的 glob 表达式,它们可能不匹配)。我建议matches=( *_S"$i"_* ); if [[ -e ${matches[0]} || -L ${matches[0]} ]]; then ... 避免外部命令启动成本,并确保您以后可以mv "${matches[@]}" "S$i" 重用单个全局操作的结果而不是重新计算它..
  • @CharlesDuffy:如果我从头开始编写这个例程,它看起来会大不相同。我发布的是最小的努力改进。
  • 为完美主义辩护——我们正在这里建立一个教育资源;一个答案的生命远远超出了最初为其编写的 OP 完成的时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-17
  • 2013-11-14
  • 2016-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多