删除文件名中的空格,连字符和下划线?


10

从目录中的所有文件或选定文件中删除空格,连字符和下划线的好命令是什么?

我将以下命令与Thunar自定义操作一起使用,以对文件名进行压缩:

for file in %N; do mv "$file" "$(echo "$file" | tr -s ' ' | tr ' A-Z' '-a-z' | tr -s '-' | tr -c '[:alnum:][:cntrl:].' '-')"; done

但是该命令仅用破折号/连字符替换空格,并用小写的带帽字符。

我在终端中使用以下命令从文件夹中成千上万个文件名中删除空格,并且运行速度非常快:

 rename "s/ //g" *

同样,它仅删除空格,而不删除连字符/破折号和下划线。

理想情况下,我不需要文件名中的空格,连字符/短划线和下划线。如果该命令可以与Thunar自定义动作一起使用在选定的文件上,那就太好了。


2
我注意到许多提议的解决方案都存在的一个问题是,在移动文件之前没有正确检查“新”名称的存在。不这样做可能会导致很多问题。
mdpc

是否可以修改John1024的命令进行检查?
2014年

@ user8547rename -i "s/[-_ ]//g" *
Sparhawk

谢谢Sparhawk。顺便说一下,对于那些有兴趣将其用作“ Thunar自定义动作”的用户,Thunar的命令是:for%N中的文件;做mv“ $ file” echo $file | sed -e 's/[ _-]//g'; 完成
2014年

Answers:


11

软件包rename随附的版本perl支持正则表达式:

rename "s/[-_ ]//g" *

或者,

rename -i "s/[-_ ]//g" *

-i标志将rename使用交互模式,提示目标是否已存在,而不是静默覆盖。

Perl的重命名有时称为prename

Perl的重命名与util-linux的重命名

在类似Debian的系统上,perl的重命名似乎是默认的,并且上面的命令应该可以正常工作。

在某些发行版中,renameutil-linux中的实用程序是默认设置。该实用程序与Perl的完全不兼容rename

  • 全部:首先,检查Perl rename是否在名称下可用prename

  • Debian: Perl的重命名应该是默认的。也可以作为prename。该rename可执行文件,不过,是的控制之下/etc/alternatives,因此可以被改变为不同的东西。

  • archlinux:运行pacman -S perl-rename,该命令以可用perl-rename。为了更方便的名称,请创建一个别名。(帽子提示:ChiseledAbs)

  • Mac OSX根据此答案rename可以通过以下方式使用自制软件安装在OSX上:

    brew install rename 
  • 直接下载: rename也可以从Perl Monks获得:

     wget 'http://www.perlmonks.org/?displaytype=displaycode;node_id=303814' -O rename

我认为这取决于rename您在说什么。从一个util的Linux的 -2.24.2-1.fc20.x86_64不支持正则表达式。
Cristian Ciupitu 2014年

1
@CristianCiupitu我刚刚检查了手册页中找到的重命名版本。根据参数,renameOP使用的perl版本看起来像版本,而不是util-linux版本。
2014年

作为记录,这是util-linux版本的rename手册页。无论如何,除了该注释外,重要的是OP得到了他的回答(您对我表示赞同:-D)。
Cristian Ciupitu 2014年

@CristianCiupitu感谢您找到它。以+1回报您。
2014年

1
@ John1024 archlinux,但是我发现了,只是走了,pacman -S perl-rename我想你可以别名了。
ChiseledAbs 2016年

5

我将用替换tr命令替换所有这些命令sed,例如:

for file in %N; do 
    mv "$file" "$(echo "$file" | sed 's/[ _-]//g')"
done

4

不计算在内mv,您实际上根本不需要外部流程-您可以只是在嘲笑它们。

ifsqz() ( LC_ALL=C sqz=$1
    isf() { [ -e "$1" ] || [ -L "$1" ] ; }  
    set -- * ; set -f
    for f do isf "$f" || break
    IFS=$sqz; set -- $f; IFS=
    isf "$*" || mv -- "$f" "$*"
    done
)

尽管如此,这意味着mv每个文件都会被调用,因此可能rename更好。尽管这仅mv在给定POSIX $PATH和POSIX shell的情况下才有效。

因此,我为此提出了一种疯狂的演示。测试集生成如下:

tee - - - - <<CGEN |\
dd cbs=90 conv=unblock |\
sed 'G;$!N'";s/^/touch -- '/;s/$/'/" |sh
$( #BEGIN CGEN
   LC_ALL=C
   i= n='"$((i=((i=i+1)==10||i==39||i==47)>0?(i+1):i))"'
   printf '%b -_   ---___'  $(
   IFS=0; eval \
       printf '"\\\\%04o\\\\%04o "' "$(
       printf "$n"' "$i" '%s $(
       printf %.252d
#END
))"))
CGEN

首先,我将首先确认上述命令产生的结果可以通过其他方式更轻松地获得。但是其他方法可能也不会很好地演示可能会做些什么,$IFS以及一点点(病态?)想象力。

因此,第一点很简单:

  • tee 输出其输入的5个副本-heredocument称为 CGEN

  • dd 通过换行符以每块90字节的格式阻止其输入,并将其传递给...

  • sed在两个\newline字符上连接这些块中的2 个,'将结果单引号,并在touch --每个行循环前将字符串添加到管道之前,以...

  • sh 然后将所有输入作为shell命令执行

虽然#CGEN有点...好吧,短暂地...

  • 底部printf打印252 0s

  • 倒数第二个接收252个''空字符串参数,并为每个参数打印$n后跟字符串的内容" $i "

  • eval解释下一个向上的参数,printf然后再将解释结果打印为以2个反斜杠开头的八进制数字

  • 最后一个一次printf输出这些八进制数2的字节值,然后打印-_ ---___每对字符串

  • $n被初始化为一个方程式,该方程式将为$i每次求值增加一个,但它会跳过值10、39或47- (分别是\newline,'单引号和/ASCII十进制的斜杠)

最终结果是一个目录,其中包含很多非常丑陋的文件名,除了单引号(仅为了避免再sed s///声明一次而被跳过/斜杠之外,它包含我的字符集中从1到255的每个字节。这些文件名看起来像这样:

(set -- *; printf '%s\n\n##############\n\n%s\n' "${9}" "${34}")  | cat -A

   ---___ww -_   ---___xx -_   ---___yy -_   ---___zz -_   ---___{{ -_   ---___|| -_   ---$
$
___}} -_   ---___~~ -_   ---___^?^? -_   ---___M-^@M-^@ -_   ---___M-^AM-^A -_   ---___M-^BM-^B -_   ---___M-^CM-^C$
$
##############$
$
 -_   ---___M-ZM-Z -_   ---___M-[M-[ -_   ---___M-\M-\ -_   ---___M-]M-] -_   ---___M-^M-^ -_   ---___M-_M-_ -_$
$
---___M-`M-` -_   ---___M-aM-a -_   ---___M-bM-b -_   ---___M-cM-c -_   ---___M-dM-d -_   ---___M-eM-e -_   ---___$

现在,我将获得有关这些文件的一些数据:

chksqz() ( LC_ALL=C sqz=$1
    set -- * ; set -f ; IFS= ; tc="$*"
    printf '#%s\n' \
        "There are $# files in this test directory." \
        "All filenames combined contain a total of ${#tc} bytes."
    IFS=$sqz ; set -- $* ; IFS= ; sc="$*"  
    printf "%s '$sqz'" \
        "#Of which ${#sc} bytes are not"\
        " and $((${#tc}-${#sc})) bytes are"
    set +f ; unset IFS
    printf ".\n#%s\n#Total:\t%d\n#Other:\t%d\n#'$sqz':\t%d\n" \
        "And to confirm these figures:" \
        $(  printf %s * | wc -c 
            printf %s * | tr -d "$sqz" | wc -c
            printf %s * | tr -dc "$sqz" | wc -c
))
chksqz '_ -'

输出值

#There are 101 files in this test directory.
#All filenames combined contain a total of 17744 bytes.
#Of which 2692 bytes are not '_ -' and 15052 bytes are '_ -'.
#And to confirm these figures:
#Total: 17744
#Other: 2692
#'_ -': 15052

好。现在终于要采取行动了:

ifsqz '_ -'
chksqz '_ -'

输出值

#There are 101 files in this test directory.
#All filenames combined contain a total of 2692 bytes.
#Of which 2692 bytes are not '_ -' and 0 bytes are '_ -'.
#And to confirm these figures:
#Total: 2692
#Other: 2692
#'_ -': 0

成功!您可以自己看到:

ls

????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
???????????????????????????
???????????????????????????
???????????????????????????
????????????????????????????
????????????????????????????
????????????????
??????????????????????
????????????????????????
??????????????????????????
??????????????????????????
??????????????????????????
??????????????????????????
???????????????????????????
???????????????????????????
???????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
????????????????????????????
??????????????????????????
????????????????????????
????????????????????
??????????????????
????????????????????????????
??
????????????????????????????
??????????????????????????
????????????????????????????
????????????????????????????
????????????????????!!""##
??????????????????!!""##$$
????????????????!!""##$$%%
????????????!!""##$$%%&&((
????????!!""??##$$%%&&(())
$$%%&&(())**??++,,..0011
%%&&(())**++??,,..00112233
&&(())**++,,??..0011223344
))**++,,..??0011223344556
**++,,..00??11223344556677
22334455667788??99::;;<<==>>
445566778899??::;;<<==>>??@@
5566778899::;;??<<==>>??@@AA
6778899::;;<<??==>>??@@AABB
8899::;;<<==??>>??@@AABBCCDD
\\]]^^``aa??bbccddeeffgghh
]]^^``aabbc??cddeeffgghhii
^^``aabbccdd??eeffgghhiijj
??@@AABBCCDDEE??FFGGHHIIJJKK
AABBCCDDEEFF??GGHHIIJJKKLLM
BBCCDDEEFFGG??HHIIJJKKLLMMNN
CCDDEEFFGGHHII??JJKKLLMMNNOO
EEFFGGHHIIJJ??KKLLMMNNOOPPQQ
ffgghhiijjkk??llmmnnooppqqrr
gghhiijjkkllmm??nnooppqqrrss
iijjkkllmmnn??ooppqqrrsstt
jjkkllmmnnoo??ppqqrrssttuuvv
kkllmmnnooppqq??rrssttuuvvww
LLMMNNOOPPQQRR??SSTTUUVVWWXX
MNNOOPPQQRRSS??TTUUVVWWXXYY
OOPPQQRRSSTT??UUVVWWXXYYZZ[[
PPQQRRSSTTUUVV??WWXXYYZZ[[\\
RRSSTTUUVVWW??XXYYZZ[[\\]]
ssttuuvvwwxx??yyzz{{||}}~~??
ttuuvvwwxxyyz??z{{||}}~~????
uuvvwwxxyyzz{{??||}}~~??????
wwxxyyzz{{||??}}~~??????????
xxyyzz{{||}}~~??????????????
YYZZ[[\\]]^^??``aabbccddee
ZZ[[\\]]^^``??aabbccddeeff

2
+1用于创造性地使用IFS+printf
John1024

@ John1024-真正有趣的是:set -- 'some arbitrary' args; eval printf '"%s\n"' "$(IFS=0; printf ' "$@" %s' $(printf %025d))"
mikeserv 2014年

1
new="$(IFS=" -_"; printf %s $1)"派生一个子shell(ksh93中除外),并在换行尾出现问题。另一种选择是使用IFS=' -_'; set -- $1; IFS=; new="$*"(和改变你的while循环for循环)
斯特凡Chazelas

1
[ -e x ]如果x是到不存在或不可访问文件的符号链接,则将返回false 。
斯特凡Chazelas

1
尼斯壳功夫!
countermode

2

如果您有perl,通常会重命名。你可以做:

> type rename
rename is /usr/bin/rename

并显示此脚本的编写方式:

> cat /usr/bin/rename | head -n 5 #firt 5 lines for example
#!/usr/bin/perl -w
#
#  This script was developed by Robin Barker (Robin.Barker@npl.co.uk),
#  from Larry Wall's original script eg/rename from the perl source.
#

该脚本不支持-i标志(这是我系统中的版本),但也许您支持。那参数呢?首先是具有PCRE格式的正则表达式,它的作用类似于过滤器,将输入名称修改为输出名称。您用星号“ *”提供的输入名称列表。例如,您这样做:

> cd /tmp
> rename 's/ //g' *

实际的“ *”可以扩展为:

> rename 's/ //g' file1 file2 file3 othe files found in current directory

当您拥有大量文件时,您将陷入困境。Shell将使您的产品线扩展的时间超过系统接受的时间。那么您可以使用find或xargs来解决。使用“查找”是有问题的,因为重命名将多次调用,其次数等于目录中的文件数。最好将xargs与-r选项一起使用。一个重命名调用将修改许多文件。例如:

> ls | xargs -r rename 's/ //g'   #thats all, names will be appended at the end of this command.

最后一个问题是什么意思:

's/ //g'

这是用于修改名称的正则表达式。在第一个'/'之后是空格。这将被检测到,并在第二个“ /”之后替换为字符串。但是有一个空字符串,以第三个“ /”结尾,然后空格被任何内容代替。选项“ g”使该表达式重复。表达式将从头到尾遍历所有名称,并检测所有空格。

但是,如果您具有制表符或其他“白色”字符怎么办?替换为该“ \ s”。还有哪些其他不需要的字符?只需将其添加到表达式中即可。全部用方括号括起来,例如:

's/[\s_-]//g'

这就是全部。你看到相似之处吗?我认为您应该阅读man perlrequick和man perlretut,这可以向您解释(我希望)正则表达式是如何工作的。您可以根据需要在自己的脚本中使用重命名命令。


1

以下shshell循环将从当前目录中的文件名中删除所有空格,下划线和破折号,请注意不要覆盖任何现有文件:

for f in *; do
    test -f "$f" || continue
    nf=$( echo "$f" | tr -d ' _-' )
    ! test -e "$nf" && echo mv "$f" "$nf"
done

对于bashksh,逻辑上有些冗长:

for f in *; do
    if [[ -f "$f" ]]; then
        nf=$( tr -d ' _-' <<<"$f" )
        if [[ ! -e "$nf" ]]; then
            echo mv "$f" "$nf"
        fi
    fi
done

echo确定可以完成所需操作后,将其删除。

tr命令将删除(-d)给定字符集中的任何字符(' _-')。将破折号放在集合的开始或结尾很重要,否则它将被解释为一系列字符。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.