查找并替换一批文件中的字符串,但不会产生任何文件名冲突

查找并替换一批文件中的字符串,但不会产生任何文件名冲突

我有一个目录树,其中包含多年来收集的数千个文件,这些文件的文件名是随机且无用的。

我想清理它们,保持它们的相关性,删除某些单词,但也不创建任何重复的文件名。

例如

WONDERBROS - PAWG Remy LaCroix Gets BREAKFAST! (pwg11717) - SOMENAME.mp4

我希望将其重命名为类似的名称

WONDERBROS.PAWG.Remy.LaCroix.Gets.BREAKFAST!.(pwg11717).mp4

所以有几点是

  • 将所有空格替换为.
  • 每当出现 SOMENAME 时就将其删除
  • 重命名之前检查该文件名是否已存在,如果存在,请附加一个数字并继续尝试,直到它成为唯一的文件名,以便不会覆盖任何内容

我认为这可以通过 shell 脚本来完成,但如果有人有推荐的工具,我也想尝试一下。

我尝试了类似的方法,但似乎只有第一行有效。

# find and replace spaces with .
find /home/matt/rename_test_tmp/ -depth -name "* *" -execdir rename 's/ /./g' "{
# find and replace somename with .
find /home/matt/rename_test_tmp/ -depth -name "somename" -execdir rename 's/ 
# find and replace SOMENAME
find /home/matt/rename_test_tmp/ -depth -name "SOMENAME" -execdir rename 's/ 
# find and replace Somename
find /home/matt/rename_test_tmp/ -depth -name "Somename" -execdir rename 's/ 

我想我的问题是我仍然不明白reg ex?

答案1

这是一个基本结构。如果出现问题,MAXTRYS 会停止重命名。选择一个合理的值。

#!/bin/bash
#
# Usage: 
#       numberedMove xy-file.txt TARGETDIR 
#
file="$1"
targetDir="$2"
MAXTRYS=666
#
# @TODO
# add tests for permissions, maybe handle symlinks etc.
#
test -f "$file" || {
    echo No such file "$file" or not an ordinary file
    exit 1;
}

test -d "$targetDir" || {
    echo No such directory "$targetDir"
    exit 2;
}

#
# append NUM+1 to filename, to create unique name
#
numbered () {
  fname="$1"
  num=$2
  if [[ $num -gt $MAXTRYS ]]
  then
    echo " giving up - max trys: $MAXTRYS "
    exit 3
  fi
  # echo "mv $fname → $targetDir/$fname$num"
  test -f "$targetDir/$fname$num" && numbered "$fname" $((num+1)) || mv "$file" "$targetDir/$fname$num"
}

#
# remove all SOMENAME and replace every blank with a dot
#
filtername () {
   fname="$1"
   fname=${fname//SOMENAME/}
   fname=${fname// /.}
   echo "$fname"
}
#
# filter filename, if unique: move, else move with number suffix
#
if [[ -e "$file" ]]
then
    newname=$(filtername "$file")
    if [[ ! -f "$targetDir/$newname" ]]
    then
        # echo "mv $file → $targetDir/$newname"
        mv "$file" "$targetDir/$newname"
    else
        # echo "mv $file → $targetDir/$newname NUMBERED"
        numbered "$newname" 1
    fi
fi

filtername可以添加更多规则并更改现有规则。

如果这是为了我自己的需要,我会将空格替换为“-”,而不是“.”,因为点是一个足够公平的弱指示符(a.tar.bz2)。

我会将多个分隔符折叠为一个,而不是:

    mv "ab cd - SOMENAME..mp3" → "./B/ab.cd.-...mp3" 
    mv "ab cd - SOMENAME..mp3" → "./B/ab-cd-.mp3" 

我会保留文件扩展名,并将数字放在最后一个扩展名前面:

    mv "ab cd- SOMENAME..mp3"  → "./B/ab-cd-0.mp3" 
    mv "ab cd - SOMENAME..mp3" → "./B/ab-cd-1.mp3" 
    mv "ab cd -SOMENAME..mp3"  → "./B/ab-cd-2.mp3" 

因为许多程序都会解释扩展名。

我只用大约10个文件测试了程序,所以使用前请做好备份,并检查(可能通过总文件大小和文件计数)操作是否成功。

我的测试:

for f in a* ; do ./numberedMove.sh "$f" ./B ; done

请注意,将程序放入 CWD 时,它可能会自行移动。

答案2

如果您使用 perl 重命名实用程序,请尝试如下操作:

find . -type f -execdir rename -n 's/\s*-\s*/./g; s/\s+/./g; s/somename//ig' {} +

火柴\s*-\s*-包围- 或更多空白字符(即-带有可选空白的字符)。 \s+火柴- 或更多空白字符。

s/search/replace/您可以根据需要在重命名脚本中添加任意数量的命令。但请注意,您需要注意执行顺序 - 例如,如果您想将“foo”更改为“bar”,将“food”更改为“drink”,那么您需要s/food/drink/ s/foo/bar/因为后者会food变成bard.

上面的命令适用于所有文件名。如果某些s/search/replace/命令不适用于特定文件名,则不是错误 - 该特定命令未应用(但其他命令仍然适用)。即使如此,如果您希望某些重命名仅应用于某些文件名,您可能需要使用或谓词运行多个find命令。-name-iname

如果您想将多个单词更改为同一个替换,您也可以使用交替。例如

s/(somename|someothername|thisname|thatname)//ig

将它们全部更改为空字符串(即删除它们)。修饰符/i使其不区分大小写。

-n来自的选项是rename一个试运行选项 - 它只会显示什么被重命名,但实际上没有重命名任何东西。-n当您确认重命名命令执行了您想要的操作并且没有执行您不需要的操作后,请从重命名命令中删除。如果您希望在重命名时获得详细输出,请将 替换-n-v.

man rename

-v,-verbose

详细:打印成功重命名的文件的名称。

-n,-nono

无操作:打印要重命名的文件的名称,但不重命名。


笔记:如果新文件名已经存在,perlrename实用程序将不会重命名文件。它没有在发生冲突时向文件名添加数字的内置功能,但此重命名的最佳功能之一是您不仅限于简单的操作,例如s/search/replace/- 您可以运行任何重命名脚本中的 perl 代码,它会将源文件名重命名为更改为的任何内容($_如果未使用显式变量名,则运算符如s///y//隐式修改。如果您是新手,这里有一个很好的解释$_perl$_https://perlmaven.com/the-default-variable-of-perl)。

这允许这样的事情(未经测试,但可能有效)

find . -type f -execdir rename -n '
    s/\s*-\s*/./g; 
    s/\s+/./g;
    s/somename//ig;

    if (-f $_) {
      my $num='001';
      while (-f "$_.$num") {
        $num=sprintf('%03i',++$num);
      };
      $_ = "$_.$num";
    }' {} +

如果文件名已经存在,则应在文件名中添加一个零填充的 3 位数字(即 from001到)。如果您只需要两位数字 ( to ),请在 sprintf 中999更改%03i为。或者,如果您不希望任何重复文件的编号超过,请将其更改为 just ,然后在 while 循环内使用 no 。%02i01999my $num=1$num++sprintf()

通过更多的工作(通过将文件名拆分为“基本名”和“扩展名”部分),可以在扩展名之前插入编号,而不是仅仅附加到末尾。


rename您可以通过该选项了解您拥有的版本-V。例如,在我的 Debian 系统上rename是 perl 重命名,而rename.ulutil-linux 重命名是:

$ rename -V
/usr/bin/rename using File::Rename version 0.20

$ rename.ul -V
rename.ul from util-linux 2.31.1

答案3

find . -maxdepth 1 -type f > orginal_file

find . -maxdepth 1 -type f -exec sed -r "s/\s+//g"| sed "s/SOMENAME//g" >> /var/tmp/modified file

paste orginal_file /var/tmp/modified file >> combined_orginal_modified_column_wise

for i in `cat /var/tmp/modified file`
do
if [[ -f $i ]]
then
echo "file exsist"
else
awk -v i="$i" '/i/{print "mv" " " $1 " " $2}'   combined_orginal_modified_column_wise
fi
done

for i in `cat /var/tmp/modified file`
do
if [[ -f $i ]]
then
for j in {1..10}
do
elif [[ -f $i$j ]]
then
echo "$i$j exsists"
fi
done
else
awk -v i="$i" -v j="$j" '{print "mv" " " $1 " " ij}'  combined_orginal_modified_column_wise
fi


done

相关内容