テキストファイルから2つのキーワード間のテキストを削除する方法

テキストファイルから2つのキーワード間のテキストを削除する方法

コンテキスト: GNU/Linux Ubuntu。

数千行のファイルがあり、2つの特定のキーワードの間にあるいくつかの行を削除するスクリプトが必要です。
初期ファイルは次のとおりです。

bla bla
...
bla bla
keyword1
bla bla
...
bla bla
keyword2
bla bla
...
bla bla

keyword1との間の部分を除くすべてのファイルを維持したいと思いますkeyword2

考えてみて、keyword1ファイルkeyword2に一度だけ表示できます。これらのキーワードには、行の前後にスペースや<その他の文字を含めることができます。>

キーワードを含む行は次のとおりです(実際にはXMLベースのファイルです)。

<keyword2>  

キーワードはファイルに残ったり、添付のテキストと共に削除したり、両方の結果が満足です。

使い続ける方法がわかりませんgrep。よくわかりませんawk。動作しますか?

答え1

サンプルテキストをファイルに入れ、fileキーワード<>を使用してテストしました。

このコマンドはsedキーワードを削除します

$ < file sed '/keyword1/,/keyword2/d'
bla bla
...
bla bla
bla bla
...
bla bla

このコマンドはsedキーワードを予約します

$ < file sed -n -e '1,/keyword1/p' -e '/keyword2/,$p'
bla bla
...
bla bla
<keyword1>
<keyword2>
bla bla
...
bla bla

答え2

Raku(以前のPerl_6)の使用

raku -ne '.put unless /keyword1/ ^fff^ /keyword2/;'

入力例:

bla bla
...
bla bla
keyword1
bla bla
...
bla bla
keyword2
bla bla
...
bla bla

出力例:

bla bla
...
bla bla
keyword1
keyword2
bla bla
...
bla bla

つまり、Rakuの-neコマンドラインフラグは、Rakuに自動的に印刷せずにコードを実行するように指示します。印刷は.put最初のコマンド(改行文字「print-using-terminator」)で行われます。.前の点はこれの略語putで、ターゲット変数(この場合は入力行のデータを含む)を表します。$_.put$_

このfffディレクティブは、2つの周囲の正規表現に基づいてオンまたはオフにするRakuのsedに似た「トリガー」演算子です。 Raku(およびPerl5)ではunlessyesですif not。最後に、^周辺キャレットはRakuにエンドポイントを除外するようにfff指示します。^fff^

unless否定であるため、エンド^fff^ポイント除外を無効にして出力に合計を維持しますkeyword1。出力から合計を削除する代わりにkeyword2使用してください。fff^fff^keyword1keyword2

(実際にファイルを解析するには、XMLRakuのモジュールを使用して1行のRakuソリューションを作成できますXML。)

https://unix.stackexchange.com/search?q=Raku+%5BXML%5D
https://github.com/raku-community-modules/XML
https://raku.org

答え3

sedの以前の提案は、「キーワード」が行の唯一の単語ではない場合、予想される結果を提供しません。場所に関係なく、任意の段落から2つの単語の間でテキストを抽出するには、特にPerlが必要です。Perlファイルを読む

たとえば、次のようなテキストがあるとします。

Sir Arthur Conan Doyle was born on May 22, 1859, in Edinburgh. 
He studied medicine at the University of Edinburgh and began to write stories while he was a student. 
Over his life he produced more than 30 books, 150 short stories, poems, plays, and essays across a wide range
of genres. 
His most famous creation is the detective Sherlock Holmes, who he introduced in his first novel, A Study in Scarlet (1887). 
This was followed in 1889 by an historical novel, Micah Clarke.

ここで重要な言葉はそれぞれ「医学」と「シャーロック・ホームズ」です。

sed の結果は、段落の最初の行と最後の行を正確に削除します。そして予想される結果は、文の前部分と埋め込まれた部分medicine、そして後ろと埋め込まれた部分も削除しなければなりませんHolmes

PerlのFile Slurpを試してみましょう:

perl -0777 -i -pe 'push @a,/medicine(.*?)Holmes/s;END{print "@a"}' myparagraph.txt

出力:

at the University of Edinburgh and began to write stories while he was a student. 
Over his life he produced more than 30 books, 150 short stories, poems, plays, and essays across a wide range
of genres. 
His most famous creation is the detective Sherlock 

答え4

作業する実際のXML文書がないので、関連文書が次のようになるとします。

<?xml version="1.0"?>
<root>
  <entry>
    <name>Joe</name>
    <number>133</number>
  </entry>
  <entry>
    <name>Mary</name>
    <number>123</number>
  </entry>
  <entry>
    <name>Stan</name>
    <number>233</number>
  </entry>
</root>

作業も少し不明ですので、方法をお見せしましょう。

  1. entry指定された値を持つノードの1つを削除しますname
  2. 値が与えられたら、numberノードの値を変更します。entryname
  3. entry値が与えられると、ノードの1つの内容を削除しますname

これは最初にかなり一般的なコマンドラインXMLパーサーを使用して行われ、次にあまり知られていないxmlstarletパーサーを使用して行われましたxqhttps://kislyuk.github.io/yq/)、有名なJSONパーサーのラッパーですjq

まずXPath構文を使用してくださいxmlstarlet

  1. スタンの取り外し:

    xmlstarlet ed \
        --var name '"Stan"' \
        --delete '//entry[name = $name]' file.xml
    

    これはXPath文字列を取得して"Stan"内部変数に割り当て、それを使用して特定の値を持つノードを$name選択します。ノードを見つけるために特定のパスの代わりに使用するため、ノードは文書のどこにでも存在できます。entrynameentry//entry/root/entry

    見つかったノードが削除さxmlstarletれ、結果のXML文書が標準出力に書き込まれます。

    生成された文書:

    <?xml version="1.0"?>
    <root>
      <entry>
        <name>Joe</name>
        <number>133</number>
      </entry>
      <entry>
        <name>Mary</name>
        <number>123</number>
      </entry>
    </root>
    
  2. Stanの番号を455に変更します。

    xmlstarlet ed \
        --var name '"Stan"' \
        --var value '455' \
        --update '//entry[name = $name]/number' \
        --expr '$value' file.xml
    

    entryこれは、目的のノードを選択するためにXPath文字列を含む内部変数を使用するという点で、最初のコマンドと似ています。$name見つかったノードは削除しませんが、number内部変数に指定された値で子ノードを更新します$value

    生成された文書:

    <?xml version="1.0"?>
    <root>
      <entry>
        <name>Joe</name>
        <number>133</number>
      </entry>
      <entry>
        <name>Mary</name>
        <number>123</number>
      </entry>
      <entry>
        <name>Stan</name>
        <number>455</number>
      </entry>
    </root>
    
  3. Stanの記録を消去する:

    xmlstarlet ed \
        --var name '"Stan"' \
        --update '//entry[name = $name]' \
        --value '' file.xml
    

    これは、その値を空の文字列に更新してノードをクリアできることを再度示しています。

    生成された文書:

    <?xml version="1.0"?>
    <root>
      <entry>
        <name>Joe</name>
        <number>133</number>
      </entry>
      <entry>
        <name>Mary</name>
        <number>123</number>
      </entry>
      <entry/>
    </root>
    

xqラッパーはjqXML文書を解析し、それをJSONにトランスコードします。次に、jq結果のJSON文書に式を適用し、オプションでそれをXMLに変換します。

この回答の冒頭にある文書を見ると、入力がXML文書であっても、次のxqJSON文書が内部的に使用されます。

{
  "root": {
    "entry": [
      {
        "name": "Joe",
        "number": "133"
      },
      {
        "name": "Mary",
        "number": "123"
      },
      {
        "name": "Stan",
        "number": "233"
      }
    ]
  }
}
  1. スタンの取り外し:

    xq --xml-output \
        --arg name 'Stan' \
        'del(.root.entry[] | select(.name == $name))' file.xml
    

    このdel()関数を使用してjq指定されたパスを削除します。パスは、コマンドラインで設定した内部変数の値を.root.entryキーとする配列から要素を選択することによって検索されます。.name$name

  2. Stanの番号を455に変更します。

    xq --xml-output \
        --arg name 'Stan' \
        --arg value 455 \
        '(.root.entry[] | select(.name == $name)).number |= $value' file.xml
    

    これは前の式と似ていますが、選択したノードを削除するのではなく、内部変数を使用してキーdel()にアクセスしてその値を更新します。.number$value

  3. Stanの記録を消去する:

    xq --xml-output \
        --arg name 'Stan' \
        '(.root.entry[] | select(.name == $name)) |= null' file.xml
    

    もう一度同様の式を使用して関心のあるノードを選択し、それを空にするようにnull更新します。empty代わりにinを使用するとnullノードが削除されるため、これは上記の最初の点と同じ結果を得るもう1つの方法です。

xmlstarletこれらと式xqの主な違いjqは、絶対パスにwithを使用し、xq関心のあるノードを再帰的に検索するために//XPath式にwithを使用することです。xmlstarlet再帰検索を使用することもできますが、xqこれは少しトリッキーであり、ここで使用することを選択した例ではこれを必要としません。

関連情報