grep
我正在尝试使用, 通过管道读取 bash 中的一行文本以tail
获取文件的最后一行,然后将该行的前三个“单词”(即使用空格分隔它们)分割为数组的元素。
如果我尝试使用循环遍历输出中的元素for
,它会很好地工作,并且我会得到我想要的元素列表:
[] foo=$(grep select file.txt | tail -n 1)
[] echo $foo
0.47331 5.11188 13.1615 # select
[] for x in $foo; do echo $x; done
0.47331
5.11188
13.1615
#
select
这正是我想要它做的!
但是,如果我尝试取出包含 前三个元素的数组foo
,我将无法使其工作:
[] echo "${foo[@]:0:2}"
0.47331 5.11188 13.1615 # select 4.95294 13.5177
特别奇怪的是,该行末尾的最后两个值实际上是来自第一的select
包含in 的行file.txt
(甚至不是该行的前两项,而是第二项和第三项!),所以它们根本不应该是foo
...的一部分
同样,如果我尝试简单地从 中切出一个“单词” foo
,我会得到一个奇怪的输出:
[] echo "${foo[0]}"
0.47331 5.11188 13.1615 # select
[] echo "${foo[1]}"
4.95294
(同样,最后一个值是一个不应该的值,据我所知,即使是 in foo
,它也是第一行的第二项select
in file.txt
...)。
我需要了解发生了什么,以及如何得到我想要的输出,即 array 0.47331 5.11188 13.1615
。
答案1
$foo
不是数组;它是一个包含多个空格分隔的单词的字符串。如果您希望它是一个数组,请将其指定为一个:
foo=( $(grep 'select' file.txt | tail -n 1) )
现在您可以引用${foo[0]}
或前三个元素作为${foo[@]:0:3}
。
请注意,它将${foo[@]}
包含最后一个匹配行中的所有五个元素,因为这里没有任何内容可以仅提取前三个元素。如果你愿意的话,你可以把它foo=("${foo[@]:0:3}")
切成${foo[@]}
合适的尺寸。
或者,如果您想创建仅包含前三个元素的数组,可以awk
像这样使用:
foo=( $(awk '/select/ { a=$1; b=$2; c=$3 } END { print a,b,c }' file.txt) )
答案2
这可以避免将数据读入变量:
$ grep -F select file | tail -n 1 | cut -d ' ' -f 1-3
0.47331 5.11188 13.1615
要将值放入 中的数组中bash
,请使用mapfile
(或readarray
):
$ mapfile -t arr < <( grep -F select file | tail -n 1 | cut -d ' ' -f 1-3 | tr ' ' '\n' )
$ printf 'arr: %s\n' "${arr[@]}"
arr: 0.47331
arr: 5.11188
arr: 13.1615
我使用tr
将数字之间的空格更改为换行符。这样,数组中最后一个值的末尾就不会出现尾随换行符。
您显然可以使用,而不是尴尬的grep
+ tail
+ cut
+管道tr
awk '/select/ { a=$1; b=$2; c=$3 } END { printf("%s\n%s\n%s\n", a, b, c) }'
或者进程替换中类似的东西<(...)
。