Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. It's 100% free, no registration required.

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?

share|improve this question
    
Did you forget the -p in read? –  BroSlow Jan 18 at 8:59
    
a=$(echo $c | awk "NR==$t") ? –  Archemar Jan 18 at 10:11
    
Please don't post answers unless you actually want to answer the question. Have a look at the tour to understand how this site works. –  terdon Jan 18 at 16:55

2 Answers 2

Use Here String redirection <<< together with Command Substitution $() and don't forget to put double quotes around your variables:

a=$(awk "NR==$t" <<< "$c")
share|improve this answer
1  
Don't forget to put double quotes around command substitution, too! –  Darkhogg Jan 18 at 12:27
1  
@Darkhogg - quoting the command substitution makes no difference here as it is contextually an assignment - not a list. 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 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.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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