![当目标文件需要空格时批量重命名](https://linux22.com/image/148536/%E5%BD%93%E7%9B%AE%E6%A0%87%E6%96%87%E4%BB%B6%E9%9C%80%E8%A6%81%E7%A9%BA%E6%A0%BC%E6%97%B6%E6%89%B9%E9%87%8F%E9%87%8D%E5%91%BD%E5%90%8D.png)
我需要根据标题名称使用随机名称重命名一批 Markdown 文件,并且目标名称需要带有空格(这是规范)。我正在尝试这个单行
for i in *;
do mv $i $(grep \# $i | head -1 | sed 's/# //' | sed 's/[_\?\!\]//' | sed 's/[:/]/ - /') ;
done
但我在空格名称中收到“目标 X 不是目录”错误。
有小费吗?
答案1
主要问题是命令替换没有被引用,因此它被分成不同的参数,并且每个参数都受路径名扩展的影响(后一部分在bash
,但不是zsh
),并且当mv
看到两个以上时,它假设最后一个是将文件移动到的目录。
出于同样的原因,$i
它本身应该被引用(在 中bash
),并且为了避免某些文件名以破折号开头的问题-
,您应该在模式中使用./*
in 代替*
,或者将选项结束标记放在命令--
中mv
。
另外,我认为我们可以稍微简化一下处理文件名的方式。首先,sed
可以接受多个命令,因此您只需调用一次。您可能还想添加/g
到命令的末尾s///
,以替换所有匹配项,而不仅仅是第一个匹配项。另请注意,其中 3 个反斜杠中有 2 个[_\?\!\]
是多余的。[_?!]
如果您想匹配_
, ?
or !
or[_\?!]
如果您还想包含反斜杠(尽管[_\\?!]
使用某些实用程序会使其更加万无一失),请使用。
for f in ./*; do
mv -- "$f" "$(grep \# "$f" | head -1 | sed -e 's/# //g' -e 's/[_?!]//g' -e's/[:/]/ - /g')"
done
您还可以将grep
and组合head
到 the 中sed
:
for f in ./*; do
mv -- "$f" "$(sed -ne '/^# / {s///; s/[_?!]//g; s/[:/]/ - /g; p; q;}' < "$f")"
done
在这里,里面的命令{}
以模式为条件/^# /
,q
最后在该块的末尾退出脚本,因此仅处理第一个匹配行。
同样在awk
:
awk '/^# / { sub(/# /, ""); gsub(/[_?!]/, ""); gsub(/[:/]/, " - "); print; exit }' < "$f"
我还可以将最后一个替换更改为s/[:/] */ - /
,这样像这样的字符串blah: bleh
就会变成blah - blah
, 而不是blah - bleh
(破折号后有两个空格)。
答案2
只需在目标名称周围添加引号即可:
mv $i "$(grep \# $i | head -1 | sed 's/# //' | sed 's/[_\?\!\]//' | sed 's/[:/]/ - /')" ;