基于多种模式重命名文件的更好方法

use*_*854 10 unix linux bash shell sed

我下载的很多文件都有文件名中的垃圾/垃圾邮件,例如

[ www.crap.com ] file.name.ext

www.crap.com - file.name.ext

我想出了两种处理它们的方法,但它们看起来都很笨重:

参数扩展:

if [[ ${base_name} != ${base_name//\[+([^\]])\]} ]]
then
    mv -v "${dir_name}/${base_name}" "${dir_name}/${base_name//\[+([^\]])\]}" &&
        base_name="${base_name//\[+([^\]])\]}"
fi

if [[ ${base_name} != ${base_name//www.*.com - /} ]]
then
    mv -v "${dir_name}/${base_name}" "${dir_name}/${base_name//www.*.com - /}" &&
        base_name="${base_name//www.*.com - /}"
fi

# more of these type of statements; one for each type of frequently-encountered pattern
Run Code Online (Sandbox Code Playgroud)

然后使用echo/sed:

tmp=`echo "${base_name}" | sed -e 's/\[[^][]*\]//g' | sed -e 's/\s-\s//g'`
mv "${base_name}" "{tmp}"
Run Code Online (Sandbox Code Playgroud)

我觉得参数扩展是两者中最差的,但我喜欢它,因为我能够保留分配给文件的相同变量,以便在重命名后进行进一步处理(上面的代码用于为每个文件调用的脚本中文件下载完成后).

所以无论如何我希望有一个更好/更清洁的方式来做上述事情,比我自己更有知识的人可以告诉我,最好是以一种方式让我轻松地将旧/原始变量重新分配给新的/重命名的文件.

谢谢

Mic*_*ald 7

利用以下经典模式:

 job_select /path/to/directory| job_strategy | job_process
Run Code Online (Sandbox Code Playgroud)

where job_select负责选择作业对象,job_strategy为这些对象准备处理计划并job_process最终执行计划.

这假设文件名不包含垂直条|或换行符.

job_select功能

 # job_select PATH
 #  Produce the list of files to process
 job_select()
 {
   find "$1" -name 'www.*.com - *' -o -name '[*] - *'
 }
Run Code Online (Sandbox Code Playgroud)

find命令可以检查文件系统维护的文件的所有属性,如创建时间,访问时间,修改时间.通过告知find不要下载到已安装的文件系统,允许多少递归级别,也可以控制文件系统的探索方式.通常将管道附加到find命令以基于文件名执行更复杂的选择.

避免job_select函数输出中包含隐藏目录内容的常见缺陷.例如,目录CVS,.svn,.svk.git使用由相应的源代码控制管理工具,它几乎总是错的,包括在输出的内容job_select的功能.通过无意中批处理这些文件,可以轻松地使受影响的工作副本无法使用.

job_strategy函数

# job_strategy
#  Prepare a plan for renaming files
job_strategy()
{
  sed -e '
    h
    s@/www\..*\.com - *@/@
    s@/\[^]]* - *@/@
    x
    G
    s/\n/|/
  '
}
Run Code Online (Sandbox Code Playgroud)

此命令读取输出job_select并为重命名作业制定计划.该计划由文本行表示,其中两个字段由字符分隔|,第一个字段是文件的旧名称,第二个字段是文件的新计算文件,它看起来像

[ www.crap.com ] file.name.1.ext|file.name.1.ext
www.crap.com - file.name.2.ext|file.name.2.ext
Run Code Online (Sandbox Code Playgroud)

用于制定计划的特定程序基本上是无关紧要的,但通常sed在示例中使用; awk或者perl为此.让我们一起来看看sed这里使用的-script:

h       Replace the contents of the hold space with the contents of the pattern space.
…       Edit the contents of the pattern space.
x       Swap the contents of the pattern and hold spaces.
G       Append a newline character followed by the contents of the hold space to the pattern space.
s/\n/|/ Replace the newline character in the pattern space by a vertical bar.
Run Code Online (Sandbox Code Playgroud)

使用多个过滤器来准备计划可能更容易.另一种常见情况是使用该stat命令将创建时间添加到文件名.

job_process函数

# job_process
#  Rename files according to a plan
job_process()
{
   local oldname
   local newname
   while IFS='|' read oldname newname; do
     mv "$oldname" "$newname"
   done
}
Run Code Online (Sandbox Code Playgroud)

所述输入字段分隔符 IFS被调整为让读功能的输出job_strategy.声明oldnamenewname本地在大型程序中很有用,但可以在非常简单的脚本中省略.job_process可以调整该功能以避免覆盖现有文件并报告有问题的项目.

关于shell程序中的数据结构 注意使用管道将数据从一个阶段传输到另一个阶段:学徒通常依靠变量来表示这些信息,但事实证明这是一个笨拙的选择.相反,优选的是表示数据作为表格文件或作为移动从一个过程到其它,在这种形式的表格数据流,数据可以很容易地通过有力工具等加工sed,awk,join,pastesort-只是举最常见的.


F. *_*uri 7

两个答案:使用重命名或使用

由于有些人不喜欢perl,我写了我的 bash only版本

使用该rename命令重命名文件.

介绍

是的,这是一个典型的rename命令工作,它精确地设计用于:

man rename | sed -ne '/example/,/^[^ ]/p'
   For example, to rename all files matching "*.bak" to strip the
   extension, you might say

           rename 's/\.bak$//' *.bak

   To translate uppercase names to lower, you'd use

           rename 'y/A-Z/a-z/' *
Run Code Online (Sandbox Code Playgroud)

更加定向的样品

只需删除所有空格方括号:

rename 's/[ \[\]]*//g;' *.ext
Run Code Online (Sandbox Code Playgroud)

.jpg通过以下编号重命名全部1:

rename 's/^.*$/sprintf "IMG_%05d.JPG",++$./e' *.jpg
Run Code Online (Sandbox Code Playgroud)

演示:

touch {a..e}.jpg
ls -ltr
total 0
-rw-r--r-- 1 user user 0 sep  6 16:35 e.jpg
-rw-r--r-- 1 user user 0 sep  6 16:35 d.jpg
-rw-r--r-- 1 user user 0 sep  6 16:35 c.jpg
-rw-r--r-- 1 user user 0 sep  6 16:35 b.jpg
-rw-r--r-- 1 user user 0 sep  6 16:35 a.jpg
rename 's/^.*$/sprintf "IMG_%05d.JPG",++$./e' *.jpg
ls -ltr
total 0
-rw-r--r-- 1 user user 0 sep  6 16:35 IMG_00005.JPG
-rw-r--r-- 1 user user 0 sep  6 16:35 IMG_00004.JPG
-rw-r--r-- 1 user user 0 sep  6 16:35 IMG_00003.JPG
-rw-r--r-- 1 user user 0 sep  6 16:35 IMG_00002.JPG
-rw-r--r-- 1 user user 0 sep  6 16:35 IMG_00001.JPG
Run Code Online (Sandbox Code Playgroud)

以安全的方式匹配SO问题的完整语法

使用实用程序有一种强大而安全的方式rename:

由于这是常用工具,我们必须使用perl语法:

rename 'my $o=$_;
        s/[ \[\]]+/-/g;
        s/-+/-/g;
        s/^-//g;
        s/-\(\..*\|\)$/$1/g;
        s/(.*[^\d])(|-(\d+))(\.[a-z0-9]{2,6})$/
                my $i=$3;
                $i=0 unless $i;
                sprintf("%s-%d%s", $1, $i+1, $4)
            /eg while
               $o ne $_  &&
               -f $_;
    ' *
Run Code Online (Sandbox Code Playgroud)

测试规则:

touch '[ www.crap.com ] file.name.ext' 'www.crap.com - file.name.ext'
ls -1
[ www.crap.com ] file.name.ext
www.crap.com - file.name.ext
rename 'my $o=$_; ...
    ...
    ...' *
ls -1
www.crap.com-file.name-1.ext
www.crap.com-file.name.ext

touch '[ www.crap.com ] file.name.ext' 'www.crap.com - file.name.ext'
ls -1
www.crap.com-file.name-1.ext
[ www.crap.com ] file.name.ext
www.crap.com - file.name.ext
www.crap.com-file.name.ext
rename 'my $o=$_; ...
    ...
    ...' *
ls -1
www.crap.com-file.name-1.ext
www.crap.com-file.name-2.ext
www.crap.com-file.name-3.ext
www.crap.com-file.name.ext
Run Code Online (Sandbox Code Playgroud)

... 等等...

...当你不使用-fflag rename命令时它是安全的:文件不会被淹没,如果出现问题你会得到一条错误信息.

使用和所谓的bashisms重命名文件:

我更喜欢使用专用实用程序,但这甚至可以通过使用 (也就是没有任何分叉)来完成

除了bash(no ,或其他)之外sed,没有使用任何其他二进制文件:awktr

#!/bin/bash

for file;do
    newname=${file//[ \]\[]/.}
    while [ "$newname" != "${newname#.}" ] ;do
        newname=${newname#.}
      done
    while [ "$newname" != "${newname//[.-][.-]/.}" ] ;do
        newname=${newname//[.-][.-]/-};done
    if [ "$file" != "$newname" ] ;then
        if [ -f $newname ] ;then
            ext=${newname##*.}
            basename=${newname%.$ext}
            partname=${basename%%-[0-9]}
            count=${basename#${partname}-}
            [ "$partname" = "$count" ] && count=0
            while printf -v newname "%s-%d.%s" $partname $[++count] $ext &&
                  [ -f "$newname" ] ;do
              :;done
          fi
        mv  "$file" $newname
      fi
  done
Run Code Online (Sandbox Code Playgroud)

要以文件作为参数运行,对于示例:

/path/to/my/script.sh \[*
Run Code Online (Sandbox Code Playgroud)
  • 用点替换空格和方括号
  • 更换的序列.-,-.,--..仅由一个-.
  • 测试文件名是否没有区别,没有任何关系.
  • 测试文件是否存在newname ...
  • 拆分文件名,计数器和扩展,为使索引NEWNAME
  • 如果文件存在newname则循环
  • 最终重命名该文件.


San*_*eep 0

如果您使用的是 Ubunntu/Debian 操作系统,请使用 rename 命令一次重命名多个文件。