1. 概要

Linuxでテキストファイルを処理する場合、読みやすくしたり、さらに処理したりするために、ファイルから空白行を削除する必要があることがよくあります。

このチュートリアルでは、実際の例を通じて、ファイルから空白行を削除するためのいくつかの一般的なシナリオについて説明します。

2. 問題点

このチュートリアルで空白行について話すときは、空白文字のみを含む行について話します。

プレーンテキストファイルがあるとしましょう:

$ cat with_blank.txt
    

   
This is the first non-blank line.

Some data comes here

-----

1
  2
    3
 
-----   
Data End.

This is the last non-blank line.
  
  
  

上記の出力が示すように、ファイル with_blank.txt には、先頭の3行と末尾の3行の空白行を含む空白行が含まれています。

通常、ファイルから空白行を削除する方法は3つあります。

  • ファイル内のすべての空白行を削除します
  • 先頭の空白行のみを削除—ファイルの先頭から最初の非空白行までのみ削除します
  • 末尾の空白行のみを削除—ファイルの最後の非空白行の後の行のみを削除します

このチュートリアルでは、 grep sed awk 、およびtacコマンドを使用してこれらに対処しようとします。

3. 空白行に一致するパターン

空白行を削除するには、最初にそれらを識別する必要があります。 正規表現は明らかなアプローチのように思われ、最もポータブルなソリューションはPOSIX BREを使用することです:

^[[:space:]]*$

[:space:] はPOSIX標準文字クラスであり、 [\ t \ n \ r \ f \v]と同じです。

文字クラスは非常に頻繁に使用されるため、対応する短縮文字クラスが利用可能です。 たとえば、 \ sはPOSIXクラス[:space:] を表し、 \S[^\s][と同等です。 X120X]。

Java、Perl、Python、GNU grep 、GNU sed 、GNU awk など、多くのプログラミング言語とテキスト処理ツールは、これらの短縮文字クラスをサポートしています。 後のセクションで例を示します。

省略形の文字クラスを使用して空白行を一致させる場合、正規表現は次のようにコンパクトになります。

^\s*$

次に、空白行を削除する問題を解決する方法を見てみましょう。

4. ファイルからすべての空白行を削除します

すべての空白行を削除することは、先頭または末尾の空白行のみを削除するよりも簡単な問題です。 これは、空白行が1つ見つかった後、それを保持するか削除するかを確認する必要がないためです。

私たちの目標は、出力を取得することです。

This is the first non-blank line.
Some data comes here
-----
1
  2
    3
-----   
Data End.
This is the last non-blank line.

この問題をどのように解決できるか見てみましょう。

4.1. grepを使用する

grepユーティリティはテキストの検索に優れていることがわかっています。 ただし、行の削除は一種のファイル編集操作です。 問題に対して間違ったツールを選択しているようです。

私達 grepの-vオプションを使用して、空白行のパターンを含まない行を印刷できます。 または、空白以外の文字を含む出力行をgrepに指示します。

grepコマンドがどのように問題を解決するかを見てみましょう。

$ grep -v '^[[:space:]]*$' with_blank.txt

grep 実装が、広く使用されているGNU Grepなどの短縮文字クラスをサポートしている場合、コマンドをかなり短くすることができます。

$ grep '\S' with_blank.txt

出力を入力ファイルに書き戻すには、出力を一時ファイルに保存してから、元の入力ファイルに「mv」する必要があります。

$ grep '\S' with_blank.txt > tmp.txt && mv tmp.txt with_blank.txt

4.2. sedを使用する

sedコマンドにはdアクションがあります。これは、現在のパターンスペースを削除することを意味します。

空白の行パターンに一致する行を削除することで、問題を簡単に解決できます。

$ sed '/^[[:space:]]*$/d' with_blank.txt

逆に解決することもできます。行に空白以外の文字が含まれている場合、その行は削除されません(!d )。

GNU sed のように、sed\S を非空白文字クラスとしてサポートしている場合、コマンドは次のように単純になります。

$ sed '/\S/!d' with_blank.txt

多くのsed実装は「インプレース」編集をサポートしているため、変更を入力ファイルに保存して戻すことができます。

たとえば、GNU sed では、-iオプションを使用できます。

$ sed -i '/^[[:space:]]*$/d' with_blank.txt

4.3. awkを使用する

awk コマンドを使用すると、さまざまな方法で空白行を削除できます。

簡単な解決策から始めましょう:

$ awk '!/^[[:space:]]*$/' with_blank.txt

上記のソリューションでは、行が空白の行パターンと一致しない場合、それを印刷します。

それはかなり短い形式で書かれています。 完全な方法で書くと、次のようになります。

$ awk '{ if($0 !~ /^[[:space:]]*$/) print $0 }' with_blank.txt

その短い形式で書くことができる理由を理解しましょう:

  • 正規表現パターンをテストするときに、テスト文字列を指定しない場合、awkはデフォルトで現在の行を取得するので、 if($ 0!〜/ pattern /)と記述できます。 as if(!/ pattern /)
  • ‘{if(condition){action}}’‘condition {action}’ と書くこともできるので、‘!/ ^ [[:スペース:]] * $ / {print $ 0}’
  • awkのデフォルトのアクションはprint$0であり、Trueはデフォルトのアクションをトリガーします; したがって、 {print $ 0} を省略して、‘!/ ^ [[:space:]] * $/’を使用できます。

この問題を解決する別の方法は、行に空白以外の文字が含まれているかどうかを確認することです。

$ awk '/\S/' with_blank.txt

正規表現チェックに加えて、awkの組み込みNF変数をチェックして、行が空白かどうかを判断することもできます。

$ awk 'NF' with_blank.txt

NF 変数は、現在の入力行のフィールド数を保持します。 awk では、デフォルトのフィールドセパレーター( FS )はスペースです。

FSがスペースの場合、先頭と末尾の空白文字はすべてスキップされます。したがって、行が空白の場合、フィールドはありません。つまり、変数 NF = =0

awkでは、ゼロ以外の数値はTrue として評価されます。したがって、‘NF’はすべての非空白行を出力します。

5. 先頭の空白行のみを削除する

先頭の空白行のみを削除する場合、主な問題は、最初の非空白行がどこから始まるかを知ることです。

grepコマンドではこの問題を解決できません。 ただし、強力なsedおよびawkユーティリティを使用してそれを行うことはできます。

問題の実用的な解決策は次のように印刷されます。

This is the first non-blank line.

Some data comes here

-----

1
  2
    3

-----   
Data End.

This is the last non-blank line.
 
  
  

5.1. sedを使用する

sedコマンドを使用して問題を解決する方法はいくつかあります。 sedアドレス範囲を使用した2つのアプローチを見てみましょう。

最初の解決策は、先頭の空白行の部分に焦点を当てています。

$ sed '1,/\S/{/\S/!d}' with_blank.txt

何が起こっているのかを理解しましょう:

  • 1、/ \ S/はアドレス範囲です。 選択は、最初の行から(包括的に)最初の非空白行まで開始されます
  • {/ \ S /!d} は、上記の範囲の各行に適用するアクションです。 !d は私たちにとって目新しいものではありません。ここでも使用して、空白以外の行を範囲内に保ち、残りを削除します。

!d アクションを最初の非空白行からファイルの終わりまでの範囲に適用して、問題を解決することもできます。

$ sed '/\S/,$!d' with_blank.txt      

5.2. awkを使用する

まず、単純なawkソリューションがどのように見えるかを見てみましょう。

$ awk '/\S/{p=1}p' with_blank.txt

awkワンライナーには2つの部分があります。

最初の部分は/\ S / {p = 1} — i faレコードは空白以外の行であるため、変数 p =1を設定します。

2番目の部分は単純にpです。変数pがゼロ以外の数値を保持している場合、現在の行が出力されます。

awk 、変数が初期化されていない場合、そのデフォルト値は空の文字列または 0。 

したがって、変数 p は、最初の非空白行が来ると0から1に設定され、値はファイル。

このようにして、 awk コマンドは、空白以外の最初の行から入力ファイルの最後まで出力します。

6. 末尾の空白行のみを削除する

通常、テキスト処理ツールはファイルの行をファイルの最初から最後まで順番に処理しますが、すでに処理した行を振り返ることは簡単ではありません。

したがって、この問題に対する私たちの主な課題は、ファイルの最後の非空白行を見つけることです。

実用的なソリューションによって出力される出力は、次のようになります。


    
    
    
This is the first non-blank line.

Some data comes here

-----

1
  2
    3

-----   
Data End.

This is the last non-blank line.

6.1. tacを使用する

tac コマンドは、 GNUCoreutilsパッケージのメンバーです。 これは、デフォルトですべてのLinuxディストリビューションにプリインストールされています。

cat コマンドはファイルを自然な順序で印刷しますが、tacコマンドはファイルを逆の順序で印刷します。 (taccatのスペルが逆になっていることに注意してください!)

例はその能力を明確に示しています:

$ cat file
1
2
3
4
5

$ tac file
5
4
3
2
1

tac コマンドを2回使用することで、末尾の空白行を削除するという問題を解決できます。

tac input | <COMMAND TO REMOVE LEADING BLANK LINES> | tac

例えば:

$ tac with_blank.txt | sed '/\S/,$!d' | tac

tacコマンドは確かに問題を単純化します。 ただし、 3つのプロセスを開始し、入力ファイルの内容を3回処理する必要があります

特に巨大な入力ファイルを処理する必要がある場合は、これが問題になることがあります。

6.2. sedを使用する

sed ソリューションは、 tacを使用したソリューションほど単純ではありません。ただし、単一のプロセスを開始し、は入力ファイルを1回だけ読み取ります

$ sed ':a; /^[[:space:]]*$/ { $d; N; ba; }' with_blank.txt

それでは、最初にsedワンライナーのコードを理解しましょう。

  • :a; a」というラベルを作成します
  • / ^ [[:space:]] * $/{アクション}–現在のパターンスペースが空白行パターンと一致する場合、次のアクションが実行されます
  • $ d; –現在の行が入力ファイルの最後の行である場合にのみパターンスペースをスキップします
  • N; –入力ファイルから次の行を読み取り、パターンスペースに追加します
  • ba; –ラベルaへの分岐

この巧妙なソリューションがどのように機能するかを説明しましょう。

空白行が読み取られると、このワンライナーは、ラベル a。に再帰的に分岐することにより、後続の行をパターンスペースに追加します。

空白クラス[[:space:]] には改行が含まれているため、/ ^ [[:space:]] *$/は複数の空白行に一致します。 

ただし、非ブランクが読み取られてパターンスペースに追加されると、パターンスペースの文字列はパターン / ^ [[:space:]] * $/と一致しなくなります。 したがって、再帰を中断し、パターンスペースを印刷してから、クリアします。

入力ファイルの末尾に空白行がある場合、それらはすべてパターンスペースにあり、ファイルの最後の行はパターンスペースを $dスキップします。 したがって、連続する末尾の空白行は出力に含まれません。

6.3. awkを使用する

awk コマンドを使用して、ファイルの最後の非空白行を識別する2つの方法があります。

最初のアプローチを見てみましょう。

$ awk '{a[NR]=$0; if(/\S/)mark=NR} END{for(i=1;i<=mark;i++)print a[i]}' with_blank.txt

上記のawkコードは、入力ファイルを1回だけ読み取ります。 それを分解しましょう:

  • 各行を読み取って配列に保存します: a []
  • markという変数に空白以外の最後の行番号を保存します
  • すべての行を読み取った後、配列をもう一度調べて、markに保存した行まで行を印刷します。

別の方法として、入力ファイルを2回読み取ることで問題を解決することもできます。

$ awk 'NR==FNR && /\S/{mark=NR; next} FNR<=mark' with_blank.txt with_blank.txt

最初の読み取りでは、最後の非空白行の行番号が検出され、markという変数に保存されます。

次に、行番号( NR )がマーク以下の場合、2回目の読み取りで各行が出力されます。

7. 結論

Linuxでテキストファイルを処理する場合、ファイルから空白行を削除するのが一般的な操作です。

この記事では、3つの異なる空白行の削除シナリオについて説明しました。

  • すべての空白行
  • 先頭の空白行のみ
  • 末尾の空白行のみ

これらの3つのシナリオは、ほとんどのユースケースをカバーします。 解決策を理解していれば、空白行を削除することは私たちにとって難しいことではありません。