EOFで複数の改行を削除する方法は?


25

1つ以上の改行で終わるファイルがあり、1つの改行のみで終わる必要があります。Bash / Unix / GNUツールを使用してこれを行うにはどうすればよいですか?

不良ファイルの例:

1\n
\n
2\n
\n
\n
3\n
\n
\n
\n

修正されたファイルの例:

1\n
\n
2\n
\n
\n
3\n

言い換えると、EOFとファイルの最後の非改行文字との間に改行が1つだけあるはずです。

参照実装

ファイルの内容を読み取り、最後にさらに2つの改行がなくなるまで1つの改行を切り取り、書き戻します。

#! /bin/python

import sys

with open(sys.argv[1]) as infile:
    lines = infile.read()

while lines.endswith("\n\n"):
    lines = lines[:-1]

with open(sys.argv[2], 'w') as outfile:
    for line in lines:
        outfile.write(line)

明確化:もちろん、それがよりエレガントであれば、パイピングは許可されます。

回答:


16
awk '/^$/ {nlstack=nlstack "\n";next;} {printf "%s",nlstack; nlstack=""; print;}' file

2
+1:awkのソリューションは(ほぼ)常にエレガントで読みやすい!
オリビエデュラック

@OlivierDulac確かに。私が見たときsedの提案を私はOMG ...と思った
Hauke Laging

1
これは、Homebrewから入手可能な最新のawkを使用するOSX Mavericksでは機能しません。でエラーが発生しawk: illegal statementます。 brew install mawkそしてコマンドをmawk動作するように変更します。
tjmcewan

@noname私も質問を理解していません...
Hauke Laging

スクリプトが機能しないawkは、ひどく壊れたawkです。これを使用するのをやめて、新しいawkを取得してください。
エドモートン

21

sedのに有用な1行スクリプト

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

4
おかげで、私は複数のファイルのための場所でそれを行うには、次を使用: find . -type f -name '*.js' -exec sed --in-place -e :a -e '/^\n*$/{$d;N;};/\n$/ba' {} \;
jakub.g

@ jakub.gが適切で再帰的であることがまさに必要でした。ありがとうございました。
バトルビュータス

あなたはOS X上で、このようなコマンドを呼び出すことができます@ jakub.gから優れたコメントに追加するには:find . -type f -name '*.js' -exec sed -i '' -e :a -e '/^\n*$/{$d;N;};/\n$/ba' {} \;
davejagoda

18

より適切なツールsedおよびawkを使用して既に回答が得られているため、$(< file)末尾の空白行を削除するという事実を活用できます。

a=$(<file); printf '%s\n' "$a" > file

その安価なハックは、スペースやその他の非印刷文字を含む可能性のある末尾の空白行を削除するためには機能せず、末尾の空行を削除するためだけです。ファイルにnullバイトが含まれている場合も機能しません。

bashおよびzsh以外のシェルでは、の$(cat file)代わりに使用します$(<file)


+1は、バグのように見えるものを指摘するために使用します。なぜ末尾の改行を破棄するのですか?(それを指摘してくれてありがとうございます!)
オリビエデュラック

2
@OlivierDulac $()は末尾の改行を破棄します。それは設計上の決定です。これにより、他の文字列への統合が容易にecho "On $(date ...) we will meet."なると思います:ほぼすべてのシェルコマンドが最後に出力する改行文字は悪意があるでしょう。
ハウケレイジング

@HaukeLaging:良い点、それはおそらくその動作のソースです
オリヴィエデュラック

空のファイルに「\ n」が追加されないようにする特別なケースを追加しました[[ $a == '' ]] || printf '%s\n' "$a" >"$file"
davidchambers

ファイルの先頭から複数の改行を削除するには、プロセスにtacを挿入します(Macではgnu coreutilsを使用しているため、gtacを使用します):a=$(gtac file.txt); printf '%s\n' "$a" | gtac > file.txt
r_alex_hall


4

この質問はでタグ付けされていedますが、誰も解決策を提案していません。

以下がその1つです。

ed -s file <<'ED_END'
a

.
?^..*?+1,.d
w
ED_END

または、同等に、

printf '%s\n' a '' . '?^..*?+1,.d' w | ed -s file

ed デフォルトでは、起動時に編集バッファの最後の行に配置されます。

最初のコマンド(a)は、バッファーの最後に空の行を追加します(編集スクリプトの空の行はこの行で、ドット(.)はコマンドモードに戻るためのものです)。

2番目のコマンド(?)は、何か(空白文字も含む)を含む最も近い前の行を探し、次の行からバッファーの最後までをすべて削除します。

3番目のコマンド(w)は、ファイルをディスクに書き戻します。

追加された空行は、元のファイルの最後に空行がない場合に、ファイルの残りが削除されないように保護します。


3

以下、一度に複数行をメモリに読み込む必要のない Perlソリューションです。

my $n = 0;
while (<>) {
    if (/./) {
        print "\n" x $n, $_;
        $n = 0;
    } else {
        $n++;
    }
}

または、ワンライナーとして:

perl -ne 'if (/./) { print "\n" x $n, $_; $n = 0 } else { $n++ }'

これは、ファイルを一度に1行ずつ読み取り、各行をチェックして、改行以外の文字が含まれているかどうかを確認します。そうでない場合は、カウンターをインクリメントします。存在する場合は、カウンターが示す改行の数を出力し、その後に行自体が続き、カウンターをリセットします。

技術的には、メモリ内の単一行をバッファリングすることさえ不要です。固定長のチャンクでファイルを読み取り、ステートマシンを使用して文字ごとに処理することにより、一定量のメモリを使用してこの問題を解決することができます。ただし、一般的なユースケースでは不必要に複雑になると思われます。


1

ファイルがメモリに丸intoみできるほど小さい場合は、これを使用できます

perl -e 'local($/);$f=<>; $f=~s/\n*$/\n/;print $f;' file

0

Pythonでは(あなたが望むものではないことを知っていますが、最適化されており、bashバージョンの前奏曲であるため、はるかに優れています)ファイルを書き換えずに、すべてのファイルを読むことなく(ファイルが非常に大きい):

#!/bin/python
import sys
infile = open(sys.argv[1], 'r+')
infile.seek(-1, 2)
while infile.read(1) == '\n':
  infile.seek(-2, 1)
infile.seek(1, 1)
infile.truncate()
infile.close()

EOL文字が「\ n」でないファイルでは機能しないことに注意してください。


0

Pythonアルゴリズムを実装するbashバージョンですが、多くのプロセスが必要になるため効率が低下します。

#!/bin/bash
n=1
while test "$(tail -n $n "$1")" == ""; do
  ((n++))
done
((n--))
truncate -s $(($(stat -c "%s" "$1") - $n)) "$1"

0

これは簡単に入力でき、sedを知っていれば覚えやすい:

tac < file | sed '/[^[:blank:]]/,$!d' | tac

sedスクリプトを使用して、上記のAlexeyが参照するsed、およびtac(reverse cat)の便利な1行スクリプトから先頭の空白行を削除します。

18MB、64,000行のファイルでの簡単なテストでは、Alexeyのアプローチは高速でした(0.036秒と0.046秒)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.