【问题标题】:How to print a string before line N and delete specific string from line N using AWK or SED如何在第 N 行之前打印字符串并使用 AWK 或 SED 从第 N 行删除特定字符串
【发布时间】:2015-08-26 15:07:12
【问题描述】:

我有一个由 HTML 代码组成的文本文件,我需要对其进行操作以使其更具可读性。我的问题是每个文件名有两行不是唯一的,但我需要将它们区分开来:

编辑

我会在这里为那些提出要求的人输入:

<body>
<tbody>
<tr><td><b>Test Suite</b></td></tr>
<tr><td><a href="HAPPY/3_step_minimal_foundation_no_prefill_HAPPY">3_step_minimal_foundation_no_prefill_HAPPY</a></td></tr>
<tr><td><a href="HAPPY/fullform_no_prefill_HAPPY">fullform_no_prefill_HAPPY</a></td></tr>
<tr><td><a href="HAPPY/fullform_mobile_foundation_no_prefill_HAPPY">fullform_mobile_foundation_no_prefill_HAPPY</a></td></tr>
<tr><td><a href="SAD/3_step_minimal_foundation_SAD">3_step_minimal_foundation_SAD</a></td></tr>
<tr><td><a href="SAD/fullform_SAD">fullform_SAD</a></td></tr>
<tr><td><a href="SAD/fullform_mobile_foundation_SAD">fullform_mobile_foundation_SAD</a></td></tr>
<tr><td><a href="HAPPY_PLUS_OPTIONS/3_step_minimal_foundation_HAPPY_PLUS_OPTIONS">3_step_minimal_foundation_HAPPY_PLUS_OPTIONS</a></td></tr>
<tr><td><a href="HAPPY_PLUS_OPTIONS/fullform_HAPPY_PLUS_OPTIONS">fullform_HAPPY_PLUS_OPTIONS</a></td></tr>
<tr><td><a href="HAPPY_PLUS_OPTIONS/fullform_mobile_foundation_HAPPY_PLUS_OPTIONS">fullform_mobile_foundation_HAPPY_PLUS_OPTIONS</a></td></tr>
<tr><td><a href="SAD_PLUS_OPTIONS/3_step_minimal_foundation_SAD_PLUS_OPTIONS">3_step_minimal_foundation_SAD_PLUS_OPTIONS</a></td></tr>
<tr><td><a href="SAD_PLUS_OPTIONS/fullform_SAD_PLUS_OPTIONS">fullform_SAD_PLUS_OPTIONS</a></td></tr>
<tr><td><a href="SAD_PLUS_OPTIONS/fullform_mobile_foundation_SAD_PLUS_OPTIONS">fullform_mobile_foundation_SAD_PLUS_OPTIONS</a></td></tr>
</tbody></table>
</body>

3_step_minimal_foundation_no_prefill_HAPPY

3_step_minimal_foundation_no_prefill_HAPPY

例如需要变成:

3_step_minimal_foundation_no_prefill

3_step_minimal_foundation_no_prefill_HAPPY

我当前的文本文件状态:

这是实现此目的的代码:

$ sed -n '/ref/p' EVERYTHING | awk -F'[/"<> ]+' '{sub("", "", $6); print $6, $7, $8}' | tr -s '[[:space:]]' '\n' | awk -v n=3 '1; NR % n == 0 {print ""}' | sed '/^HAPPY/s/^/Flow Type\: /' | sed '/^SAD/s/^/Flow Type\: /' | sed '$d'

Flow Type: HAPPY
3_step_minimal_foundation_no_prefill_HAPPY
3_step_minimal_foundation_no_prefill_HAPPY

Flow Type: HAPPY
fullform_no_prefill_HAPPY
fullform_no_prefill_HAPPY

Flow Type: HAPPY
fullform_mobile_foundation_no_prefill_HAPPY
fullform_mobile_foundation_no_prefill_HAPPY

Flow Type: SAD
3_step_minimal_foundation_SAD
3_step_minimal_foundation_SAD

Flow Type: SAD
fullform_SAD
fullform_SAD

Flow Type: SAD
fullform_mobile_foundation_SAD
fullform_mobile_foundation_SAD

Flow Type: HAPPY_PLUS_OPTIONS
3_step_minimal_foundation_HAPPY_PLUS_OPTIONS
3_step_minimal_foundation_HAPPY_PLUS_OPTIONS

Flow Type: HAPPY_PLUS_OPTIONS
fullform_HAPPY_PLUS_OPTIONS
fullform_HAPPY_PLUS_OPTIONS

我想要的输出:

Flow Type: HAPPY
Flow Name: 3_step_minimal_foundation_no_prefill
File Name: 3_step_minimal_foundation_no_prefill_HAPPY

Flow Type: HAPPY
Flow Name: fullform_no_prefill
File Name: fullform_no_prefill_HAPPY

Flow Type: HAPPY
Flow Name: fullform_mobile_foundation_no_prefill
File Name: fullform_mobile_foundation_no_prefill_HAPPY

Flow Type: SAD
Flow Name: 3_step_minimal_foundation
File Name: 3_step_minimal_foundation_SAD

Flow Type: SAD
Flow Name: fullform
File Name: fullform_SAD

Flow Type: SAD
Flow Name: fullform_mobile_foundation
File Name: fullform_mobile_foundation_SAD

Flow Type: HAPPY_PLUS_OPTIONS
Flow Name: 3_step_minimal_foundation
File Name: 3_step_minimal_foundation_HAPPY_PLUS_OPTIONS

Flow Type: HAPPY_PLUS_OPTIONS
Flow Name: fullform
File Name: fullform_HAPPY_PLUS_OPTIONS

有没有办法从第 N 行删除/保留特定文本?一旦我让每一行都独一无二,就很容易正确地标记每一行。

-最佳

【问题讨论】:

  • 您要在此处删除哪些行?目前还不清楚。您尝试将问题分解为多个步骤是件好事,但也许直接从输入到输出会更容易。
  • 我不想删除任何行,我想区分每个文件不唯一的两行,即(3_step_minimal_foundation_no_prefill_HAPPY 和 3_step_minimal_foundation_no_prefill_HAPPY)我需要为其中一个删除 _HAPPY这些行,其他行保持不变。
  • 我可以添加输入,如果这样会更容易编辑 好的,输入现在可以查看。感谢迄今为止的所有帮助!
  • 嗯,现在肯定不容易,所以去吧,你可以添加任何东西。示例输入和预期输出是标准,以及您已经编写的任何试图实现您正在做的事情的代码。如果你能解释你认为代码应该如何工作,那么解决问题的过程会快得多。
  • 对于与上一行匹配的行,您是否要删除从最后一个下划线到行尾的所有内容?

标签: bash awk sed


【解决方案1】:

好的,对于删除与前一行匹配的行从下划线到行尾的所有内容的基本功能,该过程非常简单。这里有两个选项,100% 未经测试。

在 awk 中:

awk '$0 == last { sub(/_[^_]+$/,""); } { last=$0; } 1' inputfile

在外壳中:

while read line; do
    if [ "$line" = "$last" ]; then
        line="${line%_*}"
    fi
    echo "$line"
    last="$line"
done < inputfile

但这会改变两行的。对于您需要的其他格式,您似乎想要修改两行的 first。这使得这有点复杂......

要从您拥有的文本转到您想要的文本,让我们换一种方式来看,并假设两行重复的行总是出现在以“Flow Type”开头的行之后:"。

awk '
  /^Flow Type:/ {
    print;
    getline one; getline two
    if (one == two) {
      sub(/_[^_]+$/,"",one);
      print "Flow Name: " one;
      print "File Name: " two;
    } else {
      print one; print two
    }
    next;
  }

  1
' inputfile

但我们也可以只处理您的原始 HTML。

在 sed 中,模式识别非常有趣。这是 GNU sed 中的一个:

sed -r 's|<tr><td><a href="([^/]+)/(([^"]+)_[^_]+)".*|Flow Type: \1\nFlow Name: \3\nFile Name: \2|' input.html

这是需要 GNU sed 的换行符 (\n);从结构上讲,它只是普通的 sed。此解决方案不适用于 *BSD 或 OSX。

编辑:根据 potong 的回答,在 OSX 中可以使用的变体如下:

<input.html sed -n 's/^.*"\([^"\/]*\)\/\(\([^"]*\)_\1\)".*/Flow Type: \1|Flow Name: \3|File Name: \2|/p'  | tr '|' '\n'`

或者如果您更喜欢 ERE 而不是 BRE:

<input.html sed -E 's|<tr><td><a href="([^/]+)/(([^"]+)_[^_]+)".*|Flow Type: \1#Flow Name: \3#File Name: \2#|' | tr '#' '\n'

这解决了 OSX sed 无法在 s 替换的替换字符串中插入换行符的限制。相反,我们插入了一个未使用的字符,并使用tr 将其转换为换行符。

要在 awk 中实现相同的目标(即处理 HTML),您可能会采用如下方式:

awk '
  /<tr><td><a/ {

    type=$0; file=$0;
    sub(/^[^"]+/,"",type); sub(/\/.*/,"",type);
    sub(/^[^\/]+\//,"",file); sub(/".*/,"",file);
    name=file; sub(/_[^_]+$/,"",name);

    printf("Flow type: %s\nFlow name: %s\nFile name: %s\n\n", type, name, file);

  }' input.html

好的,这是我的最后一次更新。这是你要找的吗?

awk '
  /<tr><td><a/ {

    type=$0; sub(/^[^"]+"/,"",type); sub(/\/.*/,"",type);
    file=$0; sub(/^[^\/]+\//,"",file); sub(/".*/,"",file);

    if ( index(file, type) ) {
        name=substr(file, 0, index(file, type)-2);
    } else {
        name=file; sub(/_[^_]+$/,"",name);
    }

    printf("Flow type: %s\nFlow name: %s\nFile name: %s\n\n", type, name, file);

  }'

【讨论】:

  • 我很抱歉,我告诉你的信息似乎有些不正确。我的意思是说,可以安全地假设我需要 HAPPY/SAD/HAPPY_PLUS_OPTIONS/SAD_PLUS_OPTIONS 之后的文本作为上面所需输出中显示的流名称。对此真的很抱歉。虽然,这确实适用于你所说的。谢谢你!有没有一种方法可以在我输入每个文件集之间的额外间距之前从每 3 行中截断一个特定的字符串?完成之后然后将每组之间的额外线路重新插入?
  • 这最终摆脱了 Flow Type: 行。是否可以进行一些小的更改来保持这些内容的完整性?
  • 我得到了一个非法选项 -- r 与该命令。我是否缺少能够使用它的模块?我对 SED 和 AWK 还很陌生,所以我很抱歉。非常感谢所有这些帮助!
  • 啊,您使用的是 OSX 还是基于 BSD 的操作系统?如果是这样,请尝试将-r 替换为-E。较新的 BSD 添加了 -r 以与 GNU sed 兼容,但在 OSX 和较旧的变体中 -E 应该可以实现相同的效果。
  • 我正在使用 OSX。尝试了上面的 SED 解决方案,包含 HAPPY_PLUS_OPTIONS/SAD_PLUS_OPTIONS 的文件最后仍然有 HAPPY_PLUS/SAD_PLUS。有没有办法从那些需要它的特定行中选择这个字符串并删除它们作为它们的流名称?
【解决方案2】:

求救

awk 'BEGIN{RS="\n\n"; h="\nFile Name: "}{gsub("_"$3,"",$4); $4=h$4; $5=h$5"\n"; print}'

末尾有一个额外的空行。如果重要的话,你可以用一些额外的逻辑来修剪它,或者简单地将输出传送到 sed '$d'head -n -1

带有 cmets 的修订版(感谢 Tom Fenech)

awk -vRS= '{                        # set awk to paragraph mode 
       sub("_" $3, "", $4)          # remove name from field suffix
       $4 = "\nFlow Name: " $4      # construct new fields with header and newline  
       $5 = "\nFile Name: " $5 "\n" # extra new line for record separation
       print                        # print all fields
    }'

这没什么。您将记录定义为完整的文本块,而不是每一行(这解决了一半的问题)。根据您的格式,我们可以通过索引引用各个字段。从一个定义为另一个字段的字段中删除后缀,并为其他字段准备标题。

【讨论】:

  • 我建议不要在$5 中添加换行符,而是在NR&gt;1{ print "" } 的开头使用一个块。您也可以使用sub 代替gsub,并且可能应该考虑在"_"$3"$" 字段的末尾添加一个锚点。另外,请注意第二行的前缀与每个块的第三行的前缀不同。最后,您可以使用awk -v RS= 一次读取每个块(这样您就可以摆脱BEGIN 块)。
  • 我不太确定这是在做什么。想简单解释一下吗?我尝试实现它,它返回一个奇怪的格式。文件名有新行:,但其中有两个,实际的流名称和文件名不在这些行上。
【解决方案3】:

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

sed -nr 's/^.*"([^"\/]*)\/(([^"]*)_\1)".*/Flow Type: \1\nFlow Name: \3\nFile Name: \2\n/p' file

使用扩展的正则表达式,不要自动打印每一行。匹配所需的字符串并使用反向引用来提取所需的输出。仅在成功替换时打印。

可能适用于其他 sed 的替代解决方案:

sed -n -e 'G' -e 's/^.*"\([^"\/]*\)\/\(\([^"]*\)_\1\)".*\(.\)/Flow Type: \1\4Flow Name: \3\4File Name: \2\4/p' file

【讨论】:

  • 这类似于我想出的 sed 解决方案,但正如他在 cmets 中对我的回答所说的那样,他在 OSX 上而不使用 GNU sed。
  • 嗯。不完全的。当您说$"\n" 时,您真的是要使用bash 的特殊引用,例如$'\n'?如果是这样,那将增加一个单独的依赖项。我很想看到一些使用保持空间来组装线的东西,但使用tr 可能更容易。像这样的东西:&lt; file sed -n 's/^.*"\([^"\/]*\)\/\(\([^"]*\)_\1\)".*/Flow Type: \1|Flow Name: \3|File Name: \2|/p' | tr '|' '\n'
  • @ghoti 查看替代方案的最终修订。
  • 这就是我要说的。你的 sed-fu 很强大。 :-D
【解决方案4】:
awk '
  /<tr><td><a/ {

    type=$0; file=$0;
    sub(/^[^S|^H]+/,"",type); sub(/\/.*/,"",type);
    sub(/^[^\/]+\//,"",file); sub(/".*/,"",file);
    name=file; sub(/_[^fullform|^prefill]+$/,"",name);

    printf("Flow type: %s\nFlow name: %s\nFile name: %s\n\n", type, name, file);

  }’ Filename.txt

这就是我想出的解决方案。它适用于我需要的东西。我最终指定了要截断的实际字符串,现在这很好。将来我会改进这个解决方案,使其更加向前兼容。感谢大家的帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-07
    • 1970-01-01
    • 1970-01-01
    • 2015-09-13
    • 2021-03-29
    • 1970-01-01
    相关资源
    最近更新 更多