テキスト処理と正規表現

LinuxBeginner
オンラインで実践に進む

はじめに

この実験では、正規表現を中心に、Linux における強力なテキスト処理テクニックを探索します。さまざまなコマンドを使用してテキストの検索、フィルタリング、操作を行い、Unix 系オペレーティングシステムでテキストデータを扱うための不可欠なスキルを習得します。初心者の方も、スキルの向上を目指す方も、この実験を通じてテキスト処理と正規表現の強固な基礎を築くことができます。

Grep による正規表現の理解

正規表現(regex)は、文字列内の文字の組み合わせを照合するために使用されるパターンです。これらは Linux における多くのテキスト処理タスクの基本となります。まずは、grep を使って基本的な正規表現から始めてみましょう。

まず、練習用の簡単なテキストファイルを作成します。

cd ~/project
echo -e "labex\nexlab\nlab*\nLABEX\nLab" > practice.txt

このコマンドは、現在のディレクトリに 5 行のテキストを含む practice.txt という名前のファイルを作成します。-e オプションを使用すると、改行を表す \n などのエスケープ文字を使用できます。

次に、基本的な正規表現を指定して grep を使用してみましょう。

grep "lab" practice.txt

次のように表示されるはずです。

labex
exlab
lab*

このコマンドは、"lab" を含むすべての行に一致します。大文字と小文字が区別されるため、"LABEX" や "Lab" は出力に含まれないことに注意してください。

より具体的な正規表現を試してみましょう。

grep "^lab" practice.txt

次のように表示されるはずです。

labex
lab*

^ 記号は行の先頭に一致するため、このコマンドは "lab" で始まる行のみに一致します。

次に、検索で大文字と小文字を区別しないようにしてみましょう。

grep -i "lab" practice.txt

これにより、ファイル内の 5 行すべてが一致するはずです。

解説:

  • grep は、パターンを検索するために使用するコマンドです。
  • 検索するパターンは引用符で囲みます。
  • practice.txt は検索対象のファイルです。
  • -i オプションは、検索時に大文字と小文字を区別しないようにします。

Grep の高度な使い方

テキスト検索をより強力かつ効率的にする、grep の高度な機能をいくつか見ていきましょう。

  1. 行番号を表示する:

    grep -n "lab" practice.txt
    

    これにより、一致した箇所の行番号が表示されます。-n オプションは、出力の各行の先頭にテキストファイル内での行番号を付加するよう grep に指示します。

  2. 一致した箇所の前後の行を表示する:

    grep -C 1 "exlab" practice.txt
    

    -C 1 オプションは、一致した行の前後 1 行のコンテキスト(文脈)を表示します。この数値を調整することで、表示するコンテキスト行を増減できます。

  3. 一致しない行を抽出する(反転検索):

    grep -v "lab" practice.txt
    

    -v オプションは一致を反転させ、パターンを含まない行を表示します。これは、結果から特定のパターンを除外したい場合に便利です。

  4. 正規表現の活用:

    grep "lab[ex]*" practice.txt
    

    この正規表現は、"lab" の後に任意の数の "e" または "x" 文字が続くものに一致します。検索でより複雑なパターンを使用する方法を示しています。

解説:

  • -n オプションは、各出力行にファイル内での行番号を付加します。
  • -C 1 は一致箇所の前後 1 行を表示し、文脈を理解するのに役立ちます。
  • -v は一致を反転させ、パターンに一致しない行を表示します。
  • [ex]* は、'e' または 'x' のいずれかが 0 回以上出現することに一致する正規表現です。

これらのコマンドを試して、結果を観察してください。これらのオプションを理解することで、テキストを効果的に検索およびフィルタリングする能力が大幅に向上します。

Sed の導入

sed(ストリームエディタ)は、テキストの解析と変換のための強力なツールです。ファイルや出力ストリームに対して自動的な編集を行う際によく使用されます。まずは基本的な sed 操作から始めましょう。

Sed の構文を理解する

例に入る前に、sed コマンドの基本構文、特にデリミタ(区切り文字)と特殊文字の使用方法を理解することが重要です。

Sed コマンドの構造

sed の置換コマンドの基本構造は次のとおりです。

sed 's/pattern/replacement/flags' filename

構文の分解:

  • s = 置換(substitute)コマンド
  • / = デリミタ(パターン、置換文字列、フラグを区切る)
  • pattern = 検索する文字列
  • replacement = 置換後の文字列
  • flags = g(グローバル/全置換)、i(大文字小文字を区別しない)などのオプション

デリミタについて:スラッシュ (/) とバックスラッシュ ()

デリミタとしてのスラッシュ (/):

  • 置換コマンドの異なる部分を区切るために使用されます。
  • 形式:s/検索/置換/フラグ
  • / 文字自体は、検索パターンや置換テキストの一部ではありません
  • 例:s/Hello/Hi/g は「Hello を Hi にグローバルに置換する」という意味です。

エスケープのためのバックスラッシュ ():

  • 特殊文字をエスケープしたり、リテラル(文字通り)の解釈を示したりするために使用されます。
  • i\(挿入)や a\(追加)などのコマンドで使用されます。
  • 例:1i\First line は「1 行目の前に 'First line' を挿入する」という意味です。

主な違い:

  • / = コマンド各部の区切り文字
  • \ = エスケープ文字 または コマンドの終端

まず、作業用の新しいファイルを作成します。

echo -e "Hello, world\nThis is a test\nHello, labex\nWorld of Linux" > sed_test.txt

これにより、現在のディレクトリに 4 行のテキストを含む sed_test.txt という名前のファイルが作成されます。

次に、sed を使用してテキストを置換してみましょう。

sed 's/Hello/Hi/' sed_test.txt

このコマンドの分解:

  • s = 置換コマンド
  • 最初の / = 検索パターンの開始
  • Hello = 検索するテキスト
  • 2 番目の / = 検索パターンと置換テキストの区切り
  • Hi = 置換後のテキスト
  • 3 番目の / = 置換の終了(フラグなし)

このコマンドは、各行において最初に出現する "Hello" を "Hi" に置換します。デフォルトでは、sed は各行の最初の一致のみを置換します。

注意: この例では、"Hello" が各行に 1 回しか出現しないため、g フラグがなくてもすべてのインスタンスが置換されたように見えます。

g フラグの効果をよりよく理解するために、同じ行に "Hello" が複数回出現するように sed_test.txt を修正してみましょう。

echo -e "Hello, world. Hello everyone\nThis is a test\nHello, labex says Hello\nWorld of Linux" > sed_test.txt

現在、sed_test.txt の内容は次のようになっています。

Hello, world. Hello everyone
This is a test
Hello, labex says Hello
World of Linux

g フラグを付けずに置換コマンドを再度実行します。

sed 's/Hello/Hi/' sed_test.txt

出力は次のようになります。

Hi, world. Hello everyone
This is a test
Hi, labex says Hello
World of Linux

各行の最初の "Hello" だけが置換されていることがわかります。

次に、g フラグを使用してグローバル置換(全置換)を実行します。

sed 's/Hello/Hi/g' sed_test.txt

出力は次のようになります。

Hi, world. Hi everyone
This is a test
Hi, labex says Hi
World of Linux

今回は、各行のすべての "Hello" が "Hi" に置換されました。

詳細な解説:

  • sed 's/Hello/Hi/': 各行で最初に一致した "Hello" を置換します。
    • 構造:s (置換) + /Hello/ (検索パターン) + Hi/ (置換テキスト)
    • 3 つの / 文字はデリミタであり、テキストの一部ではありません。
  • sed 's/Hello/Hi/g': 各行で一致するすべての "Hello" を置換します。
    • 構造:s (置換) + /Hello/ (検索パターン) + Hi/ (置換テキスト) + g (グローバルフラグ)
    • g フラグは "global" の略で、行内のすべての出現箇所に対して置換を行うことを示します。

代替デリミタの使用: テキストにスラッシュが含まれている場合は、他の文字をデリミタとして使用できます。例えば:

sed 's#/path/to/file#/new/path#g' filename

ここでは / の代わりに # がデリミタとして使用されています。これはファイルパスを扱う際に非常に便利です。

これらのコマンドはファイル自体を書き換えるのではなく、変更されたテキストをターミナルに表示するだけであることに注意してください。ファイルを直接編集(上書き)するには、-i オプションを使用します。

sed -i 's/Hello/Hi/g' sed_test.txt

では、ファイルの内容を確認して変更が適用されたか見てみましょう。

cat sed_test.txt

Sed の高度な使い方

sed の基本を理解したところで、テキスト操作のための強力なツールにするための、より高度な機能を見ていきましょう。

  1. 行を削除する:

    sed '2d' sed_test.txt
    

    これはファイルの 2 行目を削除します。sedd コマンドは "delete"(削除)を意味します。

  2. テキストを挿入する:

    sed '1i\First line' sed_test.txt
    

    このコマンドの分解:

    • 1 = 行番号(1 行目の前に挿入)
    • i = 挿入(insert)コマンド
    • \ = コマンドの終端(置換コマンドのデリミタとは異なります)
    • First line = 挿入するテキスト

    これはファイルの 1 行目の前に "First line" を挿入します。i コマンドは "insert" を意味します。

  3. テキストを追加する:

    sed '$a\Last line' sed_test.txt
    

    このコマンドの分解:

    • $ = 最終行を表す
    • a = 追加(append)コマンド
    • \ = コマンドの終端(コマンドの終了とテキストの開始を合図します)
    • Last line = 追加するテキスト

    これはファイルの末尾に "Last line" を追加します。a コマンドは "append" を意味します。

  4. 複数のコマンドを実行する:

    sed -e 's/Hi/Hello/g' -e 's/labex/LabEx/g' sed_test.txt
    

    1 つのコマンドで複数の置換を適用します。-e オプションを使用すると、複数の sed コマンドを指定できます。

  5. 正規表現の活用:

    sed 's/[Ww]orld/Universe/g' sed_test.txt
    

    正規表現を使用して "World" と "world" の両方に一致させ、それらを "Universe" に置換します。

コマンド構文の解説:

  • 2d は 2 行目を削除します。数値を変更することで別の行を削除できます。
    • 構造:行番号 + d (削除コマンド)
  • 1i\ は 1 行目の前にテキストを挿入します。数値を変更して別の位置に挿入できます。
    • 構造:行番号 + i (挿入) + \ (コマンド終端) + テキスト
    • 重要: ここでの \ はデリミタではなく、コマンドとテキストを区切る終端記号です。
  • $a\ はファイルの末尾にテキストを追加します。
    • 構造:$ (最終行) + a (追加) + \ (コマンド終端) + テキスト
    • 重要: 同様に、\ はコマンドを終了させるもので、デリミタではありません。
  • -e を使用すると、1 行で複数の sed コマンドを指定できます。
  • [Ww] は大文字の "W" または小文字の "w" のいずれかに一致する正規表現です。

Sed におけるデリミタ使用のまとめ:

  • 置換コマンド (s): / をデリミタとして使用します:s/パターン/置換/フラグ
  • 挿入/追加コマンド (i/a): \ をコマンド終端として使用します:i\テキスト または a\テキスト
  • その他のデリミタ: 置換コマンドでは、#|: などの代替文字を使用できます。

デリミタを理解するための実践演習:

代替デリミタの動作を確認するために、パスを含むファイルを作成しましょう。

echo -e "/home/user/documents\n/var/log/messages\n/etc/passwd" > paths.txt

異なるデリミタを使用してパスを置換してみてください。

## / をデリミタとして使用(パスに含まれる / と混同しやすく複雑になります)
sed 's/\/home\/user/\/home\/newuser/g' paths.txt

## ## をデリミタとして使用(パスを扱う際に非常に見やすくなります)
sed 's#/home/user#/home/newuser#g' paths.txt

## | をデリミタとして使用(これも見やすいです)
sed 's|/home/user|/home/newuser|g' paths.txt

3 つのコマンドはすべて同じことを行いますが、ファイルパスを扱う場合は後ろの 2 つの方がはるかに読みやすくなります。

これらのコマンドを試して結果を確認してください。-i オプションを使用しない限り、これらの変更はファイルに保存されないことを忘れないでください。

Awk の導入

awk は、特に構造化されたデータの処理に優れた強力なテキスト処理ツールです。入力の各行をレコードとして、その行の各単語をフィールドとして扱います。まずは基本的な awk 操作から始めましょう。

まず、構造化されたデータを含む新しいファイルを作成します。

echo -e "Name Age Country\nAlice 25 USA\nBob 30 Canada\nCharlie 35 UK\nDavid 28 Australia" > awk_test.txt

これにより、ヘッダー行と 4 つのデータ行を含む awk_test.txt という名前のファイルが作成されます。

次に、awk を使用して特定のフィールド(列)をプリントしてみましょう。

awk '{print $1}' awk_test.txt

これは各行の最初のフィールドをプリントします。awk では、$1 は最初のフィールド、$2 は 2 番目のフィールドというように参照します。$0 は行全体を指します。

複数のフィールドをプリントするには:

awk '{print $1, $2}' awk_test.txt

これは各行の 1 番目と 2 番目のフィールドをプリントします。

条件を使用することもできます。

awk '$2 > 28 {print $1 " is over 28"}' awk_test.txt

これは 28 歳より上の人の名前をプリントします。

もう少し複雑なことを試してみましょう。

awk 'NR > 1 {sum += $2} END {print "Average age:", sum/(NR-1)}' awk_test.txt

これはヘッダー行をスキップして、平均年齢を計算してプリントします。

解説:

  • awk では、各行は通常、空白文字によって自動的にフィールドに分割されます。
  • $1$2 などは、各行の 1 番目、2 番目などのフィールドを指します。
  • NR は、現在のレコード(行)番号を表す組み込み変数です。
  • END ブロックは、すべての行が処理された後に実行されます。
  • sum += $2 は、2 番目のフィールド(年齢)の値を合計に加算していきます。

これらのコマンドを試して結果を観察してください。awk はデータ処理タスクにおいて非常に強力です。

まとめ

この実験では、Linux における 3 つの強力なテキスト処理コマンドの基本を学びました。

  1. grep: 正規表現を使用してテキストパターンを検索します。
  2. sed: ストリーム編集とテキスト変換を行います。
  3. awk: 高度なテキスト処理とデータ抽出を行います。

特に sed を使用する際には、g フラグの効果について詳しく調べました。g フラグがない場合、sed は各行の最初の一致箇所のみを置換しますが、g フラグを付けると各行のすべての一致箇所を置換します。同じ行に複数の一致が含まれるようにサンプルファイルを修正することで、g フラグの効果を明確に観察できました。

これらのツールは、Linux ユーザーやシステム管理者にとって不可欠です。これらを使用することで、ファイルを効率的に検索し、テキストを修正し、構造化されたテキストファイルから特定のデータを抽出することができます。これらのコマンドに慣れるにつれて、Linux システムでの日常業務における多くのテキスト処理タスクを大幅に簡素化できることがわかるでしょう。

習得の鍵は練習です。さまざまなシナリオでこれらを使用し、man grepman sedman awk などのマニュアルページを調べて、より高度な機能やオプションを探索してみてください。これらのコマンドには、ここで紹介した以上の多くの機能があり、それらを効果的に使いこなすことで、Linux でテキストファイルを扱う際の生産性を大幅に向上させることができます。