2

Problem 1: I want to get the array items as user inputs at runtime; print the items and print the array length. This is what I have:

read -a myarray
echo "array items" ${myarray[@]}
echo "array length" ${#myarray[@]}

At runtime, I gave the following as input,

$ ("apple fruit" "orange" "grapes")

The output was,

array items "apple fruit" "orange" "grapes"
array length 4

which is not correct.

If I don't ask for user input and instead used an array declared and initialised as part of the code as myarray=("apple fruit" "orange" "grapes") the array length is echoed as 3. So, It seems like my usage of read command is not right.

Problem 2: If I add a prompt to the read command as follows,

read -p "enter array items: " myarray

the first item "apple fruit" gets printed as fruit" and the length is also wrong.

If I remove the prompt and add -a, everything is good. If I combine both a and p and give it as read -ap, prompt doesn't popup at all. It waits for values without any message. Why is it so? Can someone explain to me what is wrong?

3 Answers 3

6

Problem 1:

In your example, read does not get its input from a command line argument, but from stdin. As such, the input it receives does not go through bash's string parser. Instead, it is treated as a literal string, delimited by spaces. So with your input, your array values become:

[0]->("apple
[1]->fruit"
[2]->"orange"
[3]->"grapes"

To do what you want, you need to escape any spaces you have, to avoid the delimiter from kicking in. Namely, you must enter the following input after invoking read:

apple\ fruit oranges grapes

Problem 2: In order for read to store the input it receives as an array, you must have an -a switch followed by the array name. So you need:

read -a myarray -p "Enter your items"
4
  • I thought that giving the items within quotes protects the space and treats the input 'apple fruit' as one single item. Thank you very much for explaining it all!
    – Ammu
    Aug 22, 2017 at 22:42
  • 1
    Put another way, standard input is not subject to shell processing. With something like IFS=: read -a arr <<<"a:b:c", it is the read command itself which splits the single line a:b:c into three separate elements, rather than it somehow reading three elements a, b, and c from its input.
    – chepner
    Aug 23, 2017 at 13:29
  • @Alex Thank you! I experimented it with mkdir "abc def" Like you said, the space was protected since mkdir is an exteral command. I also tried cd "abc def" and expected an error since cd is a shell builtin. But, the space was still protected and the command went through.
    – Ammu
    Aug 23, 2017 at 13:46
  • @Ammu The difference is, the "abc def" is getting parsed as a command line argument, and not getting its input from stdin.
    – Alex
    Aug 23, 2017 at 14:03
3

The shell's command line parser cares about double and single quotes but read doesn't (thus it does not remove them, too). For read input you need backslashes:

apple\ fruit orange grapes
0

If you wanted to let the user use shell quoting to delimit the elements (and not only backslash), in zsh, you could do:

IFS= read -r 'string?Please enter the elements: '
array=("${(@XQ)${(z)string}}")
  • (z) is to split $string into elements like the shell parser would do.
  • Q to remove one level of quoting from the resulting words
  • X to report error messages in the syntax
  • @ and the quotes to preserve empty elements.

That way, the user can enter things like:

'first word' "second word" third\ word $'word\nwith\nnewline' ''

Beware that, while there is no expansion being done, the syntax of the shell code that is expected is not limited to quoting. For instance, $(foo bar) would be parsed into a single $(foo bar) word, ${unclosed would cause a syntax error. The user would need to enter them as '$(foo)' 'bar)', or `'${unclosed'.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .