【问题标题】:Bash script: using variables / parameter in sedBash 脚本:在 sed 中使用变量/参数
【发布时间】:2023-10-24 03:33:01
【问题描述】:

我正在尝试编写一个小 bash 脚本,您可以在其中指定分钟数,它将显示最后 X 分钟的日志文件的行。 为了得到线条,我使用 sed

sed -n '/time/,/time/p' LOGFILE

在 CLI 上这可以完美运行,但在我的脚本中却没有。

# Get date
now=$(date "+%Y-%m-%d %T")

# Get date minus X number of minutes -- $1 first argument, minutes
then=$(date -d "-$1 minutes" +"%Y-%m-%d %T")

# Filter logs -- $2 second argument, filename
sed -n '/'$then'/,/'$now'/p' $2

我尝试了不同的方法,但似乎都不起作用:

result=$(sed -n '/"$then"/,/"$now"/p' $2)
sed -n "/'$then'/,/'$now'/p" "$2"
sed -n "/$then/,/$now/p" $2
sed -n "/$then/,/$now/p" "$2

有什么建议吗? 我在 Debian 5 上,echo $SHELL/bin/sh

编辑: 该脚本不产生任何输出,因此不会出现错误。 在日志文件中,每个条目都以这样的日期开头2013-05-15 14:21:42,794

【问题讨论】:

  • 你能从日志中显示一行吗?

标签: bash sed


【解决方案1】:

我认为主要问题是您尝试通过字符串匹配来执行算术比较。 sed -n '/23/,/27/p' 为您提供包含23 的第一行和包含27 的下一行之间的行(然后从包含23 的下一行到包含27 的下一行,依此类推在)。它确实不会为您提供包含 23 到 27 之间数字的所有行。如果输入看起来像

19
22
24
26
27
30

它不输出任何东西(因为没有23)。使用字符串匹配的 awk 解决方案也有同样的问题。因此,除非您的 then 日期字符串逐字出现在日志文件中,否则您的方法将失败。您必须将日期字符串转换为数字(删除-<space>:),然后使用算术比较而不是字符串匹配来检查结果数字是否在正确的范围内。这超出了 sed 的能力; awk 和 perl 可以很容易地做到这一点。这是一个 perl 解决方案:

#!/bin/bash

NOW=$(date "+%Y%m%d%H%M%S")
THEN=$(date -d "-$1 minutes" "+%Y%m%d%H%M%S")

perl -wne '
  if (m/^(....)-(..)-(..) (..):(..):(..)/) {
    $date = "$1$2$3$4$5$6";
    if ($date >= '"$THEN"' && $date <= '"$NOW"') {
      print;
    }
  }' "$2"

【讨论】:

  • 好吧,这行得通。可惜不能用 shell 工具开箱
  • 如果您更喜欢传统的 shell 管道,您也可以使用 sed -e 's/^\(....\)-\(..\)-\(..\) \(..\):\(..\):\(..\)/\1\2\3\4\5\6 /' "$2" | awk '$1 &gt;= '"$THEN"' &amp;&amp; $1 &lt;= '"$NOW" | sed -e 's/^\(....\)\(..\)\(..\)\(..\)\(..\)\(..\) /\1-\2-\3 \4:\5:\6/',但这并不是真的更具可读性。
【解决方案2】:

不要让嵌套引号让自己头疼。使用 -v 选项和 awk 将 shell 变量的值传递到脚本中:

#!/bin/bash

# Get date
now=$(date "+%Y-%m-%d %T")

# Get date minus X number of minutes -- $1 first argument, minutes
delta=$(date -d "-$1 minutes" +"%Y-%m-%d %T")

# Filter logs -- $2 second argument, filename
awk -v n="$now" -v d="$delta" '$0~n,$0~d' $2

也不要使用 shell 内置函数的变量名,例如 then

【讨论】:

  • 我已经修正了小错字(-v而不是=v),不幸的是,这也不会产生任何输出。
  • 好地方。然后尝试调试问题,在脚本中添加echo "$now $delta" 并检查格式是否与您的日志文件中的格式匹配。
  • 嗯,这是正确的提示。由于我正在检查 HH:MM:SS 并且日志是 HH:MM:SS,MS,因此脚本没有找到任何内容。我将脚本调整为sed -n "/$then*/,/$now*/p" $2。我不确定如何正确调整 awk 命令,但一旦调整,它也应该可以工作。
  • HH:MM:SSHH:MM:SS,MS 的子字符串,所以这不解释了吗?
  • 嗯...你是对的,它不起作用...即使它曾经做过一次。就像我在第一篇文章中写的那样,日志格式是2013-05-15 14:21:42,794,后跟一个空格,然后是日志信息,脚本格式是2013-05-15 14:21:42