web-dev-qa-db-ja.com

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

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)

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

26
Bengt
awk '/^$/ {nlstack=nlstack "\n";next;} {printf "%s",nlstack; nlstack=""; print;}' file
17
Hauke Laging

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

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

Sedとawkのより適切なツールですでに回答を得ているので、 $(< file)が末尾の空白行を削除するという事実を利用できます。

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

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

Bashとzsh以外のシェルでは、$(cat file)ではなく$(<file)を使用してください。

18
llua

このトリックは catprintf で使用できます。

$ printf '%s\n' "`cat file`"

例えば

$ printf '%s\n' "`cat ifile`" > ofile
$ cat -e ofile
1$
$
2$
$
$
3$

$は行の終わりを示します。

参考文献

5
slm

この質問には ed のタグが付けられていますが、edソリューションを提案した人はいません。

ここに一つあります:

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)は、ファイルをディスクに書き戻します。

追加された空の行は、元のファイルの最後に空の行がない場合に、残りのファイルが削除されるのを防ぎます。

4
Kusalananda

しないが一度に複数行をメモリに読み込む必要がある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行をメモリにバッファリングすることも不要です。固定長のチャンクでファイルを読み取り、ステートマシンを使用して文字ごとに処理することにより、一定量のメモリを使用してこの問題を解決することができます。しかし、それは典型的なユースケースでは不必要に複雑になると思います。

3
Ilmari Karonen

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

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

これはすばやく入力でき、sedを知っている場合は覚えやすいです。

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

これは、sedスクリプトを使用して、上記のAlexeyによって参照される sedに役立つ1行スクリプト からleading空白行を削除し、tac(reverse cat)を実行します。

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

0
freeB

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
jfg956

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
jfg956