EthanP
EthanP

Reputation: 1713

bash difference between raw string and string in variable

I wrote a little script in bash, but it only worked when I stored the string as a variable, and I'd like to know why. Here's the summary:

When I use the string itself, bash treats it as a single entity

for word in "this is a sentence"; do
    echo $word
done
# => this is a sentence

If I save the exact same string into a variable, bash iterates over the words

sentence="this is a sentence"
for word in $sentence; do
    echo $word
done

# => this
# is
# a
# sentence
  1. Why are these being treated differently?
  2. Is there a simple way to iterate through the words in the string without first saving the string as a variable?

Upvotes: 4

Views: 32730

Answers (4)

user48956
user48956

Reputation: 15810

The quotes tell bash to treat a thing in quotes as a single parameter in a parameter list at the time the expression is evaluated. The quotes (unless protected with \ or ') are removed.

echo ""    # prints newlines, no quotes
echo '""'  # Print ""

export X='""'
env | grep X   # X contains ""

export X=""
env | grep X   # X is empty

When you use a variable, bash unpacks it as is (i.e. as if you typed the variable's contents in the variable's place). For a for-loop bash determines the list-elements to iterate over by separating the for-loop's parameters by whitespace, but treating (as always) quote-protected items a single parameter/list-element. Your variable contained no quotes -- items are treated as separate parameters.

Upvotes: 6

ghoti
ghoti

Reputation: 46886

As comments suggested, quotes are important. A for loop will step through a list of values terminated by a semicolon, and that list is a set of strings. Unquoted strings are delimited usually by whitespace. Whitespace inside a quoted string does not separate the string from its brethren, it's simply part of the quoted string. There's some truly excellent documentation about quotes in bash at http://mywiki.wooledge.org/Quotes . Read it. Read it now. You'll find a part that says

The quotes are not actually passed along to the command. They are removed by the shell (this process is cleverly called "quote removal").

To step through the words in a sentence that's stored in a variable (if I've inferred your question correctly), you could perhaps use an array to separate the words by whitespace:

#!/bin/bash

sentence="this is a sentence"
IFS=" " read -a words <<< "$sentence"
for word in "${words[@]}"; do
    echo "$word"
done

In bash, read -a will divide a string by $IFS and place the divided parts into elements of the array. See http://mywiki.wooledge.org/BashGuide/Arrays for more information about how bash arrays work.

If you want more details in pursuit of a specific problem, you might want to tell us what the problem is, or risk making this an XY problem.

Upvotes: 4

chepner
chepner

Reputation: 532303

In the assignment

sentence="this is a sentence"

there are no unquoted spaces, so everything to the right of the = is treated as a single word. (Something like sentence=this is a sentence would be parsed as a single assignment sentence=this followed by an attempt to run a program called is.) As a result, the value of sentences is a sequence of 18 characters. It is identical to

sentence=this\ is\ a\ sentence

because again, there are no unquoted spaces.

For the same reason

for word in "this is a sentence"; do
    echo $word
done

has word being set to each word in the following sequence, which only contains a single word because there are no unquoted spaces.

The key difference with your other loop is that parameter expansions are subject to word-splitting after the fact. The loop

for word in $sentence; do
    echo $word
done

after parameter expansion looks like

for word in this is a sentence; do
     echo $word
done

so now word is set to each of the 4 words in the list following the in keyword.

It's not clear what you are actually asking at the end of your question, but the preceding is legal code. There is no requirement that a string be placed in quotes in bash; quotes do not define something as a string value, but simply escape every character that appears within the quotes. "foo" and \f\o\o are the same thing in shell.

Upvotes: 3

John C
John C

Reputation: 1981

Quoting turns any string into a single unit. If you lose the quotes, everything should be fine.

Upvotes: 1

Related Questions