【问题标题】:Print two patterns only when the second pattern occurs仅在出现第二个图案时打印两个图案
【发布时间】:2019-09-18 09:26:43
【问题描述】:

只有当人有英语科目分数时,我如何才能打印带有分数的名字。

换句话说,grep 为 2 个模式,只有在第 2 个模式匹配时才打印第一个和第二个模式。

输入:

            {
                "sessionId": "000001",
                "name": "ABC",
                "Age": "21",
                "Score": {
                    "English": "A+",
                    "Mathematics": "B-",
                    "String Theory": "C+"
                }
            },
            {
                "sessionId": "000001",
                "name": "CDE",
                "Age": "21",
                "Score": {
                    "English": "A-",
                    "German": "B-",
                    "French": "C+"
                }
            },
            {
                "sessionId": "000001",
                "name": "EFG",
                "Age": "21",
                "Score": {
                    "German": "A+",
                    "Mathematics": "B-",
                    "Machine Learning": "C+"
                }
            },

输出

"name": "ABC",
"English": "A",
"name": "CDE",
"English": "A",

【问题讨论】:

  • 这当然可以用 sed 来完成,但我怀疑 awk 解决方案不会那么神秘......
  • 你的意思是从分数中省略 +/-?

标签: awk sed text-processing


【解决方案1】:
$ jq 'select (.Score.English) | {name, English: .Score.English}' < <(sed 's/},/}/' file) | sed -n 's/^  *//p'
"name": "ABC",
"English": "A+"
"name": "CDE",
"English": "A-"

.

$ cat tst.awk
{
    gsub(/^[[:space:]]+|[[:space:]]*,?[[:space:]]*$/,"")
    key = $1
    gsub(/^"|"[^"]*$/,"",key)
    f[key] = $0
}
key == "English" {
    print f["name"] ORS $0
}

$ awk -f tst.awk file
"name": "ABC"
"English": "A+"
"name": "CDE"
"English": "A-"

如果将来您想打印其他任何东西,例如,使用上述两种方法Age,您只需按照现有模式将它们添加到列表中即可。

【讨论】:

  • name: .name可以写成name,如{name, English: .Score.English}
  • @oguzismail 谢谢。尝试学习jq,但语法有点不直观!
  • Ikr,我至少花了一个月的时间才明白它是如何工作的。您可以研究@peak 的答案,他就像 jq 的 Jon Skeet :D
【解决方案2】:

您可以使用 awk ,在第一个块中删除前导空格。在第二个块中,如果看到包含“name”的行,则将其存储到名为name 的变量中,然后如果以下行之一包含English,则打印较早捕获的变量name 以及当前行.

awk -F: '{sub(/^[[:space:]]+/,"")} /name/{name=$0} /English/{print   name ORS $0}'
"name": "ABC",
"English": "A+",
"name": "CDE",
"English": "A-",

【讨论】:

  • 仅供参考:此答案被标记为低质量,您可能需要改进它。
  • @oguzismail ,感谢您指出。虽然想知道为什么大多数其他答案都没有标记为相同?
  • 没有一个想法。也许有人在找你?我不知道。
【解决方案3】:

这可能对你有用(GNU sed):

sed -n '/name/h;/English/!b;H;g;s/^\s*//Mgp' file

使用-n 选项打开可选打印。

复制包含name的行。

丢弃不包含English的行。

将包含English 的行附加到name 行。

用保留空间的内容替换当前行。

删除任一行开头的所有空格并打印结果。

【讨论】:

    【解决方案4】:

    如果您的输入实际上是一个正确的 JSON 数组而不是一个片段:

    $ jq -r '.[] | select(.Score.English) | {name: .name, English: .Score.English} | @text' demo.json | sed 's/^{\|}$//g; s/,/\n/'
    "name":"ABC"
    "English":"A+"
    "name":"CDE"
    "English":"A-"
    

    【讨论】:

      【解决方案5】:

      Sed 解决方案:

      sed -n 's/^ *//;/name/h;/English/{H;g;p;}'
      

      【讨论】:

        【解决方案6】:

        另一个awk 有点格式检查

        $ awk '/"name":/         {n=$0} 
               /"Score":/        {s=1} 
               s && /}/          {s=n=""} 
               s && /"English":/ {print n ORS $0}' file | awk '$1=$1'
        
        "name": "ABC",
        "English": "A+",
        "name": "CDE",
        "English": "A-",
        

        检查“Score”元素中是否包含“English”。

        【讨论】:

          猜你喜欢
          • 2017-02-08
          • 1970-01-01
          • 2014-12-08
          • 1970-01-01
          • 2016-06-13
          • 1970-01-01
          • 1970-01-01
          • 2022-01-03
          • 1970-01-01
          相关资源
          最近更新 更多