【问题标题】:how to select the last line of the shell output如何选择shell输出的最后一行
【发布时间】:2018-08-17 01:16:52
【问题描述】:

嗨,我有一个这样的 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)}' 

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

【问题讨论】:

  • cmd | sed -n \$p
  • 你能解释一下上面的命令吗?
  • $ 选择最后一行,p 打印它。你不太喜欢搜索引擎,是吗?

标签: bash shell awk sed


【解决方案1】:

通常,command | tail -n 1 打印来自command 的输出的最后一行。但是,如果command 的形式为awk '... { ... print something }',您可以重构为awk '... { ... result = something } END { print result }',以避免产生单独的进程以丢弃其他输出。 (相反,您可以将awk '/condition/ { print something }' | head -n 1 替换为awk '/condition/ { print something; exit }'。)

如果您已经在 shell 变量 s3 中获得了结果,并且只想打印最后一行,则参数扩展 echo "${s3##*$'\n'}" 可以做到这一点。表示换行符的 C 样式字符串 $'\n' 是 Bash 扩展,用于删除最长匹配前缀的参数扩展运算符 ## 也不是完全可移植的,因此您应该确保 shebang 行显示 #!/bin/bash ,而不是#!/bin/sh

另请注意,不带引号的$s3 是错误的,除非您特别要求外壳程序对值执行空格标记化和通配符扩展。你基本上应该always use double quotes around variables except in a couple of very specific scenarios.

您的 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,后者打印最后一行。 &lt;&lt;&lt;value "here string" 语法也是 Bash 扩展,不能移植到 POSIX sh。

【讨论】:

  • 用left_stuff=echo $s3 | sed "s/${s3##**$'\n'}//"抓取左边的内容
  • @archcutbank 这看起来不对有几个原因。如果没有引号,echo 的输出就不可能包含换行符。 (正如第三段中已经提到的,这是一个常见的初学者错误。)双 ** 只是多余的。我很确定无论如何你都可以通过参数扩展来实现这一点。不知道你所说的“剩下的东西”是什么意思;您所拥有的将删除左侧的内容。你在找"${s3%%$'\n'*}"来挑第一行吗?或"${s3%$'\n'*}" 获取除最后一个之外的所有内容?
  • 它只是用 sed 去掉了最后一行,剩下的都留给你(最后一行的左边)。
  • 反引号消失了,因为这是您在 cmets 中标记 code 的方式。我的意思是你需要 echo "$s3" 在字符串变量周围加上双引号。正如我在之前的评论中所指出的,你不需要sed,就像相反的操作一样。在 shell 脚本中,您通常希望尽可能避免调用外部进程。
【解决方案2】:

另一种方法是,从末尾处理文件并在第一次匹配后退出。

tac file | awk '/match/{print; exit}'

【讨论】:

    【解决方案3】:

    您好,只需添加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
    

    希望这会对你有所帮助。

    【讨论】:

    猜你喜欢
    • 2015-01-23
    • 1970-01-01
    • 1970-01-01
    • 2015-10-01
    • 2016-06-20
    • 2023-03-06
    • 1970-01-01
    • 2016-10-27
    • 2016-05-26
    相关资源
    最近更新 更多