bashで末尾の改行を削除するにはどうすればよいですか?


10

Perlのように動作するものを探していchompます。私は単に入力を表示するコマンドを探しています。それが改行の場合は最後の文字を差し引いたものです。

$ printf "one\ntwo\n" | COMMAND_IM_LOOKING_FOR ; echo " done"
one
two done
$ printf "one\ntwo" | COMMAND_IM_LOOKING_FOR ; echo " done"
one
two done

(BashとZshでコマンドを置換すると、末尾の改行がすべて削除されますが、末尾の改行を最大1つ削除するものを探しています。)

回答:


9

これはうまくいくはずです:

printf "one\ntwo\n" | awk 'NR>1{print PREV} {PREV=$0} END{printf("%s",$0)}' ; echo " done"

スクリプトは常に現在ではなく前の行を出力し、最後の行は別の方法で処理されます。

詳細は次のとおりです。

  1. NR>1{print PREV} 前の行を印刷します(初回を除く)。
  2. {PREV=$0}現在の行をPREV変数に格納します。
  3. END{printf("%s",$0)} 最後に、改行なしで最後の行を印刷します。

また、これにより、末尾の空行が最大で1行削除されることに注意してください(の削除はサポートされていません"one\ntwo\n\n\n")。


15

perlなしで使用できますchomp

$ printf "one\ntwo\n" | perl -0 -pe 's/\n\Z//'; echo " done"
one
two done

$ printf "one\ntwo" | perl -0 -pe 's/\n\Z//'; echo " done"
one
two done

しかし、なぜchompそれ自体を使用しないのですか?

$ printf "one\ntwo\n" | perl -pe 'chomp if eof'; echo " done"

4

とまったく同じものが必要な場合chomp、私の頭に浮かぶ最初の方法は、LatinSuDがすでに投稿しawkソリューションです。実装されないが、よく使用されるchompいくつかの一般的なタスクを実装する他のいくつかのメソッドを追加しますchomp

変数にテキストを入れると、最後の改行がすべて削除されます。したがって、これらすべてのコマンドは同じ単一行の出力を生成します。

echo "$(printf 'one\ntwo') done"
echo "$(printf 'one\ntwo\n') done"
echo "$(printf 'one\ntwo\n\n') done"
echo "$(printf 'one\ntwo\n\n\n\n\n\n\n\n\n\n') done"

ファイルまたはコマンドの出力の最後の行にテキストを追加する場合は、sed便利です。GNU sedおよびその他の最新の実装では、入力が改行で終わっていなくても、これは機能します¹; ただし、改行がまだない場合、これは改行を追加しません。

sed '$ s/$/ done/'

¹ ただし、これはすべてのsed実装では機能しません。sedはテキスト処理ツールであり、空ではなく、改行文字で終わらないファイルはテキストファイルではありません。


末尾の改行は最大で1つしか削除さchompchompないため、これはとまったく同じではありません。
Flimm 14

@Flimmはい、最も明白な正確な同等物chompは、LatinSuDがすでに投稿したawkソリューションです。しかし、多くの場合chomp、これは仕事をするための単なるツールであり、いくつかの一般的なタスクを実行する方法を提供します。これを明確にするために回答を更新しましょう。
Gilles「SO-邪悪なことをやめなさい」14/07/08

1

別のperlアプローチ。これは入力全体をメモリに読み込むので、大量のデータにはお勧めできません(cuonglmまたはそのawkアプローチを使用してください)。

$ printf "one\ntwo\n" | perl -0777pe 's/\n$//'; echo " done"
one
two done

ありがとう、@StéphaneChazelas、修正済み。何らかの理由で、このスイッチは常に私を混乱させます
terdon

0

私はこれをgithubレポからどこかに引っ掛けましたが、どこにあるのかわかりません

delete-trailing-blank-lines-sed

#!/bin/bash
#
# Delete all trailing blank lines.
# From http://sed.sourceforge.net/sed1line.txt
#
# Version: 1.3.0
# Created: 2011-01-02
# Updated: 2015-01-25
# Contact: Joel Parker Henderson (joel@joelparkerhenderson.com)
# License: GPL
##
set -euf
sed -e :a -e '/^\n*$/{$d;N;ba' -e '}'

0

概要

改行なしで行を印刷し、印刷する別の行がある場合にのみ改行を追加します。

$ printf 'one\ntwo\n' | 

     awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }';   echo " done"

one
two done

その他の解決策

ファイルで作業している場合は、ファイルから1文字を切り捨てることができます(改行で終わっている場合)。

removeTrailNewline(){[[$(tail -c 1 "$ 1")]] || truncate -s-1 "$ 1"; }

ファイルから1文字だけtruncateを読み取り、ファイル全体を読み取らずに直接削除する()必要があるため、これは高速なソリューションです。

ただし、stdin(ストリーム)からのデータを処理している間は、データをすべて読み取る必要があります。そして、それを読むとすぐに「消費」されます。バックトラックなし(切り捨てと同様)。ストリームの終わりを見つけるには、ストリームの終わりまで読み取る必要があります。その時点では、入力ストリームに戻る方法はありません。データはすでに「消費」されています。これは、ストリームの最後に一致するまでデータを何らかの形式のバッファに格納してから、バッファ内のデータで何かを行う必要があることを意味します。

最も明白な解決策は、ストリームをファイルに変換し、そのファイルを処理することです。しかし、質問はストリームのある種のフィルターを要求します。追加ファイルの使用についてではありません。

変数

素朴な解決策は、入力全体を変数に取り込むことです。

FilterOne(){ filecontents=$(cat; echo "x");        # capture the whole input
             filecontents=${filecontents%x};       # Remove the "x" added above.
             nl=$'\n';                             # use a variable for newline.
             printf '%s' "${filecontents%"$nl"}";  # Remove newline (if it exists).
       }

printf 'one\ntwo'     | FilterOne ; echo 1done
printf 'one\ntwo\n'   | FilterOne ; echo 2done
printf 'one\ntwo\n\n' | FilterOne ; echo 3done

記憶

sedを使用すると、ファイル全体をメモリに読み込むことができます。sedでは、最後の行の末尾の改行を回避することは不可能です。GNU sedは、末尾の改行の印刷を回避する可能性がありますが、ソースファイルにすでにそれがない場合のみです。つまり、単純なsedは役に立ちません。

-zオプション付きのGNU awkを除いて:

sed -z 's/\(.*\)\n$/\1/'

awk(任意のawk)を使用して、ストリーム全体を丸呑みにしprintfます。

awk '    { content = content $0 RS } 
     END { gsub( "\n$", "", content ); printf( "%s", content ) }
    '

ファイル全体をメモリにロードすることは良い考えではないかもしれません、それは多くのメモリを消費するかもしれません。

メモリ内の2行

awkでは、前の行を変数に格納して現在の行を出力することにより、ループごとに2行を処理できます。

awk 'NR>1{print previous} {previous=$0} END {printf("%s",$0)}'

直接処理

しかし、私たちはもっとうまくやることができました。

現在の行を改行なしで印刷し、次の行が存在する場合にのみ改行を印刷する場合、一度に1行ずつ処理し、最後の行には後続の改行がありません

awk 'NR == 1 {printf( "%s"、$ 0);次へ}; {printf( "\ n%s"、$ 0)} '

または、他の方法で書かれています:

awk 'NR>1{ print "" }; { printf( "%s", $0 ) }'

または:

awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'

そう:

$ printf 'one\ntwo\n' | awk '{ printf( "%s%s" , NR>1?"\n":"" , $0 ) }'; echo " done"
one
two done
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.