空の行をすべてファイルから削除したいのですが、それらがファイルの末尾または先頭にある場合(つまり、その前に空でない行がない場合、先頭にある場合、および最後には空行がありません)
これは、PerlやRubyなどの完全な機能を備えたスクリプト言語の外では可能ですか?可能であれば、sed
またはawk
を使用することをお勧めします。基本的には、軽量で広く利用可能なUNIX-yツール、特に私がすぐに詳しく知ることができるツール(Perl、したがって含まれていません)は問題ありません。
# 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
ですから、これについて@dogbaneの回答の一部を借ります。先頭の空白行を削除するsed
行が非常に短いためです...
tac
はcoreutilsの一部 で、ファイルを元に戻します。だからそれを2回行います:
tac file | sed -e '/./,$!d' | tac | sed -e '/./,$!d'
それは確かに最も効率的ではありませんが、あなたがneed効率でない限り、私はこれまでのすべてのものより読みやすいと思います。
これは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:]]*$/
)、空白のみの内部行は、完全に空になるように切り捨てられます。
改造された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$
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
別の答え で述べたように、 tac
はcoreutils の一部であり、ファイルを逆にします。 コマンド置換で後続の新しい行が削除されるという事実 と2回行うという考えを組み合わせると、
echo "$(echo "$(tac "$filename")" | tac)"
これはsed
に依存しません。 echo -n
を使用すると、残りの末尾の改行を削除できます。
末尾の改行ストリップ(「白い」文字を含む)の効率的な非再帰バージョンのために、この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'
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
その醜さを避けるために「本当の」プログラミング言語を学ぶことは間違いなく価値があります!
bash
の使用
$ filecontent=$(<file)
$ echo "${filecontent/$'\n'}"
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
Perl -0pe 's/^\n+|\n+(\n)$/\1/gs'
このAWKスクリプトは、トリックを実行します。
BEGIN {
ne=0;
}
/^[[:space:]]*$/ {
ne++;
}
/[^[:space:]]+/ {
for(i=0; i < ne; i++)
print "";
ne=0;
print
}
アイデアは単純です。空の行はすぐにエコーされません。代わりに、空ではない行が表示されるまで待機します。それから、最初に、その前に見られたのと同じだけの空の行をエコーし、次に新しい空でない行をエコーします。
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'
_を使用してください)。@dogbaneは、先頭の空行を削除するための素晴らしいシンプルな答えを持っています。次に、末尾の行のみを削除する単純なawkコマンドを示します。これを@dogbaneのsedコマンドで使用して、先頭と末尾の両方の空白を削除します。
awk '{ LINES=LINES $0 "\n"; } /./ { printf "%s", LINES; LINES=""; }'
これは操作が非常に簡単です。
したがって、バッファーに入れられて表示されないのは、末尾の空白だけです。
改行を使用してバッファ内の行をすでに分離しているため、改行の自動追加を回避するために、printではなくprintfを使用しました。