【问题标题】:Extracting text between two strings. These strings have spaces and are saved in variables提取两个字符串之间的文本。这些字符串有空格并保存在变量中
【发布时间】:2020-07-06 21:33:49
【问题描述】:

我需要通过以下方式提取日期之间的所有文本(以下格式为:月日小时):

start_marker: "Jul  3 2" 
end_marker: "Jul  3 7"

来自具有以下示例格式数据的日志文件

<unneeded text>
Fri Jul  3 2:51:54:780 2020
<needed text> 
<needed text> 
<needed text> 
Fri Jul  3 5:51:54:780 2020 
<needed text> 
<needed text> 
Fri Jul  3 7:51:54:780 2020 
<unneeded text>

我正在尝试下面的脚本,但它返回一个空白的 log_collector 文件

start_month="Jul"
start_date="3"
start_hour="2"

end_month="Jul"
end_date="3"
end_hour="7"

start_marker="$start_month  $start_date $start_hour"
end_marker="$end_month  $end_date $end_hour"

sed -n '/"$start_marker"/,/"$end_marker"/p' logfile >> "log_collector"

cat log_collector

【问题讨论】:

  • 您的 sed 脚本用单引号括起来,因此没有变量扩展。尝试删除双引号,然后将 sed 脚本的单引号更改为双引号。
  • 如果 start-/end_hour 不存在怎么办?它应该搜索最近的小时/时间字符串 x
  • @alecxs 嗯,日志文件每 5 分钟更新一次,所以这是一个非常遥远的可能性。但我没有想到这一点,应该在我的问题中说清楚。感谢您的来电。
  • &lt;needed text&gt; 中的任何一行是否可以包含看起来像日期的文本,例如Jul 3 2?如果是这样,您如何将像这样的行中的文本与您实际要匹配的日期行分开?
  • 嘿@EdMorton, 中没有任何看起来像日期的内容。

标签: shell sed


【解决方案1】:

在使用 sed + 变量时使用双引号,否则 sed 不会读取您的变量,您的脚本现在被读取/执行,因为文件已在您的示例中写入:

+ start_month=Jul
+ start_date=3
+ start_hour=2
+ end_month=Jul
+ end_date=3
+ end_hour=7
+ start_marker='Jul  3 2'
+ end_marker='Jul  3 7'
+ sed -n '/"$start_marker"/,/"$end_marker"/p' logfile 
+ cat log_collector
...empty file

改为尝试:

sed -n "/${start_marker}/,/${end_marker}/p" logfile >> "log_collector"

结果:

+ variables...
+ sed -n '/Jul  3 2/,/Jul  3 7/p' logfile
+ cat log_collector
Fri Jul  3 2:51:54:780 2020
text...

您的脚本现在将根据需要输出变量。

但是当您将 *_marker 用于相同的值时,我真的不明白使用 start_* 和 end_* 变量的意义,但也许这只是一个糟糕/令人困惑的例子 :)

提示:使用“bash -x”启动脚本或添加“set -x”,您将看到脚本是如何启动的。

编辑:我在您的 cmets 中看到 Bill Jetzer 更快,但是请参阅上面的示例。

【讨论】:

  • 感谢您提供清晰的示例,在 sed 上有点新。知道我也必须使用大括号。另外,我更新了问题,您对变量和标记是正确的,我将它们设为相同以避免混淆。
  • @Anupam 您不需要大括号 - 它们不会受到伤害,但如果没有它们,脚本的行为将完全相同。您的脚本的问题是引用不正确,请将您的原始脚本复制/粘贴到shellcheck.net 以查看问题。请注意,尽管没有边界,脚本包含错误 - 例如,Jul 3 1 将匹配行 Jul 3 12
  • 感谢@EdMorton,感谢您的意见。请原谅我的菜鸟问题,但边界是指大括号?如果没有,那我将如何为此设置边界?
  • 不,当没有现有的分隔符时,大括号只是围绕变量,例如foo${var}barfoobar 之间扩展$var 的值,正则表达式上下文中的边界定义匹配字符串必须开始/结束的位置,例如$ 用于字符串结尾, ^` 用于字符串开头,\b 用于某些工具中单词的开头/结尾等。您应该询问发布此答案的人如何使其更健壮。
【解决方案2】:

FWIW 我会使用一个标志(下面的inRange)而不是一个范围(不包括 sed,因为它没有变量)并且只检查看起来像您的日期/时间的行上的日期/时间标记行(因此下面的长正则表达式):

$ cat tst.awk
BEGIN { FS = "[[:space:]:]+" }
/^([[:upper:]][[:lower:]]{2} +){2}[0-9]{1,2} +([0-9]{1,2}:){3}[0-9]{3} +[0-9]{4} *$/ {
    marker = $2" "$3" "$4
}
marker == start_marker { inRange = 1 }
inRange { print }
marker == end_marker { inRange = 0 }

.

$ awk -v start_marker='Jul 3 2' -v end_marker='Jul 3 7' -f tst.awk file
Fri Jul  3 2:51:54:780 2020
<needed text>
<needed text>
<needed text>
Fri Jul  3 5:51:54:780 2020
<needed text>
<needed text>
Fri Jul  3 7:51:54:780 2020

请参阅 Is a /start/,/end/ range expression ever useful in awk? 了解我为什么不使用范围表达式 (/start/,/end/)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多