rename multiple folders under different conditions by matching regex patterns in a bash script?

huangapple go评论72阅读模式
英文:

rename multiple folders under different conditions by matching regex patterns in a bash script?

问题

I understand your request. Here is the translated content:


作为一个例子,假设我有一个包含以下文件夹的文件夹:

Universal 2023 02 15 Some Name
Universal 2023 02 15 Some Name and Words After
Sony Some Name 2023 02 15
Sony Some Name 2023 02 15 and Words After

期望输出

Some Name - 2023 02 15 - Universal
Some Name - 2023 02 15 - And Words After - Universal
Some Name - 2023 02 15 - Sony
Some Name - 2023 02 15 - and Words After – Sony

我为每种命名结构编写了一个命令。

Universal 2023 02 15 Some Name 将被重命名为:
Some Name - 2023 02 15 - Universal
使用此命令:

rename -v 's/([\s\S]+)\s((\d{4})\s(\d{2})\s(\d{2}))\s(([\s\S]+)\s([\s\S]+))/$6 - $2 - $1/g' *

Universal 2023 02 15 Some Name and Words After 将被重命名为:
Some Name - 2023 02 15 - And Words After - Universal
使用此命令:

rename -v 's/([\s\S]+)\s((\d{4})\s(\d{2})(\s)(\d{2}))\s((\w+)\s(\w+))\s([\s\S]+)/$7 - $2 - $10 - $1/g' *

Sony Some Name 2023 02 15 将被重命名为:
Some Name - 2023 02 15 - Sony
使用此命令:

rename -v 's/([\s\S]+)\s((\w+)\s(\w+))\s((\d{4})\s(\d{2})\s(\d{2}))/$2 - $5 - $1/g' *
  1. 最后,
    Sony Some Name 2023 02 15 and Words After 将被重命名为:
    Some Name - 2023 02 15 - and Words After - Sony
    使用此命令:
rename -v 's/([\s\S]+)\s((\d{4})\s(\d{2})\s(\d{2}))\s(([\w]+)\s([\w]+))\s([\s\S]+)/$6 - $2 - $9 - $1/g' *

当我想要重命名这些文件夹时,我必须将它们放入单独的文件夹中并运行相应的命令,然后在完成后将它们全部放回到同一个文件夹中。这非常麻烦。因此,我想在bash中编写一个脚本,避免将它们分开文件并在主文件夹中完成所有操作。在VS Code中,一切似乎都很顺利,除了重命名命令。这一行呈橙色... 这意味着某些东西丢失了,但我不知道是什么:

's/([\s\S]+)\s((\d{4})\s(\d{2})\s(\d{2}))\s(([\w]+)\s([\w]+))\s([\s\S]+)/$6 - $2 - $9 - $1/g'

请查看此链接以查看在VS Code中的脚本颜色:VS Code中的脚本颜色

我的脚本:

for i in $*/; do
        # 对于 Universal 2023 02 15 Some Name
        if [[ "$i" =~ ([\s\S]+)\s((\d{4})\s(\d{2})\s(\d{2}))\s(([\s\S]+)\s([\s\S]+)) ]];
                then
                        rename -v 's/([\s\S]+)\s((\d{4})\s(\d{2})\s(\d{2}))\s(([\s\S]+)\s([\s\S]+))/$6 - $2 - $1/g' *
        
        # 对于 Universal 2023 02 15 Some Name and Words After
        elif [[ "$i" =~ ([\s\S]+)\s((\d{4})\s(\d{2})(\s)(\d{2}))\s((\w+)\s(\w+))\s([\s\S]+) * ]];
                then
                        rename -v 's/([\s\S]+)\s((\d{4})\s(\d{2})(\s)(\d{2}))\s((\w+)\s(\w+))\s([\s\S]+)/$7 - $2 - $10 - $1/g' *

        # 对于 Sony Some Name 2023 02 15
        elif [[ "$i" =~ ([\s\S]+)\s((\w+)\s(\w+))\s((\d{4})\s(\d{2})\s(\d{2})) ]];
                then
                        rename -v 's/([\s\S]+)\s((\w+)\s(\w+))\s((\d{4})\s(\d{2})\s(\d{2}))/$2 - $5 - $1/g' *
        
        # 对于 Sony Some Name 2023 02 15 and Words After
        else [[ "$i" =~ ([\s\S]+)\s((\w+)\s(\w+))\s((\d{4})\s(\d{2})\s(\d{2})) ]];
                then
                        rename -v 's/([\s\S]+)\s((\d{4})\s(\d{2})\s(\d{2}))\s(([\w]+)\s([\w]+))\s([\s\S]+)/$6 - $2 - $9 - $1/g' *
        
        fi

done

在VS Code中的脚本颜色链接

请帮忙看看,谢谢!

Martin

英文:

As an example, let's say I have a folder containing these folders:

Universal 2023 02 15 Some Name
Universal 2023 02 15 Some Name and Words After
Sony Some Name 2023 02 15
Sony Some Name 2023 02 15 and Words After

Desired output

Some Name - 2023 02 15 - Universal
Some Name - 2023 02 15 - And Words After - Universal
Some Name - 2023 02 15 - Sony
Some Name - 2023 02 15 - and Words After – Sony

I wrote a command for every name structure.

1.
« Universal 2023 02 15 Some Name » will be renamed:
« Some Name - 2023 02 15 - Universal »
With this command:

rename -v 's/([\s\S]+)\s((\d{4})\s(\d{2})\s(\d{2}))\s(([\s\S]+)\s([\s\S]+))/$6 - $2 - $1/g' *

2.
« Universal 2023 02 15 Some Name and Words After » will be renamed:
« Some Name - 2023 02 15 - And Words After - Universal »
With this command:

rename -v 's/([\s\S]+)\s((\d{4})\s(\d{2})(\s)(\d{2}))\s((\w+)\s(\w+))\s([\s\S]+)/$7 - $2 - $10 - $1/g' *

3.
« Sony Some Name 2023 02 15 » will be renamed :
« Some Name - 2023 02 15 - Sony »
With this command :

rename -v 's/([\s\S]+)\s((\w+)\s(\w+))\s((\d{4})\s(\d{2})\s(\d{2}))/$2 - $5 - $1/g' *
  1. Finally,
    « Sony Some Name 2023 02 15 and Words After » will be renamed :
    « Some Name - 2023 02 15 - and Words After - Sony »
    With this command :
rename -v 's/([\s\S]+)\s((\d{4})\s(\d{2})\s(\d{2}))\s(([\w]+)\s([\w]+))\s([\s\S]+)/$6 - $2 - $9 - $1/g' *

When I want to rename these folders, I have to put them in separate folders and run the corresponding command, then put them all back in the same folder when I'm done. This is very annoying. So I thought of writing a script in bash to avoid having to file them separately and have everything done in the main folder. In the VS code, everything seems to work fine except for the renaming commands. This line is colored orange... Which means that something is missing but I don't know what it is:

's/([\s\S]+)\s((\d{4})\s(\d{2})\s(\d{2}))\s(([\w]+)\s([\w]+))\s([\s\S]+)/$6 - $2 - $9 - $1/g'

See this link to view the scipt in VS code colors:
https://i.stack.imgur.com/tosSv.png

My script :

for i in $*/; do
        # for Universal 2023 02 15 Some Name
        if [[ "$i" =~ ([\s\S]+)\s((\d{4})\s(\d{2})\s(\d{2}))\s(([\s\S]+)\s([\s\S]+)) ]];
                then
                        rename -v 's/([\s\S]+)\s((\d{4})\s(\d{2})\s(\d{2}))\s(([\s\S]+)\s([\s\S]+))/$6 - $2 - $1/g' *
        
        # for Universal 2023 02 15 Some Name and Words After
        elif [[ "$i" =~ ([\s\S]+)\s((\d{4})\s(\d{2})(\s)(\d{2}))\s((\w+)\s(\w+))\s([\s\S]+) * ]];
                then
                        rename -v 's/([\s\S]+)\s((\d{4})\s(\d{2})(\s)(\d{2}))\s((\w+)\s(\w+))\s([\s\S]+)/$7 - $2 - $10 - $1/g' *

        # for Sony Some Name 2023 02 15
        elif [[ "$i" =~ ([\s\S]+)\s((\w+)\s(\w+))\s((\d{4})\s(\d{2})\s(\d{2})) ]];
                then
                        rename -v 's/([\s\S]+)\s((\w+)\s(\w+))\s((\d{4})\s(\d{2})\s(\d{2}))/$2 - $5 - $1/g' *
        
        # for Sony Some Name 2023 02 15 and Words After
        else [[ "$i" =~ ([\s\S]+)\s((\w+)\s(\w+))\s((\d{4})\s(\d{2})\s(\d{2})) ]];
                then
                        rename -v 's/([\s\S]+)\s((\d{4})\s(\d{2})\s(\d{2}))\s(([\w]+)\s([\w]+))\s([\s\S]+)/$6 - $2 - $9 - $1/g' *
        
        fi

done

the script in color for VS code. My commands are all orange...

Anyone can help me please!!!!!!!!
Many Thanks!
Martin

答案1

得分: 3

此脚本可以重命名所有四个目录(除了And Words After中的大写字母):

rename -n 's/(\w+) (.* )?(\d{4} \d{2} \d{2})( \w+ \w+)?(.*)/
          ($2 ? $2 : (substr($4,1). " ")) .
          "- " .
          $3 .
          " -" .
          ($2 ? ($4 ? $4 : "") . $5 : $5) . ($5 ? " - " : " ") . $1
          /e' *

一旦您对结果满意,删除-n

英文:

This script can rename all four directories (apart from the capitalization in And Words After):

rename -n 's/(\w+) (.* )?(\d{4} \d{2} \d{2})( \w+ \w+)?(.*)/
          ($2 ? $2 : (substr($4,1). " ")) .
          "- " .
          $3 .
          " -" .
          ($2 ? ($4 ? $4 : "") . $5 : $5) . ($5 ? " - " : " ") . $1
          /e' *

Remove -n once you are satisfied of the result.

答案2

得分: 2

以下是翻译好的代码部分:

# 尝试这段经过[Shellcheck](https://www.shellcheck.net/)清理的代码:

#! /bin/bash -p

sep_rx='[[:space:]]+'
part_rx='[^[:space:]]+'
company_rx=$part_rx
name_rx="${part_rx}${sep_rx}${part_rx}"
date_rx="[[:digit:]]{4}${sep_rx}[[:digit:]]{2}${sep_rx}[[:digit:]]{2}"
after_rx="${part_rx}(${sep_rx}${part_rx})*"

cdn_rx="^($company_rx)$sep_rx($date_rx)$sep_rx($name_rx)"
cdna_rx="^($company_rx)$sep_rx($date_rx)$sep_rx($name_rx)$sep_rx($after_rx)"
cnd_rx="^($company_rx)$sep_rx($name_rx)$sep_rx($date_rx)"
cnda_rx="^($company_rx)$sep_rx($name_rx)$sep_rx($date_rx)$sep_rx($after_rx)"

for d in */; do
    dir=${d%/}
    if [[ $dir =~ $cdn_rx ]]; then
        company=${BASH_REMATCH[1]}
        date=${BASH_REMATCH[2]}
        name=${BASH_REMATCH[3]}
        newdir="$name - $date - $company"
    elif [[ $dir =~ $cdna_rx ]]; then
        company=${BASH_REMATCH[1]}
        date=${BASH_REMATCH[2]}
        name=${BASH_REMATCH[3]}
        words_after=${BASH_REMATCH[4]}
        newdir="$name - $date - $words_after - $company"
    elif [[ $dir =~ $cnd_rx ]]; then
        company=${BASH_REMATCH[1]}
        name=${BASH_REMATCH[2]}
        date=${BASH_REMATCH[3]}
        newdir="$name - $date - $company"
    elif [[ $dir =~ $cnda_rx ]]; then
        company=${BASH_REMATCH[1]}
        name=${BASH_REMATCH[2]}
        date=${BASH_REMATCH[3]}
        words_after=${BASH_REMATCH[4]}
        newdir="$name - $date - $words_after - $company"
    else
        printf 'ERROR: Failed to match: %s\n' "$dir" >&2
        exit 1
    fi
    mv -v -- "$dir" "$newdir"
done

请注意,这是您提供的代码的翻译部分,不包括其他内容。

英文:

Try this Shellcheck-clean code:

#! /bin/bash -p
sep_rx='[[:space:]]+'
part_rx='[^[:space:]]+'
company_rx=$part_rx
name_rx="${part_rx}${sep_rx}${part_rx}"
date_rx="[[:digit:]]{4}${sep_rx}[[:digit:]]{2}${sep_rx}[[:digit:]]{2}"
after_rx="${part_rx}(${sep_rx}${part_rx})*"
cdn_rx="^($company_rx)$sep_rx($date_rx)$sep_rx($name_rx)\$"
cdna_rx="^($company_rx)$sep_rx($date_rx)$sep_rx($name_rx)$sep_rx($after_rx)\$"
cnd_rx="^($company_rx)$sep_rx($name_rx)$sep_rx($date_rx)\$"
cnda_rx="^($company_rx)$sep_rx($name_rx)$sep_rx($date_rx)$sep_rx($after_rx)\$"
for d in */; do
dir=${d%/}
if [[ $dir =~ $cdn_rx ]]; then
company=${BASH_REMATCH[1]}
date=${BASH_REMATCH[2]}
name=${BASH_REMATCH[3]}
newdir="$name - $date - $company"
elif [[ $dir =~ $cdna_rx ]]; then
company=${BASH_REMATCH[1]}
date=${BASH_REMATCH[2]}
name=${BASH_REMATCH[3]}
words_after=${BASH_REMATCH[4]}
newdir="$name - $date - $words_after - $company"
elif [[ $dir =~ $cnd_rx ]]; then
company=${BASH_REMATCH[1]}
name=${BASH_REMATCH[2]}
date=${BASH_REMATCH[3]}
newdir="$name - $date - $company"
elif [[ $dir =~ $cnda_rx ]]; then
company=${BASH_REMATCH[1]}
name=${BASH_REMATCH[2]}
date=${BASH_REMATCH[3]}
words_after=${BASH_REMATCH[4]}
newdir="$name - $date - $words_after - $company"
else
printf 'ERROR: Failed to match: %s\n' "$dir" >&2
exit 1
fi
mv -v -- "$dir" "$newdir"
done
  • The long, and duplicated, regular expressions in the original code are very difficult to read, so I've tried to break them down into named parts.
  • Regular expression extensions such as \s, \S, and \d don't work consistently with =~ in Bash, so I've used portable character classes instead (e.g. [^[:space:]] for \S).
  • See mkelement0's excellent answer to How do I use a regex in a shell script? to learn more about using regular expressions in Bash code.
  • See Bash Pitfalls #35 (if [[ $foo =~ 'some RE' ]]) for an explanation of why I put all the regular expressions in variables.
  • The rename utility isn't available on all systems, and there are at least two very different versions of it in circulation, so I've used the standard mv utility instead. See Why is the rename utility on Debian/Ubuntu different than the one on other distributions, like CentOS?.
  • The code works on the given examples, but it may well fail on other directory names. You'll need to check the regular expressions and modify them as necessary.
  • The -p in the #! /bin/bash -p shebang prevents Bash from reading configuration files and environment variables that could change how it behaves (e.g. by defining functions that override standard utilities, or by defining environment variables that make standard utilities behave in non-standard ways). It makes Bash programs more reliable, and reduces the "it works on my machine" effect. It may also avoid some security issues (see Shell Script Security - Apple Developer).
  • The parentheses in regular expression strings like "^($company_rx)$sep_rx($date_rx)$sep_rx($name_rx)\$" delimit "capture groups". Matches for regular expression parts between parentheses are copied into the BASH_REMATCH array. For instance, the second set of parentheses in the given string surround the date pattern, so the matched date is copied into index 2 in BASH_REMATCH (${BASH_REMATCH[2]}). The \$ at the end of the reqular expression is a backslash-escaped literal dollar character, which is a regular expression metacharacter matching the end of the string being matched. See POSIX Extended Regular Expressions for a full description of the Bash regular expressions. (Though some implementations, inconsistently, support extensions like \s etc.) The backslash in \$ is to prevent the dollar causing an expansion (which it normally does within double quotes).
  • The */ in for d in */ expands to the list of slash-terminated names (excluding names beginning with the dot character (.)) of directories under the current directory. See glob - Greg's Wiki.
  • dir=${d%/} causes dir to get the value of d with a trailing slash removed. See Removing part of a string (BashFAQ/100 (How do I do string manipulation in bash?)).
  • printf 'ERROR: Failed to match: %s\n' "$dir" prints the string ERROR: Failed to match: %s with a trailing newline and with %s replaced by the value of dir. It is a safer version of echo "ERROR: Failed to match: $dir", which doesn't work in general. See the accepted, and excellent, answer to Why is printf better than echo? for more information. See the POSIX printf page for detailed information about the printf utility.
  • The >&2 at the end of printf 'ERROR: ...' "$dir" >&2 causes the output to go to the "standard error" stream ("stderr") instead of the "standard output" stream ("stdout"). One practical consequence of this is that the error message will be visible even if the (standard) output of the program is redirected. It is normal to do that for error messages, and other diagnostic messages (warning, debugging, ...). See BashGuide/InputAndOutput - Greg's Wiki (wooledge.org).
  • The -- in mv -v -- "$dir" "$newdir" is to ensure that there will not be a problem if the code is ever used with names that begin with hyphen/dash (-), even if the code is copied into a different program. Without the -- leading hyphens would cause the directory names to be interpreted as strings of options to mv. See Bash Pitfalls #2 (cp $file $target) and Bash Pitfalls #3 (Filenames with leading dashes).

huangapple
  • 本文由 发表于 2023年5月14日 23:42:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/76248337.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定