【问题标题】:How to replace N repeated special characters in Bash?如何在 Bash 中替换 N 个重复的特殊字符?
【发布时间】:2019-11-04 16:51:15
【问题描述】:

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

我用一些字符尝试了下面的代码,但是当字符重复超过 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、regexp...

【问题讨论】:

  • 不需要循环。您只需要tr -s [[:punct:]] '-',例如:echo "test______file" | tr -s [[:punct:]] '-' 只需将其粘贴到命令行即可进行测试。
  • 要处理末尾的标点符号,您可以使用 命令替换,例如a=$(echo "test555----file__" | tr -s [[:punct:]] '-'); echo ${a%-} 产生 test555-file
  • 两个不同的文件名可以导致相同的文件名。注意不要覆盖任何文件。
  • 我会选择对初学者更友好的东西sed -e 's/\(\W\+\|\_\+\)\+/-/g' -e 's/-$//g' sed 用于流编辑。因此,您可以将文件名流传递给它。您似乎要求找到特殊字符(不是单词和_)的,并将它们更改为一个-。意思是在正则表达式中你试图找到\W_ 的分组然后你不希望任何文件以- 结束所以再替换's/-$//g' 然后你可以从这里编写一个小脚本来迭代你的文件,然后重命名它们。

标签: linux string bash replace opensuse


【解决方案1】:

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

但您也可以使用 Bash 的扩展通配符来完成其中的一些操作。模式${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/%-/}

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

【讨论】:

  • @JoãoVitorBarbosa: Always 在变量要被扩展时引用变量,并在文件名参数之前添加 -- 作为最后一个选项如果文件名可能以连字符开头:mv -n -- "${file[@]}" "${f//+([-_ ])/-}"
  • 感谢您的建议。事实证明这是一个由循环文件名数组引起的问题(一些文件名有空格)。但只是像这样直接循环:for file in *.txt;解决了。​​
  • @JoãoVitorBarbosa:抱歉,我错过了那个,因为在 cmets 中很难阅读代码。 for file in "${files[@]}"; do echo "$file"; done 总是引用你的变量。
【解决方案2】:

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

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

您可以使用sed 表达式:

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

使用/输出示例

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-19
    • 2021-08-12
    • 2017-07-05
    • 1970-01-01
    • 2023-02-24
    • 2015-03-17
    • 2016-09-21
    • 1970-01-01
    相关资源
    最近更新 更多