如何在第N行之前打印字符串,并使用AWK或SED从第N行删除特定字符串

时间:2015-08-25 11:59:20

标签: bash awk sed

我有一个由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的特定文本?一旦我得到每一行都是唯一的,就可以很容易地正确标记每一行。

- 最佳

4 个答案:

答案 0 :(得分: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

带评论的修订版(thx to 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
    }'

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

答案 1 :(得分:2)

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

在awk中:

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

在shell中:

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

但这改变了两行的 second 。对于您需要的其他格式,您似乎想要修改两行的第一个。这使得这更加复杂......

要从您拥有的文本转到您想要的文本,让我们以不同的方式看待它,并假设在以“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 ubstitute的替换字符串中插入换行符的限制。相反,我们插入一个未使用的字符,并使用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);

  }'

答案 2 :(得分:1)

这可能适合你(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

答案 3 :(得分:0)

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

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