42

Suppose i have a string, $str. I want $str to be edited such that all the spaces in it are replaced by underscores.

Example

a="hello world"

I want the final output of

echo "$a"

to be hello_world

Ayush Mishra
  • 1,583
  • 3
  • 13
  • 13

5 Answers5

83

You could try the following:

str="${str// /_}"
William Hay
  • 2,148
  • 16
  • 20
30
$ a="hello world"
$ echo ${a// /_}
hello_world

According to bash(1):

${parameter/pattern/string}

Pattern substitution. The pattern is expanded to produce a pattern just as in pathname expansion. Parameter is expanded and the longest match of pattern against its value is replaced with string. If pattern begins with /, all matches of pattern are replaced
with string. Normally only the first match is replaced. If pattern begins with #, it must match at the beginning of the expanded value of parameter. If pattern begins with %, it must match at the end of the expanded value of parameter. If string is null, matches of pattern are deleted and the / following pattern may be omitted. If parameter is @ or *, the substitution operation is applied to each positional parameter in turn, and the expansion is the resultant list. If parameter is an array variable subscripted with @ or *, the substitution operation is applied to each member of the array in turn, and the expansion is the resultant list.

falsetru
  • 357,413
  • 63
  • 732
  • 636
14

Pure bash:

a="hello world"
echo "${a// /_}"

OR tr:

tr -s ' ' '_' <<< "$a"
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • 2
    +1 for just pure bash syntax!. Would like to know what's it doing behind scenes. Looks like it is doing 'sed' replacement operations. Any bash manual states for using such syntax?. – user1587504 Oct 29 '13 at 14:45
  • 1
    @user1587504: I believe `falsetru` has added relevant man section for this `Pattern substitution` – anubhava Oct 29 '13 at 14:47
10

With sed reading directly from a variable:

$ sed 's/ /_/g' <<< "$a"
hello_world

And to store the result you have to use the var=$(command) syntax:

a=$(sed 's/ /_/g' <<< "$a")

For completeness, with awk it can be done like this:

$ a="hello my name is"
$ awk 'BEGIN{OFS="_"} {for (i=1; i<NF; i++) printf "%s%s",$i,OFS; printf "%s\n", $NF}' <<< "$a"
hello_my_name_is
fedorqui
  • 275,237
  • 103
  • 548
  • 598
  • Neither of these appears to deal with more than one space. – William Hay Oct 29 '13 at 14:46
  • @WilliamHay you are right, didn't check with more than one space. Updated to use `sed .../g`, as default was doing it just once. – fedorqui Oct 29 '13 at 14:49
  • @fedorqui +1 for letting know usage of direct variable without echo piping to sed. I donot see direct help for this: '<<<' . Where is it defined in man pages. Could you please point me? – user1587504 Oct 29 '13 at 14:50
  • 1
    @user1587504 they are called "Here Strings". See http://linux.die.net/abs-guide/x15683.html for more reference. They are useful. – fedorqui Oct 29 '13 at 14:53
  • @fedorqui . Thanks! for letting me know. I did used one with '<<' to check some EOF character when using isql inside a script. Usage of <<< is nice. .. – user1587504 Oct 29 '13 at 14:56
7

Multiple spaces to one underscore

This can easily be achieved with a GNU shell parameter expansion. In particular:

${parameter/pattern/string}

If pattern begins with /, all matches of pattern are replaced with string.

with +(pattern-list)

Matches one or more occurrences of the given patterns.

Hence:

$ a='hello world    example'

$ echo ${a// /_}
hello_world____example

$ echo ${a//+( )/_}
hello_world_example

However, for this to work in a bash script two amendments need to be made:

  1. The parameter expansion requires encapsulation in double quotes " " to prevent word splitting with the input field separator $IFS.
  2. The extglob shell option needs to be enabled using the shopt builtin, for extended pattern matching operators to be recognised.

The bash script finally looks like this:

#!/usr/bin/env bash
shopt -s extglob
a='hello world    example'
echo "${a//+( )/_}"
Serge Stroobandt
  • 28,495
  • 9
  • 107
  • 102