如何从文件名中删除问号和其余部分?

How to remove question mark and the rest from file names?

我使用 wget 抓取了整个网站模板。它在不同的子目录中创建了许多资产文件,其中文件名包含问号,例如 ./fonts/fontawesome-webfont.woff?v=4.5.0。我需要从这些文件名中截断 ? 和其余部分。我试过这个命令:

find . -type f -name '*\?*' -exec mv "{}" "$(echo {} | sed 's/\?.*//g')" \;

但是不行;什么都不是 replaced/removed,它显示此错误:

mv: './fonts/fontawesome-webfont.woff?v=4.5.0' and './fonts/fontawesome-webfont.woff?v=4.5.0' are the same file

我在不同的目录中有这些类型的文件名,所以我尝试了 find 命令。我在这里错过了什么?

编辑

的变体:

find . -name '*\?*' -type f -exec sh -c 'for fp; do echo mv -- "$fp" $(printf "%s" "$fp" | sed "s,?[^/]*$,,"); done' sh {} +
## or
find . -name '*\?*' -type f -exec sh -c 'for fp; do echo mv "$fp" $(echo "$fp" | sed "s,?[^/]*$,,"); done' sh {} +

find 命令从当前文件夹 (.),一旦找到文件,就会执行 sh 脚本。该脚本是 for fp; do echo mv -- "$fp" $(printf "%s" "$fp" | sed "s,?[^/]*$,,"); done,它遍历找到的文件路径 (fp),并移动(重命名)具有相同文件路径的文件路径,但不包含以第一个 ? 字符开头的部分文件路径。

sed "s,?[^/]*$,," 命令实际上找到了一个 ?,然后是除 / 之外的任意数量的字符,直到字符串结尾,并从文件路径中删除这部分(所以,它只删除文件名的一部分,文件夹名可以包含 ? 个字符)。

如果文件路径以 - 字符开头,mv 之后的 -- 是必需的。

注意:从上面的命令中删除 echo 以实际重命名文件。

Linux 18.04 测试解决方案 perl-rename

你可以使用

find . -depth -type f -exec rename -n -v 's/\?.*//' {} +

检查结果,如果它们符合预期,请删除用于“干”的 -n 运行,只检查命令的工作情况。这里,-v(“verbose”选项)用于打印重命名成功的文件名。

您可以阅读有关如何安装和使用此 rename command here 的信息。

请考虑,本回答纯粹针对find命令


中所述,sedfind 找到文件之前被调用。我们可以使用 -exec bash -c 解决这个问题,并将路径名 ({}) 作为参数传递:

find . -type f  -exec bash -c 'echo mv [=10=] $(sed "s/\?.*//g" <<< [=10=])' "{}" \;

如果每个文件的输出似乎都找到了,请删除 mv 文件的 echo 前缀:

find . -type f  -exec bash -c 'mv [=11=] $(sed "s/\?.*//g" <<< [=11=])' "{}" \;

我本地机器上的小例子:

$ ls -lt
total 0
-rw-r--r-- 1 me wheel 0 Jun  3 17:32 'something.exe?v=23456'
-rw-r--r-- 1 me wheel 0 Jun  3 17:32 'myrandom_file?x=123'
-rw-r--r-- 1 me wheel 0 Jun  3 17:32 'fontaweietsanderssome-webfont.woff?v=4.5.0'
$
$ find . -type f  -exec bash -c 'mv [=12=] $(sed "s/\?.*//g" <<< [=12=])' "{}" \;
$
$ ls -lt
total 0
-rw-r--r-- 1 me wheel 0 Jun  3 17:32 something.exe
-rw-r--r-- 1 me wheel 0 Jun  3 17:32 myrandom_file
-rw-r--r-- 1 me wheel 0 Jun  3 17:32 fontaweietsanderssome-webfont.woff
$

假设 rename 不可用(或者您无法安装它),我可能会选择更直接、更容易 (?) 的 understand/maintain。

假设我在名为 question 的目录下有以下内容:

$ ls -1 question
abc?v=4.5.0
def.txt
ghi.pdf?v=1.4.3

一个想法使用简单的 while 循环和 parameter substitution:

find . -type f -name '*\?*' |
while read -r fname
do
    echo mv "${fname}" "${fname//\?*/}"
done

这会生成:

mv ./question/abc?v=4.5.0 ./question/abc
mv ./question/ghi.pdf?v=1.4.3 ./question/ghi.pdf

一旦您对生成的 mv 命令感到满意,就可以删除 echo 以便实际 运行 mv 命令。

这是防弹的。

find . -name '*\?*' -type f -exec sh -c '
for fp; do
  fn=${fp##*/}
  echo mv "$fp" "${fp%/*}/${fn%%\?*}"
done' sh {} +

上面的循环隔离了每个选定文件的名称(在名为 fn 的变量中),以便能够在不破坏任何内容的情况下删除最左边的问号和其余部分;如果我们改为 ${fp%%\?*} ,我们将无法处理名称中带有问号的目录的内容。虽然如果我们确定每个文件名只包含一个问号,${fp%\?*} 就可以正常工作。

如果输出看起来不错,请删除 echo