bash,如果条件为

时间:2018-03-30 08:18:03

标签: bash awk

在开始解释我的问题之前,我必须说这是我第一次使用bash和awk命令。

我有一个包含很多行的文件,如果该行的某些字符符合条件,我有兴趣打印其中一些行。我已经有一个简单的方法,但我打算尝试使用awk来查看它是否更快。我尝试的命令受到了同事的启发,但我并不完全理解。

我的文件如下:

@ 15247.479
1 23775U 96005A   18088.90328565 -.00000293 +00000-0 +00000-0 0 9992
2 23775 014.2616 019.1859 0018427 174.9850 255.8427 00.99889926081074
@ 15250.479
1 23775U 96005A   18088.35358271 -.00000295 +00000-0 +00000-0 0 9990
2 23775 014.2614 019.1913 0018425 174.9634 058.1812 00.99890136081067

第4个字段编号是指日期,如果粗体编号优于 startDate 且低于 endDate ,我想打印以1和2开头的行。

我正在尝试:

< $file awk ' BEGIN {ok=0} 
    {date=substring($0,19,10) if ($date>='$firstTime' && $date<= '$lastTime' ) {print; ok=1} else ok=0;next} 
    {if (ok) print}'

这会返回语法错误,但我担心这不是唯一的问题。我真的不明白子串中的 $ 0 是指什么。

感谢大家的帮助!

3 个答案:

答案 0 :(得分:0)

您在变量分配和;之间错过了if。而不是连接shell变量,将它们分配给awk个变量。无需初始化ok=0,未初始化的变量会自动视为falsey。如果您想访问输入字段,请使用$n,其中n是字段编号,而不是substr()

当您到达以ok=0开头的下一行时,您需要设置@,否则您只会继续打印文件的其余部分。

awk -v firstTime="$firstTime" -v lastTime="$lastTime" '
    NF > 3 && $4 > firstTime && $4 <= lastTime { print; ok=1 }
    $1 == "@" { ok = 0 }
    ok { print }' "$file"

答案 1 :(得分:0)

关于$0

的问题

Awk 是为处理表而构建的语言,具有特定于过滤和操作表格数据的语言功能。一种语言功能是自动字段拆分。

如果您在变量或常量前面看到$,则表示&#34;字段。&#34;当 awk 看到$ field_number 在变量上下文中使用时, awk 会根据FS中的内容拆分当前记录缓冲区变量并允许您像处理任何其他变量一样处理它 - 只是该变量的后备存储是记录缓冲区。

$0是一个引用整个记录缓冲区的特殊字段。 awk 文档中有一些有趣的注释,指出$0分配$ field_number 变量,FS和{OFS的副作用{1}}值得深入阅读。

以下是我对您的申请的回答:

(1)首先,LC_ALL可以帮助我们提高速度。我使用ll / ul作为下限和上限 - 其原因将在以后显而易见。将它们指定为脚本之外的变量有助于我们的可读性。适当引用shell变量是一种好习惯。

(2)优良作法是使用BEGIN { ... },就像在尝试中一样,正式初始化变量。如果使用 gawk ,我们可以使用LINT = 1来测试这样的内容。

(3)/^@/可能是我们重置的最简单(也是最快)的模式。我们使用next因为我们从不想对此行应用限制,我们也不希望在输出中看到这一行(即使ll = ul = "")。

(4)在极限上犯错误是非常容易的。始终如一地实施限制,我们的读者将感谢我们。我们记得检查ll和/或ul为空的边角情况。一个角落的情况是我们已经触发了我们的极限,我们正在等待/^@/ - 我们不想再次重新扫描限制。

(5)模式的默认操作是print

(6)记住引用我们的文件名变量将在某一天我们不可避免地遇到名称中带有空格的迷路"$file"时保存我们。

LC_ALL=C awk -v ll="$firstTime" -v ul="$lastTime" '                 # (1)
    BEGIN { ok = 0 }                                                # (2)
    /^@/  { ok = 0; next }                                          # (3)
    !ok   { ok = (ll == "" || ll <= $4) && (ul == "" || $4 <= ul) } # (4)
    ok    # <- print if ok                                          # (5)
' "$file"                                                           # (6)

答案 2 :(得分:0)

这个答案是基于我原来的,但考虑到 @clem 在评论中发送给我们的一些新信息 - 我们现在知道我们需要测试的行总是立即在匹配/^@/的行之后。因此,当我们在此新解决方案中匹配时,我们会立即执行getline以获取下一行,并根据下一行的数据设置ok。我们现在只检查比赛后的线路上的限制,我们不会检查我们不应该在线路上的限制。

LC_ALL=C awk -v ll="$firstTime" -v ul="$lastTime" '
    BEGIN { ok = 0 }
    /^@/  {
        getline
        ok = (ll == "" || ll <= $4) && (ul == "" || $4 <= ul)
    }
    ok # <- print if ok
' "$file"