当前位置 : 首页 » 互动问答 » 正文

How to split one string into multiple strings separated by at least one space in bash shell?

分类 : 互动问答 | 发布时间 : 2009-09-24 13:07:37 | 评论 : 7 | 浏览 : 331167 | 喜欢 : 200

I have a string containing many words with at least one space between each two. How can I split the string into individual words so I can loop through them?

The string is passed as an argument. E.g. ${2} == "cat cat file". How can I loop through it?

Also, how can I check if a string contains spaces?

回答(7)

  • 1楼
  • I like the conversion to an array, to be able to access individual elements:

    sentence="this is a story"
    stringarray=($sentence)
    

    now you can access individual elements directly (it starts with 0):

    echo ${stringarray[0]}
    

    or convert back to string in order to loop:

    for i in "${stringarray[@]}"
    do
      :
      # do whatever on $i
    done
    

    Of course looping through the string directly was answered before, but that answer had the the disadvantage to not keep track of the individual elements for later use:

    for i in $sentence
    do
      :
      # do whatever on $i
    done
    

    See also Bash Array Reference.

  • 2楼
  • Did you try just passing the string variable to a for loop? Bash, for one, will split on whitespace automatically.

    sentence="This is   a sentence."
    for word in $sentence
    do
        echo $word
    done
    

     

    This
    is
    a
    sentence.
    
  • 3楼
  • Just use the shells "set" built-in. For example,

    set $text
    

    After that, individual words in $text will be in $1, $2, $3, etc. For robustness, one usually does

    set -- junk $text
    shift
    

    to handle the case where $text is empty or start with a dash. For example:

    text="This is          a              test"
    set -- junk $text
    shift
    for word; do
      echo "[$word]"
    done
    

    This prints

    [This]
    [is]
    [a]
    [test]
    
  • 4楼
  • The probably most easy and most secure way in BASH 3 and above is:

    var="string    to  split"
    read -ra arr <<<"$var"
    

    (where arr is the array which takes the splitted parts of the string) or, if there might be newlines in the input and you want more than just the first line:

    var="string    to  split"
    read -ra arr -d '' <<<"$var"
    

    (please note the space in -d '', it cannot be left away), but this might give you an unexpected newline from <<<"$var" (as this implicitly adds an LF at the end).

    Example:

    touch NOPE
    var="* a  *"
    read -ra arr <<<"$var"
    for a in "${arr[@]}"; do echo "[$a]"; done
    

    Outputs the expected

    [*]
    [a]
    [*]
    

    as this solution (in contrast to all previous solutions here) is not prone to unexpected and often uncontrollable shell globbing.

    Also this gives you the full power of IFS as you probably want:

    Example:

    IFS=: read -ra arr < <(grep "^$USER:" /etc/passwd)
    for a in "${arr[@]}"; do echo "[$a]"; done
    

    Outputs something like:

    [tino]
    [x]
    [1000]
    [1000]
    [Valentin Hilbig]
    [/home/tino]
    [/bin/bash]
    

    As you can see, spaces can be preserved this way, too:

    IFS=: read -ra arr <<<' split  :   this    '
    for a in "${arr[@]}"; do echo "[$a]"; done
    

    outputs

    [ split  ]
    [   this    ]
    

    Please note that the handling of IFS in BASH is a subject on it's own, so do your tests, some interesting topics on this:

    • unset IFS: Ignores runs of SPC, TAB, NL and on line starts and ends
    • IFS='': No field separation, just reads everything
    • IFS=' ': Runs of SPC (and SPC only)

    Some last example

    var=$'\n\nthis is\n\n\na test\n\n'
    IFS=$'\n' read -ra arr -d '' <<<"$var"
    i=0; for a in "${arr[@]}"; do let i++; echo "$i [$a]"; done
    

    outputs

    1 [this is]
    2 [a test]
    

    while

    unset IFS
    var=$'\n\nthis is\n\n\na test\n\n'
    read -ra arr -d '' <<<"$var"
    i=0; for a in "${arr[@]}"; do let i++; echo "$i [$a]"; done
    

    outputs

    1 [this]
    2 [is]
    3 [a]
    4 [test]
    

    BTW:

    • If you are not used to $'ANSI-ESCAPED-STRING' get used to it, it's a timesaver.

    • If you do not include -r (like in read -a arr <<<"$var") then read does backslash escapes. This is left as exercise for the reader.


    For the second question:

    To test for something in a string I usually stick to case, as this can check for multiple cases at once (note: case only executes the first match, if you need fallthrough use multiplce case statements), and this need is quite often the case (pun intended):

    case "$var" in
    '')                empty_var;;                # variable is empty
    *' '*)             have_space "$var";;        # have SPC
    *[[:space:]]*)     have_whitespace "$var";;   # have whitespaces like TAB
    *[^-+.,A-Za-z0-9]*) have_nonalnum "$var";;    # non-alphanum-chars found
    *[-+.,]*)          have_punctuation "$var";;  # some punctuation chars found
    *)                 default_case "$var";;      # if all above does not match
    esac
    

    So you can set the return value to check for SPC like this:

    case "$var" in (*' '*) true;; (*) false;; esac
    

    Why case? Because it usually is a bit more readable than regex sequences, and thanks to Shell metacharacters it handles 99% of all needs very well.

  • 5楼
  • $ echo "This is   a sentence." | tr -s " " "\012"
    This
    is
    a
    sentence.
    

    For checking for spaces, use grep:

    $ echo "This is   a sentence." | grep " " > /dev/null
    $ echo $?
    0
    $ echo "Thisisasentence." | grep " " > /dev/null     
    $ echo $?
    1
    
  • 6楼
  • (A) To split a sentence into its words (space separated) you can simply use the default IFS by using

    array=( $string )
    


    Example running the following snippet

    #!/bin/bash
    
    sentence="this is the \"sentence\"   'you' want to split"
    words=( $sentence )
    
    len="${#words[@]}"
    echo "words counted: $len"
    
    printf "%s\n" "${words[@]}" ## print array
    

    will output

    words counted: 8
    this
    is
    the
    "sentence"
    'you'
    want
    to
    split
    

    As you can see you can use single or double quotes too without any problem

    Notes:
    -- this is basically the same of mob's answer, but in this way you store the array for any further needing. If you only need a single loop, you can use his answer, which is one line shorter :)
    -- please refer to this question for alternate methods to split a string based on delimiter.


    (B) To check for a character in a string you can also use a regular expression match.
    Example to check for the presence of a space character you can use:

    regex='\s{1,}'
    if [[ "$sentence" =~ $regex ]]
        then
            echo "Space here!";
    fi
    
  • 7楼
  • For checking spaces just with bash:

    [[ "$str" = "${str% *}" ]] && echo "no spaces" || echo "has spaces"
    

相关阅读:

Bash foreach loop

How to split one string into multiple strings separated by at least one space in bash shell?

How to split one string into multiple strings separated by at least one space in bash shell?

How to split one string into multiple strings separated by at least one space in bash shell?

How to split one string into multiple strings separated by at least one space in bash shell?

How to convert string to char array in C++?

How do I replace a character at a particular index in JavaScript?

How many spaces will Java String.trim() remove?

String isNullOrEmpty in Java?

How to check whether a string contains a substring in Ruby?