web-dev-qa-db-ja.com

sed、awk、tr、およびその友達による末尾の改行の削除

空の行をすべてファイルから削除したいのですが、それらがファイルの末尾または先頭にある場合(つまり、その前に空でない行がない場合、先頭にある場合、および最後には空行がありません)

これは、PerlやRubyなどの完全な機能を備えたスクリプト言語の外では可能ですか?可能であれば、sedまたはawkを使用することをお勧めします。基本的には、軽量で広く利用可能なUNIX-yツール、特に私がすぐに詳しく知ることができるツール(Perl、したがって含まれていません)は問題ありません。

39
ELLIOTTCABLE

からsedの便利な1行のスクリプト

# Delete all leading blank lines at top of file (only).
sed '/./,$!d' file

# Delete all trailing blank lines at end of file (only).
sed -e :a -e '/^\n*$/{$d;N;};/\n$/ba' file

したがって、ファイルから先頭と末尾の両方の空白行を削除するには、上記のコマンドを次のように組み合わせることができます。

sed -e :a -e '/./,$!d;/^\n*$/{$d;N;};/\n$/ba' file
54
dogbane

ですから、これについて@dogbaneの回答の一部を借ります。先頭の空白行を削除するsed行が非常に短いためです...

tacはcoreutilsの一部 で、ファイルを元に戻します。だからそれを2回行います:

tac file | sed -e '/./,$!d' | tac | sed -e '/./,$!d'

それは確かに最も効率的ではありませんが、あなたがneed効率でない限り、私はこれまでのすべてのものより読みやすいと思います。

11
Izkata

これはawkのワンパスソリューションです。空でない行が見つかるまで印刷を開始せず、空の行が見つかると、次の空でない行までそれを記憶します。

awk '
    /[[:graph:]]/ {
        # a non-empty line
        # set the flag to begin printing lines
        p=1      
        # print the accumulated "interior" empty lines 
        for (i=1; i<=n; i++) print ""
        n=0
        # then print this line
        print
    }
    p && /^[[:space:]]*$/ {
        # a potentially "interior" empty line. remember it.
        n++
    }
' filename

空の/空でない行を考慮するために私が使用しているメカニズムのために、([[:graph:]]および/^[[:space:]]*$/)、空白のみの内部行は、完全に空になるように切り捨てられます。

3
glenn jackman

改造されたsedバージョンは次のとおりです。これは、スペースとタブのみを含む行を「空」と見なします。

sed -e :a -e '/[^[:blank:]]/,$!d; /^[[:space:]]*$/{ $d; N; ba' -e '}'

これは基本的には受け入れられた回答バージョン(BryanHコメントを考慮)ですが、最初のコマンドのドット.[^[:blank:]](空白ではないもの)に変更され、2番目のコマンドアドレス内の\nは改行を許可し、タブをスペースで区切るために[[:space:]]に変更されました。

POSIXクラスを使用しない代替バージョンですが、sedは\t内への\nおよび[…]の挿入をサポートする必要があります。 GNU sedにはありますが、BSD sedにはありません。

sed -e :a -e '/[^\t ]/,$!d; /^[\n\t ]*$/{ $d; N; ba' -e '}'

テスト:

Prompt$ printf '\n \t \n\nfoo\n\nfoo\n\n \t \n\n' 



foo

foo



Prompt$ printf '\n \t \n\nfoo\n\nfoo\n\n \t \n\n' | sed -n l
$
 \t $
$
foo$
$
foo$
$
 \t $
$
Prompt$ printf '\n \t \n\nfoo\n\nfoo\n\n \t \n\n' | sed -e :a -e '/[^[:blank:]]/,$!d; /^[[:space:]]*$/{ $d; N; ba' -e '}'
foo

foo
Prompt$
3
Aurelio Jargas

awkを使用:

awk '{a[NR]=$0;if($0 && !s)s=NR;}
    END{e=NR;
        for(i=NR;i>1;i--) 
            if(a[i]){ e=i; break; } 
        for(i=s;i<=e;i++)
            print a[i];}' yourFile
2
Kent

別の答え で述べたように、 tacはcoreutils の一部であり、ファイルを逆にします。 コマンド置換で後続の新しい行が削除されるという事実 と2回行うという考えを組み合わせると、

echo "$(echo "$(tac "$filename")" | tac)"

これはsedに依存しません。 echo -nを使用すると、残りの末尾の改行を削除できます。

2
Jason Gross

末尾の改行ストリップ(「白い」文字を含む)の効率的な非再帰バージョンのために、このsedスクリプトを開発しました。

sed -n '/^[[:space:]]*$/ !{x;/\n/{s/^\n//;p;s/.*//;};x;p;}; /^[[:space:]]*$/H'

ホールドバッファーを使用してすべての空白行を格納し、空白以外の行を検出した後にのみそれらを出力します。改行だけが必要な場合は、2つを削除するだけで十分です[[:space:]]*パーツ:

sed -n '/^$/ !{x;/\n/{s/^\n//;p;s/.*//;};x;p;}; /^$/H'

よく知られている再帰スクリプトとの簡単なパフォーマンス比較を試しました

sed -e :a -e '/^\n*$/{$d;N;};/\n$/ba'

ランダムなbase64テキストの周りに1MBのランダムな空白行がある3MBのファイル。

shuf -re 1 2 3 | tr -d "\n" | tr 123 " \t\n" | dd bs=1 count=1M > bigfile
base64 </dev/urandom | dd bs=1 count=1M >> bigfile
shuf -re 1 2 3 | tr -d "\n" | tr 123 " \t\n" | dd bs=1 count=1M >> bigfile

ストリーミングスクリプトは完了するまでに約0.5秒かかり、15分後に再帰は終了しませんでした。勝つ:)

答えを完全にするために、sedスクリプトを削除する先頭行は既に正常にストリーミングされています。あなたに最も適したものを使用してください。

sed '/[^[:blank:]]/,$!d'
sed '/./,$!d'
1
tlwhitec

Bashでは、cat、wc、grep、sed、tail、headを使用します。

# number of first line that contains non-empty character
i=`grep -n "^[^\B*]" <your_file> | sed -e 's/:.*//' | head -1`
# number of hte last one
j=`grep -n "^[^\B*]" <your_file> | sed -e 's/:.*//' | tail -1`
# overall number of lines:
k=`cat <your_file> | wc -l`
# how much empty lines at the end of file we have?
m=$(($k-$j))
# let strip last m lines!
cat <your_file> | head -n-$m
# now we have to strip first i lines and we are done 8-)
cat <your_file> | tail -n+$i

その醜さを避けるために「本当の」プログラミング言語を学ぶことは間違いなく価値があります!

1

bashの使用

$ filecontent=$(<file)
$ echo "${filecontent/$'\n'}"
1
bash-o-logist

gawk v4.1 +の別のバリアントを紹介したい

result=($(gawk '
    BEGIN {
        lines_count         = 0;
        empty_lines_in_head = 0;
        empty_lines_in_tail = 0;
    }
    /[^[:space:]]/ {
        found_not_empty_line = 1;
        empty_lines_in_tail  = 0;
    }
    /^[[:space:]]*?$/ {
        if ( found_not_empty_line ) {
            empty_lines_in_tail ++;
        } else {
            empty_lines_in_head ++;
        }
    }
    {
        lines_count ++;
    }
    END {
        print (empty_lines_in_head " " empty_lines_in_tail " " lines_count);
    }
' "$file"))

empty_lines_in_head=${result[0]}
empty_lines_in_tail=${result[1]}
lines_count=${result[2]}

if [ $empty_lines_in_head -gt 0 ] || [ $empty_lines_in_tail -gt 0 ]; then
    echo "Removing whitespace from \"$file\""
    eval "gawk -i inplace '
        {
            if ( NR > $empty_lines_in_head && NR <= $(($lines_count - $empty_lines_in_tail)) ) {
                print
            }
        }
    ' \"$file\""
fi
0
puchu
Perl -0pe 's/^\n+|\n+(\n)$/\1/gs'
0
Jan Kyu Peblik

このAWKスクリプトは、トリックを実行します。

BEGIN {
    ne=0;
}

/^[[:space:]]*$/ {
    ne++;
}

/[^[:space:]]+/ {
    for(i=0; i < ne; i++)
        print "";
    ne=0;
    print
}

アイデアは単純です。空の行はすぐにエコーされません。代わりに、空ではない行が表示されるまで待機します。それから、最初に、その前に見られたのと同じだけの空の行をエコーし​​、次に新しい空でない行をエコーし​​ます。

0
Adi Degani

A bash solution

注:有用なのはファイルが十分に小さい場合一度にメモリに読み込まれることだけです。

_[[ $(<file) =~ ^$'\n'*(.*)$ ]] && echo "${BASH_REMATCH[1]}"
_
  • $(<file)は、ファイル全体を読み取り、末尾改行をトリムします。これは、コマンド置換($(....)暗黙的にが行うためです。
  • _=~_はbashの正規表現一致演算子であり、=~ ^$'\n'*(.*)$は任意で先頭の改行に(貪欲に)一致し、その後のすべてをキャプチャします。エスケープシーケンス_$'\n'_はサポートされていないため、 ANSI C quoting を使用してリテラル改行を挿入する、混乱を招く可能性のある_\n_に注意してください。
  • この特定の正規表現常にが一致するため、_&&_の後のコマンドは常に実行されることに注意してください。
  • 特別な配列変数_BASH_REMATCH_ rematchには、最新の正規表現一致の結果が含まれ、配列要素_[1]_には、(最初​​かつ唯一の)括弧で囲まれた部分式(キャプチャグループ)がキャプチャしたものが含まれます。先頭の改行が削除されました。最終的には、_${BASH_REMATCH[1]}_には、先頭と末尾の両方の改行が削除された入力ファイルの内容が含まれます。
  • echoを使用して印刷すると、末尾に改行が1つ追加されることに注意してください。それを避けたい場合は、代わりに_echo -n_を使用してください(またはより移植性の高い_printf '%s'_を使用してください)。
0
mklement0

@dogbaneは、先頭の空行を削除するための素晴らしいシンプルな答えを持っています。次に、末尾の行のみを削除する単純なawkコマンドを示します。これを@dogbaneのsedコマンドで使用して、先頭と末尾の両方の空白を削除します。

awk '{ LINES=LINES $0 "\n"; } /./ { printf "%s", LINES; LINES=""; }'

これは操作が非常に簡単です。

  • 読みながら、すべての行をバッファに追加します。
  • 文字を含むすべての行について、バッファの内容を出力してから消去します。

したがって、バッファーに入れられて表示されないのは、末尾の空白だけです。

改行を使用してバッファ内の行をすでに分離しているため、改行の自動追加を回避するために、printではなくprintfを使用しました。

0
Andy Mortimer