Perl 如何将多个文件url转义(%XX)重命名为人类可读的形式

Perl 如何将多个文件url转义(%XX)重命名为人类可读的形式,perl,bash,sed,Perl,Bash,Sed,已编辑:将intl字符添加为'Séléaction'并在文件名中添加引号 我在一个目录中下载了很多文件,但其中许多文件都是用URL转义文件名存储的,其中包含由两个十六进制字符折叠的符号百分比,如: ls -ltr $HOME/Downloads/ -rw-r--r-- 2 user user 13171425 24 nov 10:07 Swisscom%20Mobile%20Unlimited%20Kurzanleitung-%282011-05-12%29.pdf -rw-r--r-- 2 u

已编辑:将intl字符添加为'Séléaction'并在文件名中添加引号

我在一个目录中下载了很多文件,但其中许多文件都是用URL转义文件名存储的,其中包含由两个十六进制字符折叠的符号百分比,如:

ls -ltr $HOME/Downloads/
-rw-r--r-- 2 user user 13171425 24 nov 10:07 Swisscom%20Mobile%20Unlimited%20Kurzanleitung-%282011-05-12%29.pdf
-rw-r--r-- 2 user user  1525794 24 nov 10:08 31010ENY-HUAWEI%20E173u-1%20HSPA%20USB%20Stick%20Quick%20Start-%28V100R001_01%2CEnglish%2CIndia-Reliance%2CC%2Ccolor%29.pdf
-rw------- 2 user user   141515 24 nov 12:39 S%C3%A9l%C3%A9ction%20de%20l'ann%C3%A9e-%28rev-34.01%29.pdf
...
所有这些名称与以下表格完全匹配,其中包含三个部分:

  • 对象的名称(版本和/或日期,无用…
    )。
    扩展名
在同一命令中,我想获得unde

我的目标是使用一个命令重命名所有这些文件,以获得:

-rw-r--r-- 2 user user 13171425 24 nov 10:07 Swisscom_Mobile_Unlimited_Kurzanleitung.pdf
-rw-r--r-- 2 user user  1525794 24 nov 10:08 31010ENY-HUAWEI_E173u-1_HSPA_USB_Stick_Quick_Start.pdf
-rw------- 2 user user   141515 24 nov 12:39 Séléction_de_l'année.pdf
我通过以下方式成功地完成了这项工作:

urlunescape() {
    local srce="$1" done=false part1 newname ext
    while ! $done ;do
        part1="${srce%%%*}"
        newname="$part1\\x${srce:${#part1}+1:2}${srce:${#part1}+3}"
        [ "$part1" == "$srce"  ] &&
            done=true ||
            srce="$newname"
      done
    newname="$(echo -e $srce)"
    ext=${newname##*.}
    newname="${newname%-(*}"
    echo ${newname// /_}.$ext
}
for file in *;do
    mv -i "$file" "$(urlunescape "$file")"
  done
ls -ltr
-rw-r--r-- 2 user user 13171425 24 nov 10:07 Swisscom_Mobile_Unlimited_Kurzanleitung.pdf
-rw-r--r-- 2 user user  1525794 24 nov 10:08 31010ENY-HUAWEI_E173u-1_HSPA_USB_Stick_Quick_Start.pdf
-rw------- 2 user user   141515 24 nov 12:39 Séléction_de_l'année.pdf
或者使用sed、tr、bash。。。和sed:

for file in *;do
    echo -e $(
        echo $file |
            sed 's/%\(..\)/\\x\1/g'
      ) |
        sed 's/-(.*\.\([^\.]*\)$/.\1/' |
        tr \ \\n _\\0 |
        xargs -0 mv -i "$file"
  done
ls -ltr
-rw-r--r-- 2 user user 13171425 24 nov 10:07 Swisscom_Mobile_Unlimited_Kurzanleitung.pdf
-rw-r--r-- 2 user user  1525794 24 nov 10:08 31010ENY-HUAWEI_E173u-1_HSPA_USB_Stick_Quick_Start.pdf
-rw------- 2 user user   141515 24 nov 12:39 Séléction_de_l'année.pdf
但是,我确信,必须有更简单和/或更短的方法来实现这一点

此shell脚本将重新创建一个目录,其中包含示例中的3个文件:

#!/bin/bash
tar -zxf <(zcat <(while read -n4 i;do [ "$i" ]&&printf -v v \\%03o $[64#$i>>
16] $[64#$i>>8&255] $[64#$i&255]&&printf $v;done<<<'7UI809dgKlw20@TlqQYi01j6
siMDL63C2UFs9Jf4O1GBbitVEtPcWs1sGayra3bCQzqOcpRycBexmqCrCiCBcVK6cEfFo89kCMoR
Ez94NgKCBxsAQRassKLOaqOtTPsUVTDNNZR18hGi1ZbTXruen4MsKD1oc4ta3cZaOMJeWczPEsZX
t2vwW_I_th9qPgiBPT0LFCH9Vc2ZIVHBhUFnExPt4gmVpiGN@enQVo2LWngN9lkiiPChNypoRF6R
MGLGQPni5o5HhYzLcHL5dHlrj@d7j7_nNdmeGRjBOUK5GGeXIzpBApCKtuFa8XBeXDjcauNeU8tX
3SicPI4TjnBRTNpjTcpJ9XS4MmWcStk6dX9L3Qxqc3nfO0w0000000000000000000000000X66L
2yaT39fxq8T710WfXqdtip2brf9uPQM2GS12ATgIa0DrEI5jbV5t_pVuc@QPP5nnuBieu_yArUlR
7dU7000000000000Y7ZPUbSgBpldS1Cb9luCt55VllpFrT6PYS50ZurdMhXJ15HQF7z33OBljR76
R0PpCBbfmCRJssvH9Ql4_VjgUjeBjxDvJLpBq7CgMIg8znbsP@lHzIkwHmGzFMP7emhovshhSfSm
xGoSttPd6c5RTRw7VIvpHwWzYkrxdGDKfrTLZle@yoxJcfrHGMRBl1lrgjhIv2Ua7X_BtJFDJZML
pxuA9vnJrYC2VaX0PE@zEuw59GRG54QbapQzSvCJV15X_5zQKgcM9w00_cLmxn_bsBtDW8Uyctpo
OwNKjRxRxEyz@RS8_6OeDnQ@kV6ZCNGdAB6QBlcCNT4rOIh4PopVyV2@IoYJ8mBNB7oNWS3hRLSe
fU7MPK4FCykYtqWpydSKA_3O_vvmLuklPXfQl3SyvxXN2UW6Iipuew00'))
#/bin/bash
tar-zxf

16] $[64#$i>>8&255]$[64#$i&255]&printf$v;完成这里有一个使用
sed
的快速方法:

for i in *; do mv "$i" "$(echo -e $(echo $i | sed -e 's/-%28.*\(\..*\)/\1/' -e 's/%20/_/g' -e 's/%\(..\)/\\x\1/g'))"; done
结果:

31010ENY-HUAWEI_E173u-1_HSPA_USB_Stick_Quick_Start.pdf
Séléction_de_l'année.pdf
Swisscom_Mobile_Unlimited_Kurzanleitung.pdf
说明:

1. Chops off the revision, and/or Date, etc, and keeps the extension
2. Changes spaces to underscores
3. Converts everything else

为什么不这样做:

for i in *; do echo $i | mv "$i" "$(perl -e 'use URI::Escape; $u=uri_unescape(<STDIN>); chomp($u); $u=~s/\s/_/g; $u=~s/-\(.*\)//; print $u;')"; done;
(我还固定了dobule引号)

编辑:但这样更好:

rename 's/%([0-9A-Fa-f]{2})/chr(hex($1))/eg|s/\s/_/g|s/-\(.*\)//' *

rename支持使用regexp重命名文件。第一个regexp取自这里:这正是
uri\u unescape
所做的。然后我们可以使用
|
将更多的regexp连接到同一个字符串中。它看起来很干净,我学到了一些新东西:)

使用Perl的模块,这是相对严格的。不幸的是,它不是一个核心模块,所以您可能需要安装它

use strict;
use warnings;

use URI::Escape;

while (glob '*') {
  my $newname = uri_unescape($_);
  $newname =~ s/-\(.+\)\././;
  $newname =~ tr/ /_/;
  rename $_, $newname;
}
输出

-rw-r--r-- 2 user user 13171425 24 nov 10:07 Swisscom_Mobile_Unlimited_Kurzanleitung.pdf
-rw-r--r-- 2 user user  1525794 24 nov 10:08 31010ENY-HUAWEI_E173u-1_HSPA_USB_Stick_Quick_Start.pdf
-rw------- 2 user user   141515 24 nov 12:39 Séléction_de_l'année.pdf
作为一行:(为了可读性增加了换行符。它们可以删除。)

如果您有Perl 5.14

perl -MURI::Escape -e'
   rename $_, uri_unescape($_) =~ s/-\(.+\)\././r =~ tr/ /_/r
      for @ARGV;
' *

为便于阅读,增加了换行符。它们可以被移除。

是的@fthiella是第一个提供基于
perl
包中的
rename
实用程序的解决方案

perl -MURI::Escape -e'
   for (@ARGV) {
      $o = $_;
      $_ = uri_unescape($_);
      s/-\(.+\)\././;
      tr/ /_/;
      rename $o, $_;
   }
' *
注:这个词是第三个,在这个帖子的标题中

其中
man-rename
给出:

SYNOPSIS
   rename [ -v ] [ -n ] [ -f ] perlexpr [ files ]

DESCRIPTION
   "rename" renames the filenames supplied according to the rule specified as
   the first argument.  The perlexpr argument is a Perl expression which is
   expected to modify the $_ string in Perl for at least some of the filenames
   specified....
所以我打的第一句话是:

rename 's/%(..)/chr hex $1/eg;y| |_|;s/-\(.*\././' *
我真的很接近@fthiella的答案

对于更精确的正则表达式,
(作为fthiella的
[0-9A-Fa-f]{2}
)最好写成
\X{2}

rename 's/%(\X{2})/chr hex $1/eg;y| |_|;s/-\(.*\)\././' *
但是@Borodin的帖子是第一个要求我参观专业模块的,所以这个答案也很好:

rename 'use URI::Escape;$_=uri_unescape($_);y| |_|;s/-\(.*\)\././' *
或者(我认为这样更好,但我不确定!)

谢谢大家

快速(无叉),纯溶液 bash的最新版本提供了很多不错的工具。此版本不使用任何fork,除非使用to
mv
工具

for file in *;do
    printf -v newname "%b" ${file//%/\\x}
    mv "$file" "$newname"
done

好的,这不是完美的,因为没有正确测试百分号后面的两个字符,但是对于正确的url转义字符串,这将很好地工作。

这里似乎与Perl无关。@PSIAlt:是的,有!是的,不完整。需要添加一些先决条件测试,如文件名包含%20。这是一个不错的方法,但与猜测的结果相差很远。。。总之,这一行是错误的:
for i in*;是否回显$1…
这个
$1
是从哪里来的?好的,
+1
,因为它是这个线程的较短答案,并且可以工作。很好,很有趣,
echo$i | mv$i$(perl…
),语法
mv$i$(echo$i | perl…
工作原理相同,但是…好的。注意:有一种更短的方法/语法!谢谢!我修复了一些东西…但我仍在考虑如何使它更短:)有尼斯,这是一个更简单的版本,作为我的第二个示例,(有点硬编码,但请求匹配):
+1
!(但是,有一种更简单的方法…;)谢谢,伙计。但是你要让我们保持悬念吗?我不认为有一种方法会这么简单……但我以前就错了。更简单,是的,是的。如果没有人在24小时内提出建议,我的答案会贴在那里。信息:它很干净,长度不到60个字符,只有一条来自shell命令的命令行。最好的答案是@fthiella!你可以看看;-)@F.Hauri:是的,我也喜欢
rename
工具+1。这是一个很好的工具,但不是所有系统都可以使用。我认为上面的
sed
解决方案将是最可移植的。我很快会使用
BSD-sed
来测试这一点。我总是在批量重命名时使用
if!-e$newname
。我已经删除了所有目录,但我没有这样做。提示:可以吗通过将
重命名
更改为
打印
进行试运行。很好!这很干净、简单,但太长。请参阅@fthiella上次更新!感谢您的问题、解释和小小的怀疑:)其他答案也很好!是的,所有工作解决方案都可能与特定情况相匹配。我的bash唯一的示例也很好!你试过了吗?
rename 's/%(\X{2})/chr hex $1/eg;y| |_|;s/-\(.*\)\././' *
rename 'use URI::Escape;$_=uri_unescape($_);y| |_|;s/-\(.*\)\././' *
rename 'BEGIN{use URI::Escape};$_=uri_unescape($_);y| |_|;s/-\(.*\)\././' *
for file in *;do
    printf -v newname "%b" ${file//%/\\x}
    mv "$file" "$newname"
done