如何选择shell输出的最后一行

6

你好,我有一个类似这样的shell命令。

s3=$(awk 'BEGIN{ print "S3 bucket path" }
 /Executing command\(queryId/{ sub(/.*queryId=[^[:space:]]+: /,""); q=$0 }
 /s3:\/\//{ print "," $10 }' OFS=',' hive-server2.log)

上述命令的输出如下:
echo $s3

2018-02-21T17:58:22,
2018-02-21T17:58:26,
2018-02-21T18:05:33,
2018-02-21T18:05:34

我想仅选择最后一行。我需要像这样的最后输出。
 2018-02-21T18:05:34

我试过这样做。
awk -v $s3 '{print $(NF)}' 

无法工作。任何帮助都将不胜感激。


1
cmd | sed -n \$p - William Pursell
你能解释一下上面的命令吗? - Teju Priya
1
$ 选择最后一行,p 打印它。你不太擅长搜索引擎,是吗? - tripleee
5个回答

7
一般来说,command | tail -n 1会打印出command的输出的最后一行。但是,如果command的形式为awk '... { ... print something }',则可以重构为awk '... { ... result = something } END { print result }',以避免生成一个单独的进程来丢弃其他输出。(相反,你可以用awk '/condition/ { print something; exit }'替换awk '/condition/ { print something }' | head -n 1。)
如果你已经有了一个shell变量s3的结果,并希望只打印最后一行,则可以使用参数扩展echo "${s3##*$'\n'}"。使用C风格字符串$'\n'表示换行符是Bash的扩展,而参数扩展运算符##用于删除最长匹配前缀也不是完全可移植的,因此你应该确保shebang行为#!/bin/bash,而不是#!/bin/sh
还要注意,除非你特别需要shell对值执行空格分词和通配符扩展,否则$s3没有引号是错误的。基本上,你应该始终在变量周围使用双引号,除了一些非常特定的情况。 你的Awk命令之所以无法工作有两个原因:首先,如前面的段落所解释的那样,你将s3设置为变量的第一个标记,其次是你的Awk脚本(可能是语法错误)。更详细地说,你基本上正在运行
awk -v s3=firstvalue secondvalue thirdvalue '{ print $(NF) }'
          ^ value    ^ script to run   ^ names of files ...

你可能想要说的是

awk -v s3=$'firstvalue\nsecondvalue\nthirdvalue' '{ print $(NF) }'

即使使用引用,你的脚本也会将v设置为某个值,但然后告诉Awk(忽略变量并)处理标准输入,在命令行上,这意味着它将从终端读取。修正后的脚本可能如下所示:

awk 'END { print }' <<<"$s3"

这个命令将变量作为标准输入传递给Awk,然后打印最后一行。使用<<<value "here string" 语法是Bash的扩展功能,不可移植到POSIX sh


使用以下命令从左边提取内容:left_stuff=echo $s3 | sed "s/${s3##**$'\n'}//" - archcutbank
@archcutbank 这看起来有几个问题。没有引号,echo 的输出不可能包含换行符。(正如第三段已经提到的那样,这是初学者常见的错误。)双重星号只是多余的。我相信你可以通过参数扩展来实现它。不确定你所说的“left stuff”是什么意思;你现在的代码将删除左边的内容。你是想用 "${s3%%$'\n'*}" 来选择第一行吗?还是用 "${s3%$'\n'*}" 来获取除最后一行以外的所有内容? - tripleee
它只使用sed命令去掉最后一行,留下其余所有内容(即最后一行之前的内容)。 - archcutbank
反引号消失了,因为这是在注释中标记 code 的方式。我的意思是你需要用双引号将字符串变量括起来,例如 echo "$s3"。正如我之前的评论所指出的那样,你不需要使用 sed 进行此操作,就像相反的操作一样。在 shell 脚本中,通常希望尽可能避免调用外部进程。 - tripleee

4
更为简单的方法是:
command | grep "your filter" | tail -n 1

或者直接。
command | tail -n 1

1
你可以尝试这个方法:
echo -e "This is the first line \nThis is the second line" | awk 'END{print}'

0
另一种方法可以是从文件末尾处理并在第一次匹配后退出。
tac file | awk '/match/{print; exit}'

-1

嗨,你可以通过添加echo $s3 | sed '$!d'来实现。

s3=$(awk 'BEGIN{ print "S3 bucket path" }/Executing command\(queryId/{ sub(/.*queryId=[^[:space:]]+: /,""); q=$0 } /s3:\/\//{ print "," $10 }' OFS=',' hive-server2.log)

echo $s3 | sed '$!d'

它将简单地打印:-
2018-02-21T18:05:34

希望这能对你有所帮助。

如果不加引号,echo 命令只会输出一行。请参考 When to wrap quotes around a shell variable? - tripleee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接