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

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

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

我使用以下命令和 Thunar 自定义操作来对文件名进行 slugify:

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 自定义操作一起使用,那就太好了。

答案1

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

  • 德班:Perl 的重命名应该是默认的。它也可以作为prename.不过,可执行rename文件受 控制/etc/alternatives,因此可以更改为不同的内容。

  • 拱Linux:运行pacman -S perl-rename,该命令将以perl-rename.为了获得更方便的名称,请创建别名。 (帽子提示:ChiseledAbs)

  • 苹果电脑根据这个答案rename可以通过以下方式使用自制软件安装在 OSX 上:

    brew install rename 
    
  • 直接下载: renamePerl Monks 也提供:

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

答案2

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

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

答案3

不算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更好。尽管这应该只在 POSIX mvin$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 个副本 - 此处的文档称为CGEN

  • dd以每块 90 字节的换行符阻止其输入,并将其通过管道传输到...

  • sed将其中 2 个块连接到两个\newline 字符上,'用单引号引用结果,并在管道touch --输出到 ... 之前为每个行循环预先添加字符串。

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

#CGEN不过……好吧,简单说一下……

  • 底部printf打印 252 个 0

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

  • eval解释下一个printf参数,然后将解释结果打印为前面加 2 个反斜杠的八进制数字

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

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

最终结果是一个目录,其中包含许多非常丑陋的文件名,其中包含我的字符集中从 1 到 255 的每个字节(单引号除外)(只是为了避免再出现一个sed s///语句而跳过)/斜线。这些文件名如下所示:

(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

答案4

以下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) 给定字符集中 ( ' _-') 中的任何字符。在该组的开头或结尾放置破折号非常重要,否则它将被解释为字符范围。

相关内容