【问题标题】:How to delete the first occurence of a bibtex entry using sed?如何使用 sed 删除第一次出现的 bibtex 条目?
【发布时间】:2014-10-22 09:26:50
【问题描述】:

我想删除bibtex 文件中第一次出现的条目 基于它的id。例如,假设我们有这个文件:

@inproceedings{id1,
  author = "",
  title = "",
  ...
}

@inproceedings{id2,
  author = "",
  title = "",
  ...
}

@misc{id1,
  author = "",
  title = "",
  ...
}

我想删除包含id1的第一个条目,所以我想要的输出 是:

@inproceedings{id2,
  author = "",
  title = "",
  ...
}

@misc{id1,
  author = "",
  title = "",
  ...
}

我想要一种自动化的方式来做到这一点,最好使用sed。到目前为止,我有这个:

sed '/^@.*{id1/, /}/d' input_file

但这会删除文件中的所有匹配项。你能帮我想办法吗 只删除第一个?

【问题讨论】:

    标签: regex bash sed pattern-matching bibtex


    【解决方案1】:

    使用 awk,您可以通过自定义 RS(记录分隔符)来做到这一点:

    awk -v RS= -v ORS='\n\n' '!/@inproceedings{id1/' f
    @inproceedings{id2,
      author = "",
        title = "",
          ...
    }
    
    @misc{id1,
      author = "",
        title = "",
          ...
    }
    

    【讨论】:

    • 我会搜索“id1”而不是假设它是第一个。
    • 是的,我可以搜索,但我虽然 OP 只是想删除第一个条目。让我在我的回答中补充一下。
    • 对!我想根据我在问题中提到的 id 删除一些内容。
    • 但是id1 也出现在第三个条目中。可以根据@inproceedings{id1文字删除吗?
    • @ThanasisPetsas:这是否接近您所需要的,还是需要在 awk 条件下进行更多过滤?
    【解决方案2】:
    sed '/^@inproceedings{id1,/,/}/ d' YourFile
    

    删除该部分的每一行 (/start/,/end/ action)

    【讨论】:

    • 但是如果两个带有 id1 的条目都具有inproceedings 属性,那么它们都会被删除,但我只想删除第一个..
    • 确切。调整您的示例,我将尝试调整代码。我只是看到 Potong 已经添加了一个更好的代码,包括这个评论:-)
    【解决方案3】:

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

    sed 'x;/./{x;b};x;/^@.*{id1/,/}/{/}/h;d}' file
    

    一旦第一次出现被删除,在保留空间中设置一个标志,如果设置了标志,则忽略文件末尾的其他行。

    在编写 sed 命令时可以使用两个寄存器。 Sed 会将当前行(减去换行符)放在模式空间(PS)中,第二个寄存器称为保持空间(HS)。 x 将 PS 交换为 HS,h 将 PS 复制到 HS。 sed 单行脚本将 PS 换成 HS,检查 HS 是否有任何字符 /./,如果此条件为真,则将 HS 换成 PS 并退出。如果条件不成立,则为 PS 重新交换 HS 并执行进一步的命令。查找范围条件/^@.*{id1/,/}/,它包含两个字符串之间的所有行,如果找到,则删除这些行,但首先如果发现当前行是结束条件,则将此行复制到 HS。 /}/h。现在后续行将被忽略到文件末尾。

    【讨论】:

    • 酷!它有效,但我不明白它的作用。你能给我更多关于它是如何工作的信息吗?我也会尝试在谷歌中搜索详细信息..谢谢!
    • 非常感谢您的描述!我不知道这些寄存器.. 他们似乎很有帮助!
    【解决方案4】:

    sed 是用于单行简单替换的出色工具,但对于所有其他文本操作,您应该使用 awk。

    $ awk -v type="inproceedings" -v id="id1" -v RS= -v ORS='\n\n' -F'[@{,]' '!($2 == type && $3 == id)' file
    @inproceedings{id2,
      author = "",
      title = "",
      ...
    }
    
    @misc{id1,
      author = "",
      title = "",
      ...
    }
    

    .

    $ awk -v type="inproceedings" -v id="id2" -v RS= -v ORS='\n\n' -F'[@{,]' '!($2 == type && $3 == id)' file
    @inproceedings{id1,
      author = "",
      title = "",
      ...
    }
    
    @misc{id1,
      author = "",
      title = "",
      ...
    }
    

    .

    $ awk -v type="misc" -v id="id1" -v RS= -v ORS='\n\n' -F'[@{,]' '!($2 == type && $3 == id)' file
    @inproceedings{id1,
      author = "",
      title = "",
      ...
    }
    
    @inproceedings{id2,
      author = "",
      title = "",
      ...
    }
    

    如果你想选择匹配的块而不是不匹配的块,只需去掉条件开头的!

    在 1970 年代中期,当 awk 被发明时,所有用于操作多行代码块的 sed 语言结构(即除 s、g 和 p 之外的所有内容)都已过时,因此请忽略它们。永远不需要它们。获取 Arnold Robbins 的《Effective Awk Programming, Third Edition》一书,然后将时间花在这上面。

    【讨论】:

    • 非常感谢!这个解决方案也很棒。但是当我要求基于sed 的更好解决方案时,我接受了 potong 的回答。尽管如此,awk 是一个了不起的命令/语言!也感谢您对书籍的建议。
    • 不客气。既然您要求了,那么接受基于 sed 的解决方案是完全合理的。当然不要实际使用它,因为这会导致经常听到的 sed 口头禅Cool! It works, but I don't understand what it does 并尝试修改它以不同或附加地做最简单的事情,例如当块中缺少author 时向stderr 打印一条消息:-)。
    猜你喜欢
    • 2011-12-30
    • 2014-07-05
    • 2015-06-04
    • 1970-01-01
    • 1970-01-01
    • 2020-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多