如何替换Bash中N个重复的特殊字符?

How to replace N repeated special characters in Bash?

我想将任何特殊字符(不是数字或字母)替换为一个“-”。

我用一些字符尝试了下面的代码,但是当字符重复超过 1 次时它不起作用,因为仍然会有多个 '-'。

#!/bin/bash
for f in *; do mv "$f" "${f// /-}"; done

for f in *; do mv "$f" "${f//_/-}"; done

for f in *; do mv "$f" "${f//-/-}"; done  

我想要的:

test---file       ->  test-file

test   file       ->  test-file

test______file    ->  test-file

teeesst--ffile    ->  teeesst-ffile

test555----file__ ->  test555-file

请解释你的答案,因为我不太了解 bash、正则表达式...

您可以使用 tr(如上面的评论所示),或者实际上,sed 在这种情况下更有意义。例如,给定您的文件名列表:

$ cat fnames
test---file
test   file
test______file
teeesst--ffile
test555----file__

您可以使用 sed 表达式:

sed -e 's/[[:punct:] ][[:punct:] ]*/-/' -e 's/[[:punct:] ]*$//'

示例Use/Output

$ sed -e 's/[[:punct:] ][[:punct:] ]*/-/' -e 's/[[:punct:] ]*$//' fnames
test-file
test-file
test-file
teeesst-ffile
test555-file

根据文件名的存储方式,您可以单独使用命令替换,也可以使用进程替换并提供将名称更新为 while 循环或类似的东西。

在 Linux 的各种发行版中有几个不同的 rename(或 prename)命令可以处理正则表达式替换。

但您也可以使用 Bash 的扩展 globbing 来完成其中的一些工作。模式 ${var//+([-_ ])/-} 表示将方括号中列出的任何一个或多个字符替换为一个连字符。

shopt -s extglob
# demonstration:
for file in test---file 'test   file' test______file teeesst--ffile test555----file__
do
    echo "${file//+([-_ ])/-}"
done

输出:

test-file
test-file
test-file
teeesst-ffile
test555-file-

扩展的 glob +() 类似于正则表达式中的 .+。其他 Bash 扩展 glob(来自 man bash):

          ?(pattern-list)
                 Matches zero or one occurrence of the given patterns
          *(pattern-list)
                 Matches zero or more occurrences of the given patterns
          +(pattern-list)
                 Matches one or more occurrences of the given patterns
          @(pattern-list)
                 Matches one of the given patterns
          !(pattern-list)
                 Matches anything except one of the given patterns

请注意,此处未删除最后一个连字符,但可以使用额外的参数扩展:

file=${file/%-/}

表示要删除字符串末尾的连字符。