【问题标题】:bash rename parts of filenames in different directoriesbash 重命名不同目录中的部分文件名
【发布时间】:2019-02-05 17:49:23
【问题描述】:

这很简单,并且已被多次询问/回答,但当文件位于不同目录时却没有。我想做这个: Renaming part of a filename,但文件位于不同的子目录中。

这是我尝试过的:

for f in */*run?.t; do mv -i -- "$f" "${f//subset/subset5}"; done

但它也尝试重命名目录(因为子集也在目录名称中),然后由于找不到该目录名称而出错。我错过了什么?

这是一般目录结构:

Gene1_subset/
       Gene1file_subset.run1.t
       Gene1file_subset.run2.t
Gene2_subset/
       Gene2file_subset.run1.t
       Gene2file_subset.run2.t

这是我想要的:

Gene1_subset/
           Gene1file_subset5.run1.t
           Gene1file_subset5.run2.t
Gene2_subset/
           Gene2file_subset5.run1.t
           Gene2file_subset5.run2.t

但它也会尝试重命名目录,因此您会收到错误消息:

cannot move Gene1_subset/Gene1file_subset.run1 to Gene1_subset5/Gene1file_subset5.run1 : No such file or directory

【问题讨论】:

  • 能否展示重命名前后的整个目录结构?

标签: bash directory rename


【解决方案1】:

您可以检查文件是否为目录,如果不是,请重命名。

例如,如果FILE 存在并且是一个目录,[ -d FILE ] 将返回true

不要试图从根目录移动所有内容,而是循环所有目录,并为每个目录循环所有文件。

例如

#!/bin/bash
rename() {
  for dir in *; do
    if [ -d "${dir}" ]; then
      cd "${dir}"
      for f in *run?; do
        ! [ -d "${f}" ] && mv -i -- "${f}" "${f//subset/subset5}"
      done
      cd ..
    fi
  done
}
rename || exit $?
exit 0

【讨论】:

  • 感谢您的建议。我尝试了上述方法并得到了错误:mv: cannot stat ‘*run?.t’: No such file or directory。然后它让我退出服务器。虽然,我检查并确实在每个文件夹中,我都有扩展名为 run1.t、run2.t 等的文件
  • 你说得对,我忘了换目录...我在函数中添加了一些cds
  • 嗯.. 还是不太对劲。在上面复制(逐行尝试捕获问题)它在最后一个} 之后完成,因此不使用rename || exit $? exit 0。但它实际上并没有改变文件名。
  • 它是一个函数,在} 之后不会执行任何代码,除非您按名称调用该函数(在这种情况下为rename)。无论如何,在*run? 之后有一个.t 是我从你以前的代码中复制的,它不应该存在......我检查了这个函数,顺便说一句,它现在可以工作了[:干杯!
  • 原谅我,因为我是 bash 新手。这些文件是 something.subset.run1.t 等,所以这是需要的。抱歉,原帖中没有。另外,我的函数在第二个 } 之后“完成”,所以它不允许我完成第二个函数的输入:rename || exit $? exit 0
【解决方案2】:
$ mkdir -p Gene1_subset Gene2_subset
$ touch Gene1_subset/Gene1file_subset.run1 Gene1_subset/Gene1file_subset.run2 Gene2_subset/Gene2file_subset.run1 Gene2_subset/Gene2file_subset.run2

我认为这一切都归结为找到合适的正则表达式。在这里,我将字符串subset.run 替换为subset5.run

$ find . -name '*.run[0-9]' -exec sh -c 'mv -v "{}" "$(echo {} | sed "s/subset\.run/subset5\.run/")"' \;
renamed './Gene2_subset/Gene2file_subset.run2' -> './Gene2_subset/Gene2file_subset5.run2'
renamed './Gene2_subset/Gene2file_subset.run1' -> './Gene2_subset/Gene2file_subset5.run1'
renamed './Gene1_subset/Gene1file_subset.run2' -> './Gene1_subset/Gene1file_subset5.run2'
renamed './Gene1_subset/Gene1file_subset.run1' -> './Gene1_subset/Gene1file_subset5.run1'

但我们可以例如使用basenamedirname 从文件名和目录中去除路径并仅在文件名上运行 sed:

$ find . -name '*.run[0-9]' -exec sh -c "mv -v \"{}\" \"\$(dirname \"{}\")/\$(basename \"{}\" | sed 's/subset/subset5/')\"" \;
renamed './Gene2_subset/Gene2file_subset.run2' -> './Gene2_subset/Gene2file_subset5.run2'
renamed './Gene2_subset/Gene2file_subset.run1' -> './Gene2_subset/Gene2file_subset5.run1'
renamed './Gene1_subset/Gene1file_subset.run2' -> './Gene1_subset/Gene1file_subset5.run2'
renamed './Gene1_subset/Gene1file_subset.run1' -> './Gene1_subset/Gene1file_subset5.run1'

有点逃避学习。这对您来说可能更具可读性:

for f in */*run?; do f2=$(basename "$f"); mv -vi -- "$f" "$(dirname "$f")/${f2//subset/subset5}"; done
renamed 'Gene1_subset/Gene1file_subset.run1' -> 'Gene1_subset/Gene1file_subset5.run1'
renamed 'Gene1_subset/Gene1file_subset.run2' -> 'Gene1_subset/Gene1file_subset5.run2'
renamed 'Gene2_subset/Gene2file_subset.run1' -> 'Gene2_subset/Gene2file_subset5.run1'
renamed 'Gene2_subset/Gene2file_subset.run2' -> 'Gene2_subset/Gene2file_subset5.run2'

【讨论】:

    【解决方案3】:

    应该这样做:

    #!/usr/bin/env bash
    
    shopt -s extglob globstar nullglob
    for f in **/*_subset.run+([0-9]).t; do
        tmp=${f##*_}
        mv -i -- "$f" "${f%_*}_${tmp/et/et5}"
    done
    

    如果您的文件严格遵循结构,您只需将代码更改为:

    mv -i -- "$f" "${f/file_subset/file_subset5}"
    

    【讨论】:

      猜你喜欢
      • 2013-12-05
      • 1970-01-01
      • 2011-12-03
      • 2014-01-06
      • 2015-08-02
      • 2021-11-27
      • 2016-08-03
      • 2018-04-10
      • 2015-12-17
      相关资源
      最近更新 更多