I would like to take a specific line from a variable to another variable. I tried this but it doesn't work:
c="1.apple
2.banna
3.peach"
read "Please choose fruit [1-3]:" t
a=$c | awk "NR==$t"
echo "You choose: $a"
What is my mistake?
I would like to take a specific line from a variable to another variable. I tried this but it doesn't work:
c="1.apple
2.banna
3.peach"
read "Please choose fruit [1-3]:" t
a=$c | awk "NR==$t"
echo "You choose: $a"
What is my mistake?
Use Here String redirection <<<
together with Command Substitution $()
and don't forget to put double quotes around your variables:
a=$(awk "NR==$t" <<< "$c")
var=$(: std out of command sub)
is no different than var="$(: std out of command sub)"
and the former form is a little less cluttered.
– mikeserv
Jan 18 '15 at 17:41
In the first place:
read "Please choose fruit [1-3]:" t
...will not work. It looks like you're trying to provide a prompt string to the shell builtin read
, but read
interprets its first argument as the name of a variable to assign the value of the line it reads from stdin unless it is handed options.
read -p "Please choose fruit [1-3]:" t
...is an option supported in many shells and is probably nearer to what you intend to do.
That said, you probably shouldn't stack multiple values in a singly delimited assignment unless you have previously settled on a means splitting it out. When you do:
var=' some list of things '
The shell will eventually parse that out to something like:
\0 some list of things \0
...and assign the single name to the single value. Many shells offer more advance forms of delimiting - such as named arrays - but all POSIX shells provide at least one easily definable array per function context - the $@
shell array. Various implementations providing named arrays generally mimic the $@
shell array behavior for their named arrays as well.
So rather than assigning all of those individual values to a single string as you do, you might do instead:
set apple banana peach
You can witness the effect of this by addressing each individual value singly by number like:
printf %s\\n "$1"
...which prints:
apple
Note - if you get to using values larger than 9 it is best to enclose the reference in braces like "${10}"
You can address the number of different strings in "$#"
like:
printf %d\\n "$#"
...which prints...
3
You can address the lot of them in a list of separate strings like:
printf %s\\n "$@"
...which prints:
apple
banana
peach
...or as a single, concatenated string like:
printf %s\\n "$*"
...where the separate array value strings are concatenated on the first character contained in the shell variable $IFS
. So if you have a default $IFS
value of <space><tab><newline>
each string in the array will join to the next with a single <space>
betwixt them. The above command, for example, prints:
apple banana peach
...but if I do:
IFS='fruit sucks'; printf %s\\n "$*"
...it prints:
applefbananafpeach
Most shells that implement named array extensions do so with similar syntax, excepting that the various means of addressing the array must be associated with a name. An array assignment typically looks like:
array=( apple banana peach )
...or...
array[0]=apple array[1]=banana array[2]=peach
Compared to "$1"
, "$#"
, "$@"
, "$*"
, named arrays will usually work like "${array[1]}"
, "${#array[@]}"
, "${array[@]}"
, and "${array[*]}"
where the previously mentioned relationship between "$*"
and "$IFS"
still holds true for "${array[*]}"
.
Once you have properly delimited your values your problem becomes a much easier one to handle:
set apple banana peach; n=0
{ for a do printf "$((n+=1)).:\t%s\n" "$a"; done
printf "Please choose fruit [1-$#]: "; read t
} <>/dev/tty >&0 &&
[ "0$((!${#t}))" -lt "0${t##*[!0-9]*}" ] &&
eval 'printf "You choose: %s\n" "${'"$t"}\"
Some may scoff at the use of eval
above but its use here is no less safe than many would do like "${array[$t]}"
(which is one way you might do this with named arrays) because "${array[$t]}"
implies a second evaluation of $t
as an index after it is first parsed as a string. Without testing it as I do above to ensure that it contains at least one and nothing other than a digit and that it is greater than 0 (or whatever the shell implementation's minimum named array index might be) either case could render unintentional results.
-p
inread
? – Jan 18 '15 at 08:59