简体   繁体   English

bash重命名不同目录中文件名的一部分

[英]bash rename parts of filenames in different directories

This is simple and has been asked/answered many times but not when files are in different directories. 这很简单,已被询问/回答过很多次,但是当文件位于不同目录中时却并非如此。 I want to do this: Renaming part of a filename , but with the files in different sub-directories. 我要这样做: 重命名文件名的一部分 ,但文件位于不同的子目录中。

This is what I've tried: 这是我尝试过的:

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

but it tries to rename the directory too (because subset is also in the directory name) and then errors because that directory name is not found. 但它也会尝试重命名目录(因为子集也位于目录名中),然后由于未找到该目录名而出错。 What am I missing? 我想念什么?

Here is the general directory structure: 这是常规的目录结构:

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

Here is what I'd like: 这是我想要的:

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

But it attempts to rename the directories too so you get the error: 但是它也会尝试重命名目录,因此会出现错误:

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

You can check if the file is a directory and, if not, rename it. 您可以检查文件是否为目录,如果不是,则将其重命名。

For example, [ -d FILE ] will return true if FILE exists and is a directory. 例如,如果FILE存在并且是目录,则[ -d FILE ]将返回true

Instead of trying to move everything from the root, cycle all directories and for each one, cycle all files. 不要尝试从根目录移走所有内容,而是循环所有目录,并为每个目录循环所有文件。

EG 例如

#!/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
$ 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

I think it all comes down to finding the proper regex. 我认为一切都归结于找到合适的正则表达式。 Here I substitute the string subset.run for subset5.run : 在这里,我将字符串subset.runsubset5.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'

But we can for example use basename and dirname to strip the path from filename and directory and run sed only on filename: 但是,例如,我们可以使用basenamedirname从文件名和目录中删除路径,并仅对filename运行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'

A little escaping to learn. 有点逃脱学习。 This will probably be more readable to you: 您可能会更容易理解:

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'

Something like this should do: 这样的事情应该做:

#!/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

If your files strictly follow the structure, you could just change your code to: 如果文件严格遵循结构,则可以将代码更改为:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM