57

Possible Duplicate:
Split string based on delimiter in Bash?

In a bash script how do I split string with a separator like ; and loop through the resulting array?

Community
  • 1
  • 1
Funky Dude
  • 3,867
  • 2
  • 23
  • 33

5 Answers5

89

You can probably skip the step of explicitly creating an array...

One trick that I like to use is to set the inter-field separator (IFS) to the delimiter character. This is especially handy for iterating through the space or return delimited results from the stdout of any of a number of unix commands.

Below is an example using semicolons (as you had mentioned in your question):

export IFS=";"
sentence="one;two;three"
for word in $sentence; do
  echo "$word"
done

Note: in regular Bourne-shell scripting setting and exporting the IFS would occur on two separate lines (IFS='x'; export IFS;).

user132447
  • 1,651
  • 13
  • 6
  • 12
    I think you should store IFS before modifying it, in an temp variable and restore it back once you are done with it!! – vpram86 Sep 10 '09 at 19:04
  • 3
    I'd recommend either doing this in a subshell (wrap in parentheses) or saving and restoring IFS (OLDIFS="$IFS" ...code... export IFS="$OLDIFS") - a lot of funky things can happen if it's not set right. – Cascabel Sep 10 '09 at 19:04
  • thanks. i am using this script and also saving and restoring ifs according to jefromi. but i get this error export: =: bad variable name did i do something wrong? – Funky Dude Sep 10 '09 at 19:44
  • 5
    It is not necessary to export IFS. Just save it `oldIFS=$IFS`, set it `IFS=';'`, do stuff, and restore it `IFS=$oldIFS`. – Dennis Williamson Sep 10 '09 at 23:15
  • You saved me, thank you – Cyrille MODIANO Nov 21 '17 at 21:33
  • I would use exprt IFS="," instead because if the input is feed from argument then the argument containing ; could have a different meaning than just a separator – Anton Krug Aug 23 '18 at 12:43
35

If you don't wish to mess with IFS (perhaps for the code within the loop) this might help.

If know that your string will not have whitespace, you can substitute the ';' with a space and use the for/in construct:

#local str
for str in ${STR//;/ } ; do 
   echo "+ \"$str\""
done

But if you might have whitespace, then for this approach you will need to use a temp variable to hold the "rest" like this:

#local str rest
rest=$STR
while [ -n "$rest" ] ; do
   str=${rest%%;*}  # Everything up to the first ';'
   # Trim up to the first ';' -- and handle final case, too.
   [ "$rest" = "${rest/;/}" ] && rest= || rest=${rest#*;}
   echo "+ \"$str\""
done
NVRAM
  • 6,947
  • 10
  • 41
  • 44
  • 5
    Why the down-vote w/o a comment? You should justify that.... – NVRAM Sep 17 '09 at 21:33
  • 1
    I use the first method using using `${STR//;/ }` syntax. But I got a problem now, that if I pass a string which doesn't have the delimiter, it gives an error: "Bad substitution". I'd like to treat such strings as an array with one element. Do you have any suggestion to get around this issue? – Елин Й. Jul 27 '18 at 10:50
13

Here's a variation on ashirazi's answer which doesn't rely on $IFS. It does have its own issues which I ouline below.

sentence="one;two;three"
sentence=${sentence//;/$'\n'}  # change the semicolons to white space
for word in $sentence
do
    echo "$word"
done

Here I've used a newline, but you could use a tab "\t" or a space. However, if any of those characters are in the text it will be split there, too. That's the advantage of $IFS - it can not only enable a separator, but disable the default ones. Just make sure you save its value before you change it - as others have suggested.

Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
  • You could just do `for word in ${sentence//;/$'\n'}` (omit the second assignment). To loop only on newlines, you can do something like `echo ${sentence//;/$'\n'} | while read elem; do echo "$elem"; done`. – Cascabel Sep 10 '09 at 19:44
1

Here is an example code that you may use:

$ STR="String;1;2;3"
$ for EACH in `echo "$STR" | grep -o -e "[^;]*"`; do
    echo "Found: \"$EACH\"";
done

grep -o -e "[^;]*" will select anything that is not ';', therefore spliting the string by ';'.

Hope that help.

NawaMan
  • 901
  • 5
  • 12
0
sentence="one;two;three"
a="${sentence};"
while [ -n "${a}" ]
do
    echo ${a%%;*}
    a=${a#*;}
done
kawauso
  • 355
  • 2
  • 5