陷入使用 find 和 sed 替换文件名中的字符串的困境

陷入使用 find 和 sed 替换文件名中的字符串的困境

下列的这个帖子作为参考,我可以运行findandsed命令而不会抛出错误,但文件名保持不变。

试图脱衣发音_de_来自当前目录中的所有 mp3:

pronunciation_de_wählen.mp3
pronunciation_de_wange.mp3
pronunciation_de_weil.mp3
pronunciation_de_werden.mp3
pronunciation_de_zentrum.mp3

在对命令进行故障排除之前,请先进行一次快速的完整性检查: find . -name "*.mp3"
它返回当前目录中的所有 mp3。现在我们知道这部分是有效的,继续......

sed --version回报sed(GNU sed)4.4

我跑find . -name "*.mp3" -exec sed -i 's/pronunciation_de_//g' {} \;

为了确保我完全理解正在发生的事情:

find .运行寻找当前目录下的命令。
-name "*.mp3"返回任何 .mp3 文件类型。
-exec执行您键入的下一个命令。
sed -i-我切换意味着处理实际文件,而不是(临时)副本。

为了's/old_word/new_word/g'

  • s将 sed 设置为替代模式。
  • /旧词是您要替换的单词。
  • /新词是您要替换的单词。在我的示例中,它将是空白的。
  • /G将替换应用于所有匹配项(不仅仅是第一个匹配项)。

{}该字符串将在每次迭代期间被文件名替换。
\;分号终止寻找命令。反斜杠会转义分号字符,以防 shell 尝试按字面解释它。

我从随机博客和 Stack Exchange 帖子中获得的大部分信息:

了解 -exec 选项find
find 的 -exec 命令中的 {} + 是什么意思?

在发布这个几乎肯定是重复的问题之前,我真的很想花时间进行实验和研究,但我完全陷入困境!

答案1

您可以使用find -exec ...一行 shell 脚本进行替换。

find . -name "*.mp3" -type f -exec bash -c 'mv "$1" "${1/pronunciation_de_}"' bash {} \;

文件名{}作为参数传递$1给执行的 bash 进程,并${1/pronunciation_de_}利用 bash 的参数扩展功能,并将第一次出现的pronunciation_de_in替换$1为空字符串。

附加选项-type f可确保仅匹配常规文件。

答案2

如果您想pronunciation_de_从所有名称以 结尾的文件中删除前缀.mp3,那么您应该搜索名称匹配的文件,pronunciation_de_*.mp3以确保您不会错误地修改其他文件名(尽管这种情况不太可能发生)。

不要使用面向行的文本编辑工具(例如sed文件名)。 shell 知道如何有效地从文件名中删除前缀和后缀字符串。

通过使用find,您可以解决您的问题

find . -type f -name 'pronunciation_de_*.mp3' -exec sh -c '
    for pathname do
        newname=${pathname##*/}              # removes directory path, leaves filename
        newname=${newname#pronunciation_de_} # deletes the prefix string

        mv -i "$pathname" "${pathname%/*}/$newname"
    done' sh {} +

find命令生成通过-type-name测试的文件的路径名,并将这些文件批量提供给一个简短的内联sh -c脚本。

该脚本迭代给定的路径名​​,并为每个路径名创建一个新名称

  1. 删除初始目录路径,将路径名变成./some/path/pronunciation_de_werden.mp3pronunciation_de_werden.mp3然后
  2. 去掉前缀字符串pronunciation_de_pronunciation_de_werden.mp3变成werden.mp3.

mv命令通过将原始文件移动到原始目录中的新名称来重命名原始文件(${pathname%/*}将扩展到原始路径名的目录路径)。

答案3

你的:

find . -name "*.mp3" -exec sed -i 's/pronunciation_de_//g' {} \;

查找文件(任何类型,包括常规的,目录,符号链接,先进先出...),其名称以当前工作及以下结尾.mp3,并且每个文件都以sed就地模式运行,并s/pronunciation_de_//g作为程序和找到的文件作为要操作的文件。

然而sedstream editor。它的工作是编辑文本流。当给定一个文件作为参数时溪流是个内容文件的名称(而不是名称)sed不会重命名文件。重命名文件的标准命令是mv,还有其他几个命令,其中可以包括一些专门用于批量重命名的命令(也可以内置查找文件并编辑其名称的功能),例如mmvrenamezmv

虽然sed如果我们设法以流的形式提供该名称,则可以用来编辑文件的名称,但这将非常麻烦且难以可靠地完成。

LC_ALL=C find . -depth -name 'pronunciation_de_*.mp3' -exec sh -c '
  ret=0
  for file do
    new_file=$(
      printf '%s\n' "$file" |
        sed "
          :1
          \$!{
            N
            b1
          }
          s|/pronunciation_de_\([^/]*\)\$|/\1|
        "
    )
    mv -i -- "$file" "$newfile" || ret=$?
  done
  exit "$ret"' sh {} +

(未经测试)

请注意我们如何需要:

  • LC_ALL=C以避免文件名未以区域设置的字符编码进行编码的问题。
  • -depth在处理叶子之前处理它们所在的树枝。
  • 运行 shell,以便能够sed通过管道 ( |) 将文件路径提供给并通过另一个管道 ( ) 将其输出收集到 shell 变量中$(...)
  • 由于sed一次只处理一行,并且文件路径可以由任意数量的行组成,因此我们需要告诉sed在进行替换之前在其模式空间中累积所有行。
  • 请注意,仅对文件的基本名称进行替换,而不是对较早的路径组件进行替换。
  • 让用户有机会通过该-i选项避免数据丢失。但请注意,如果有一个名为 的文件./pronunciation_de_weil.mp3和一个名为的目录,它仍然没有帮助./weil.mp3,在这种情况下,该文件将默默地重命名为./weil.mp3/pronunciation_de_weil.mp3. GNU 实现mv有一个-T可以缓解这种情况的选项。

zmv与(shell 的自动加载函数)比较zsh

autoload zmv
zmv '(**/)pronunciation_de_(*.mp3)' '$1$2'

这还有一个好处:

  • 在执行之前检查潜在的冲突任何重命名
  • 跳过隐藏文件(如果您还想重命名隐藏文件,可以将其更改为zmv '(**/)pronunciation_de_(*.mp3)(#qD)' '$1$2')。

1 假设一个sed实现支持该非标准扩展,并且以下参数不被视为备份后缀。

答案4

对于批处理操作,我更喜欢 GNU 并行。

ls -1 *.mp3 |
  sed 's/pronunciation_de_//' |
  parallel mv pronunciation_de_{} {}

sed删除了pronunciation_de_文件名中的内容。因此{}会像wählen.mp3, wange.mp3, ...

相关内容