下列的这个帖子作为参考,我可以运行find
andsed
命令而不会抛出错误,但文件名保持不变。
试图脱衣发音_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
脚本。
该脚本迭代给定的路径名,并为每个路径名创建一个新名称
- 删除初始目录路径,将路径名变成
./some/path/pronunciation_de_werden.mp3
,pronunciation_de_werden.mp3
然后 - 去掉前缀字符串
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
作为程序和找到的文件作为要操作的文件。
然而sed
是s
tream ed
itor。它的工作是编辑文本流。当给定一个文件作为参数时溪流是个内容文件的名称(而不是名称)sed
不会重命名文件。重命名文件的标准命令是mv
,还有其他几个命令,其中可以包括一些专门用于批量重命名的命令(也可以内置查找文件并编辑其名称的功能),例如mmv
、rename
、zmv
。
虽然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
, ...