我想将一个特定的行从一个变量转移到另一个变量。我尝试了这个,但它不起作用:
c="1.apple
2.banna
3.peach"
read "Please choose fruit [1-3]:" t
a=$c | awk "NR==$t"
echo "You choose: $a"
我的错误是什么?
答案1
使用这里字符串重定向<<<
和...一起命令替换$()
并且不要忘记在变量两边加上双引号:
a=$(awk "NR==$t" <<< "$c")
答案2
首先:
read "Please choose fruit [1-3]:" t
...不管用。看起来您正在尝试向 shellbuiltin 提供提示字符串read
,但read
将其第一个参数解释为变量的名称,以分配从 stdin 读取的行的值,除非它被赋予选项。
read -p "Please choose fruit [1-3]:" t
...是许多 shell 中支持的选项,并且可能更接近您想要执行的操作。
也就是说,您可能不应该在单分隔赋值中堆叠多个值,除非您之前已经确定了将其拆分的方法。当你这样做时:
var=' some list of things '
shell 最终会将其解析为:
\0 some list of things \0
...并将单个名称分配给单个值。许多 shell 提供更高级的分隔形式 - 例如命名数组 - 但所有 POSIX shell 为每个函数上下文提供至少一个易于定义的数组 - $@
shell 数组。提供命名数组的各种实现通常$@
也模仿其命名数组的 shell 数组行为。
因此,您可以这样做,而不是像您那样将所有这些单独的值分配给单个字符串:
set apple banana peach
您可以通过按数字单独寻址每个单独的值来见证其效果,例如:
printf %s\\n "$1"
...打印:
apple
注意 - 如果您要使用大于 9 的值,最好将引用括在大括号中,例如"${10}"
您可以像这样解决不同字符串的数量"$#"
:
printf %d\\n "$#"
...打印...
3
您可以在单独的字符串列表中解决其中的很多问题,例如:
printf %s\\n "$@"
...打印:
apple
banana
peach
...或者作为单个连接字符串,例如:
printf %s\\n "$*"
...其中单独的数组值字符串连接在 shell 变量中包含的第一个字符上$IFS
。因此,如果数组中的每个字符串都有默认$IFS
值,则它们之间将用单个字符串连接到下一个字符串。例如,上面的命令打印:<space><tab><newline>
<space>
apple banana peach
...但是如果我这样做:
IFS='fruit sucks'; printf %s\\n "$*"
...它打印:
applefbananafpeach
大多数实现命名数组扩展的 shell 都使用类似的语法,但寻址数组的各种方法必须与名称相关联。数组赋值通常如下所示:
array=( apple banana peach )
...或者...
array[0]=apple array[1]=banana array[2]=peach
"$1"
与, "$#"
, "$@"
,相比"$*"
,命名数组通常会像"${array[1]}"
, "${#array[@]}"
,一样工作"${array[@]}"
,并且前面提到的和"${array[*]}"
之间的关系仍然适用。"$*"
"$IFS"
"${array[*]}"
一旦你正确地界定了你的价值观,你的问题就会变得更容易处理:
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"}\"
有些人可能会嘲笑上面的使用eval
,但它在这里的使用并不比许多人希望的安全"${array[$t]}"
(这是使用命名数组执行此操作的一种方法)因为意味着在第一次将其解析为字符串后,将对其"${array[$t]}"
进行第二次评估。$t
无需像我上面那样对其进行测试,以确保它至少包含 1 并且只包含数字并且大于 0(或者 shell 实现的最小命名数组索引可能是什么)任何一种情况都可能导致意想不到的结果。