简体   繁体   中英

How to replace N repeated special characters in Bash?

I want to replace any special character (not a number or letter) to one single '-'.

I tried the code below with some characters, but it doesn't work when the character is repeated more than 1 time because would still have more than one '-'.

#!/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  

what I want:

test---file       ->  test-file

test   file       ->  test-file

test______file    ->  test-file

teeesst--ffile    ->  teeesst-ffile

test555----file__ ->  test555-file

Please, explain your answer because I don't know much about bash, regexp...

There are a couple of different rename (or prename ) commands available in various distributions of Linux what will handle regex substitutions.

But you can also use Bash's extended globbing to do some of that. The pattern ${var//+([-_ ])/-} says to replace any runs of one or more characters that are listed in the square brackets with one hyphen.

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

Output:

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

The extended glob +() is similar to .+ in regex. Other Bash extended globs (from 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

Note that the final hyphen is not removed here, but could be using an additional parameter expansion:

file=${file/%-/}

which says to remove a hyphen at the end of the string.

You can use tr (as shown above in the comment) or, actually, sed makes more sense in this case. For example, given your list of filenames:

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

You can use the sed expression:

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

Example Use/Output

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

Depending on how your filenames are stored, you can either use command substitution individually, or you can use process substitution and feed the updated names into a while loop or something similar.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM