Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to replace multiple spaces with a single space using Bash? [duplicate]

I'd like to use bash to replace multiple adjacent spaces in a string by a single space. Example:

Original string:

"too         many       spaces."

Transformed string:

"too many spaces."

I've tried things like "${str//*( )/.}" or awk '{gsub(/[:blank:]/," ")}1' but I can't get it right.

Note: I was able to make it work with <CMD_THAT_GENERATES_THE_INPUT_STRINGH> | perl -lpe's/\s+/ /g' but I had to use perl to do the job. I'd like to use some bash internal syntax instead of calling an external program, if that is possible.

like image 238
Bruno Negrão Zica Avatar asked May 09 '18 18:05

Bruno Negrão Zica


People also ask

How do I replace multiple spaces with single space in bash?

Continuing with that same thought, if your string with spaces is already stored in a variable, you can simply use echo unquoted within command substitution to have bash remove the additional whitespace for your, e.g. $ foo="too many spaces."; bar=$(echo $foo); echo "$bar" too many spaces.

How do I replace multiple spaces in single space?

The metacharacter “\s” matches spaces and + indicates the occurrence of the spaces one or more times, therefore, the regular expression \S+ matches all the space characters (single or multiple). Therefore, to replace multiple spaces with a single space.

Which of the given commands can be used to replace the multiple occurrences of spaces with a single space?

Answer: Use the JavaScript replace() method.

How do I remove extra spaces in bash?

You can enable shopt -s extglob and then just use NEWHEAD=${NEWHAED/+( )/ } to remove internal spaces.


2 Answers

Using tr:

$ echo "too         many       spaces." | tr -s ' '
too many spaces

man tr:

-s, --squeeze-repeats
       replace each sequence of a repeated character that is listed  in
       the last specified SET, with a single occurrence of that charac‐
       ter

Edit: Oh, by the way:

$ s="foo      bar"
$ echo $s
foo bar
$ echo "$s"
foo      bar

Edit 2: On the performance:

$ shopt -s extglob
$ s=$(for i in {1..100} ; do echo -n "word   " ; done) # 100 times: word   word   word...
$ time echo "${s//+([[:blank:]])/ }" > /dev/null

real    0m7.296s
user    0m7.292s
sys     0m0.000s
$ time echo "$s" | tr -s ' ' >/dev/null

real    0m0.002s
user    0m0.000s
sys     0m0.000s

Over 7 seconds?! How is that even possible. Well, this mini laptop is from 2014 but still. Then again:

$ time echo "${s//+( )/ }" > /dev/null

real    0m1.198s
user    0m1.192s
sys     0m0.000s
like image 87
James Brown Avatar answered Sep 29 '22 19:09

James Brown


Here is a way to do this using pure bash and extglob:

s="too         many       spaces."

shopt -s extglob
echo "${s//+([[:blank:]])/ }"

too many spaces.
  • Bracket expression [[:blank:]] matches a space or tab character
  • +([[:blank:]]) matches one or more of the bracket expression (requires extglob)
like image 36
anubhava Avatar answered Sep 29 '22 19:09

anubhava