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?

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"
  • 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 '17 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 '17 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 '17 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 '17 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'.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.