改行がファイルの最後の文字である場合、それを削除するにはどうすればよいですか?


162

ファイル内の最後の文字である場合、最後の改行を削除したいファイルがいくつかあります。 od -c私が実行したコマンドがファイルに最後の新しい行を書き込むことを示しています:

0013600   n   t  >  \n

私はsedを使っていくつかのトリックを試しましたが、考えられる最善の方法はそのトリックを行わないことです。

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

これを行う方法はありますか?


4
改行は、UNIXの改行の1文字のみです。DOS改行は2文字です。もちろん、リテラル「\ n」は2文字です。あなたは実際に何を探していますか?
追って通知があるまで一時停止。

3
表現はかもしれませんが\n、Linuxでは1文字です
パビウム2009年

10
なぜこれをしたいのか、詳しく説明していただけますか?テキストファイルは、完全に空でない限り、行末で終了すること想定されています。あなたがそのような切り捨てられたファイルを持ちたいと思うのは奇妙に思えますか?
Thomas Padron-McCarthy

実行するための通常の理由何かこのようなCSVファイルの最後の行から末尾のカンマを削除することです。Sedはうまく機能しますが、改行は別の方法で処理する必要があります。
パビウム2009年

9
@ ThomasPadron-McCarthy「コンピューティングでは、あらゆる正当な理由で何かをする必要があります。それを行わない正当な理由があり、その逆もあります。」-イエス-「それをしてはいけない」は質問に関係なく恐ろしい答えです。正しい形式は、[方法]ですが、[理由よくないかもしれません]です。#sacrilege
Cory Mawhorter

回答:


223
perl -pe 'chomp if eof' filename >filename2

または、ファイルをその場で編集するには:

perl -pi -e 'chomp if eof' filename

[編集者注:-pi -eはもともとでした-pieが、いくつかのコメンターによって指摘され、@ hvdによって説明されたように、後者は機能しません。]

これは、私が見たawkウェブサイトで「perl冒涜」と説明されていました。

しかし、テストでは、うまくいきました。


11
を使用すると、安全性を高めることができchompます。そして、それはファイルを丸呑みするのに勝ります。
SinanÜnür09年

6
冒とくですが、非常にうまく機能します。perl -i -pe 'chomp if eof'ファイル名。ありがとうございました。
トッドパートリッジ 'Gen2ly'

13
冒とくと異端についての面白いことは、それが正しいのでそれが通常嫌われているということです。:)
エーテル、

8
小さな修正:を使用してperl -pi -e 'chomp if eof' filename、一時ファイルを作成する代わりにインプレースでファイルを編集することができます
Romuald Brunet

7
perl -pie 'chomp if eof' filename-> Perlスクリプト "chomp if eof"を開けません:そのようなファイルやディレクトリはありません。perl -pi -e 'chomp if eof' filename- >作品
SEは悪であるためaditsuはやめ

56

シェルコマンドの置換によって末尾の改行文字が削除されるという事実を利用できます

bash、ksh、zshで機能する単純な形式:

printf %s "$(< in.txt)" > out.txt

ポータブル(POSIX準拠)の代替(わずかに効率が悪い):

printf %s "$(cat in.txt)" > out.txt

注意:

  • 複数の改行文字でin.txt終了している場合、コマンド置換はそれらすべてを削除ます -ありがとう、@ Sparhawk。(末尾の改行以外の空白文字は削除されません。)
  • この方法では、入力ファイル全体がメモリ読み込まれるため、ファイルが小さい場合にのみお勧めします。
  • printf %s何も改行が出力に追加されていないことを保証します(それは非標準のPOSIX準拠の代替であるecho -n;参照http://pubs.opengroup.org/onlinepubs/009696799/utilities/echo.htmlおよびHTTPSを://unix.stackexchange。 com / a / 65819

他の回答へのガイド

  • Perlが利用可能な場合は、受け入れられた答えを探してください。それはシンプルでメモリ効率が良いです(入力ファイル全体を一度に読み取らない)。

  • それ以外の場合は、ghostdog74のAwk回答を検討してください。これはあいまいですが、メモリ効率も高くなります。より読みやすいと同等(POSIX準拠)は、次のとおりです。

    • awk 'NR > 1 { print prev } { prev=$0 } END { ORS=""; print }' in.txt
    • 印刷は1行遅れるので、最終行をENDブロックで処理できます。\n出力レコードの区切り文字(OFS)を空の文字列ます。
  • (元のファイルを置き換える一時ファイルを作成するのとは対照的に)インプレース本当に編集する冗長で高速かつ堅牢なソリューションが必要な場合は、jrockwayのPerlスクリプトを検討してください


3
ファイルの終わりに複数の改行がある場合、このコマンドはそれらすべてを削除します。
スパーホーク2013

47

これはhead、GNU coreutilsから実行できます。ファイルの末尾に関連する引数をサポートしています。したがって、最後のバイトを省略するには、以下を使用します。

head -c -1

最後の改行をテストするには、tailおよびを使用できますwc。次の例では、結果を一時ファイルに保存し、その後元のファイルを上書きします。

if [[ $(tail -c1 file | wc -l) == 1 ]]; then
  head -c -1 file > file.tmp
  mv file.tmp file
fi

spongefrom moreutilsを使用して「インプレース」編集を行うこともできます。

[[ $(tail -c1 file | wc -l) == 1 ]] && head -c -1 file | sponge file

これを.bashrcファイルに詰め込むことで、一般的な再利用可能な関数を作ることもできます:

# Example:  remove-last-newline < multiline.txt
function remove-last-newline(){
    local file=$(mktemp)
    cat > $file
    if [[ $(tail -c1 $file | wc -l) == 1 ]]; then
        head -c -1 $file > $file.tmp
        mv $file.tmp $file
    fi
    cat $file
}

更新

コメントでKarlWilburが言及し、Sorentarの回答で使用されているようにtruncate --size=-1置換できhead -c-1、インプレース編集をサポートします。


3
これまでのすべての最善の解決策。sedやperlのウィザードを使用せずに、実際にすべてのLinuxディストリビューションが備えている簡潔で明確な標準ツールを使用します。
ダッカロン

2
素晴らしい解決策。1つの変更点は、入力ファイルを読み取って別のファイルに書き出し、元のファイルを出力ファイルで置き換えるのではなく、入力ファイルのサイズを変更するだけなので、truncate --size=-1代わりに使用すると思いhead -c -1ます。
Karl Wilbur、

1
head -c -1は、最後の文字が改行かどうかに関係なく削除することに注意してください。そのため、削除する前に最後の文字が改行かどうかを確認する必要があります。
wisbucky

残念ながらMacでは動作しません。BSDバリアントでは機能しないと思います。
エドワードフォーク

16
head -n -1 abc > newfile
tail -n 1 abc | tr -d '\n' >> newfile

編集2:

これは、潜在的に巨大な配列を蓄積しないawkバージョン(修正済み)です。

awk '{if(line)print line; line = $ 0} END {printf $ 0} 'abc


それについて考える良いオリジナルの方法。デニスに感謝します。
トッドパートリッジ 'Gen2ly'

あなたは正しいです。私はあなたのawkバージョンに従います。これには2つのオフセット(および別のテスト)が必要で、1つだけ使用しました。ただし、のprintf代わりに使用できますORS
追って通知があるまで一時停止。

:あなたは、プロセス置換を有するパイプ出力を行うことができますhead -n -1 abc | cat <(tail -n 1 abc | tr -d '\n') | ...
BCoates

2
ヘッドとテールに-nの代わりに-cを使用すると、さらに高速になります。
rudimeier 2013

1
私の場合、head -n -1 abcはファイルの最後の実際の行を削除し、末尾に改行を残しました。head -c -1 abcはうまく機能するようです
ChrisV 14

10

ガウク

   awk '{q=p;p=$0}NR>1{print q}END{ORS = ""; print p}' file

それでも私には多くのキャラクターのように見えます...ゆっくりとそれを学びます:) でも仕事はします。ゴーストドッグに感謝します。
トッドパートリッジ 'Gen2ly'

1
awk '{ prev_line = line; line = $0; } NR > 1 { print prev_line; } END { ORS = ""; print line; }' fileこれは読みやすいはずです。
Yevhen Pavliuk 2013年

どのように:awk 'NR>1 {print p} {p=$0} END {printf $0}' file
Isaac

@sorontarの最初の引数printfフォーマット引数です。したがって、入力ファイルにのような形式指定子として解釈できるものがあった場合、%dエラーが発生します。修正はそれを次のように変更することですprintf "%s" $0
ロビンA.ミード

9

coreutilsからのGNU echoを必要とする単一行ファイルの非常に単純な方法:

/bin/echo -n $(cat $file)

高すぎない(繰り返し)場合、これは適切な方法です。

\n存在する場合、これには問題があります。新しい行に変換されます。
Chris Stryczynski 2017

$(...)引用されている複数行のファイルでも機能するようです
Thor

間違いなくそれを引用する必要があります... /bin/echo -n "$(cat infile)" また、echoos / shellバージョン/ distros全体の最大長またはシェルがどうなるかわかりません(私はこれをググっているだけで、うさぎの穴でした)。小さなファイル以外の場合に実際に移植可能(またはパフォーマンス)であるかどうかはわかりませんが、小さなファイルの場合は素晴らしいです。
マイケル2018年

8

正しく実行したい場合は、次のようなものが必要です。

use autodie qw(open sysseek sysread truncate);

my $file = shift;
open my $fh, '+>>', $file;
my $pos = tell $fh;
sysseek $fh, $pos - 1, 0;
sysread $fh, my $buf, 1 or die 'No data to read?';

if($buf eq "\n"){
    truncate $fh, $pos - 1;
}

読み取りと追加のためにファイルを開きます。追加用に開くとseekは、ファイルの最後まで既に移動していることを意味します。次に、でファイルの最後の数値位置を取得しますtell。その数字を使用して1文字を検索し、その1文字を読み取ります。改行の場合は、ファイルをその改行の前の文字に切り捨てます。それ以外の場合は何もしません。

これは、入力に対して一定の時間と一定のスペースで実行され、ディスクスペースも必要としません。


2
ただし、ファイルの所有権/アクセス権がリセットされないという欠点があります...エラー、待機...
ysth

1
冗長ですが、高速で堅牢です- ここで唯一の真のインプレースファイル編集の回答のようです(そして、誰にとっても明らかではない可能性があるため、これはPerlスクリプトです)。
mklement0 2015

6

以下は、整頓された素晴らしいPythonソリューションです。ここでは簡潔にするつもりはありませんでした。

これにより、ファイルのコピーを作成して、コピーの最後の行から改行を削除するのではなく、ファイルをインプレースで変更します。ファイルが大きい場合、これは最良の回答として選択されたPerlソリューションよりもはるかに高速になります。

最後の2バイトがCR / LFの場合は2バイトで切り捨て、最後のバイトがLFの場合は1バイトで切り捨てます。最後のバイトが(CR)LFでない場合、ファイルの変更は試行されません。エラーを処理します。Python 2.6でテストされています。

これを「striplast」と呼ばれるファイルに入れますchmod +x striplast

#!/usr/bin/python

# strip newline from last line of a file


import sys

def trunc(filename, new_len):
    try:
        # open with mode "append" so we have permission to modify
        # cannot open with mode "write" because that clobbers the file!
        f = open(filename, "ab")
        f.truncate(new_len)
        f.close()
    except IOError:
        print "cannot write to file:", filename
        sys.exit(2)

# get input argument
if len(sys.argv) == 2:
    filename = sys.argv[1]
else:
    filename = "--help"  # wrong number of arguments so print help

if filename == "--help" or filename == "-h" or filename == "/?":
    print "Usage: %s <filename>" % sys.argv[0]
    print "Strips a newline off the last line of a file."
    sys.exit(1)


try:
    # must have mode "b" (binary) to allow f.seek() with negative offset
    f = open(filename, "rb")
except IOError:
    print "file does not exist:", filename
    sys.exit(2)


SEEK_EOF = 2
f.seek(-2, SEEK_EOF)  # seek to two bytes before end of file

end_pos = f.tell()

line = f.read()
f.close()

if line.endswith("\r\n"):
    trunc(filename, end_pos)
elif line.endswith("\n"):
    trunc(filename, end_pos + 1)

PS「Perlゴルフ」の精神で、ここに私の最短のPythonソリューションがあります。ファイル全体を標準入力からメモリに取り込み、改行をすべて取り除き、結果を標準出力に書き込みます。Perlほど簡潔ではありません。このような少しトリッキーで速いもののためにPerlを倒すことはできません。

への呼び出しから「\ n」を削除します .rstrip()と、複数の空白行を含め、ファイルの末尾からすべての空白が削除されます。

これを「slurp_and_chomp.py」に入れて、を実行しpython slurp_and_chomp.py < inputfile > outputfileます。

import sys

sys.stdout.write(sys.stdin.read().rstrip("\n"))

os.path.isfile()はファイルの存在を通知します。try / exceptを使用すると、多くの異なるエラーがキャッチされる可能性があります:)
Denis Barmenkov 2013

5

高速な解決策は、gnuユーティリティを使用することですtruncate

[ -z $(tail -c1 file) ] && truncate -s-1 file

ファイルに末尾の新しい行がある場合、テストは真になります。

削除は非常に高速で、本当に適切です。新しいファイルは必要なく、検索も最後から1バイト(tail -c1)だけ読み取っています。


1
切り捨て:ファイルオペランドがありません
Brian Hannay

2
例の末尾のファイル名が欠落しているだけです[ -z $(tail -c1 filename) ] && truncate -s -1 filename(つまり、他のコメントへの応答として、truncateコマンドはstdinでは機能しません。ファイル名が必要です)
michael

4

さらに別のperl WTDI:

perl -i -p0777we's/\n\z//' filename

3
$ perl -e 'ローカル$ /; $ _ = <>; s / \ n $ //; 印刷 'a-text-file.txt

sedの任意の文字(改行を含む)に一致するを参照してください。


1
これですべての改行が削除されます。同等tr -d '\n'
追って通知があるまで一時停止。

これもうまくいきます。おそらくパビウムよりも冒涜的ではありません。
トッドパートリッジ 'Gen2ly'

Sinan、LinuxとUnixでは改行で終わるようにテキストファイルを定義する場合がありますが、Windowsではそのような要件はありません。たとえば、メモ帳は、最後に何も追加せずに、入力した文字のみを書き込みます。Cコンパイラでは、ソースファイルが改行で終わる必要がある場合がありますが、Cソースファイルは「単なる」テキストファイルではないため、追加の要件がある場合があります。
ロブ・ケネディ

その意味で、ほとんどのjavascript / cssミニファイアは末尾の改行を削除し、それでもテキストファイルを生成します。
ysth、2009年

@Rob Kennedyと@ysth:なぜそのようなファイルが実際にはテキストファイルなどではないのかについては、興味深い議論があります。
SinanÜnür2009年

2

ddを使用する:

file='/path/to/file'
[[ "$(tail -c 1 "${file}" | tr -dc '\n' | wc -c)" -eq 1 ]] && \
    printf "" | dd  of="${file}" seek=$(($(stat -f "%z" "${file}") - 1)) bs=1 count=1
    #printf "" | dd  of="${file}" seek=$(($(wc -c < "${file}") - 1)) bs=1 count=1

2
perl -pi -e 's/\n$// if(eof)' your_file

受け入れられた回答と実質的に同じですが、Perl以外のユーザーにとっては間違いなく概念が明確です。注意は必要ありませんことをg周りの括弧はeofperl -pi -e 's/\n$// if eof' your_file
mklement0 2015


1

さらに別の答えFTR(そして私のお気に入り!):バッククォートを介して出力を取り除き、キャプチャしたいものをエコー/キャットします。最後の改行は削除されます。例えば:

# Sadly, outputs newline, and we have to feed the newline to sed to be portable
echo thingy | sed -e 's/thing/sill/'

# No newline! Happy.
out=`echo thingy | sed -e 's/thing/sill/'`
printf %s "$out"

# Similarly for files:
file=`cat file_ending_in_newline`
printf %s "$file" > file_no_newline

1
猫とprintfのコンボが偶然見つかった(反対の動作を試みていた)。これにより、最後の改行だけでなく、最後の改行もすべて削除されることに注意してください。
テクノサウルス2013

1

POSIX SED:

'$ {/ ^ $ / d}'

$ - match last line


{ COMMANDS } - A group of commands may be enclosed between { and } characters. This is particularly useful when you want a group of commands to be triggered by a single address (or address-range) match.

最後の行が空白の場合にのみ削除されると思います。最後の行が空白でない場合、末尾の改行は削除されません。たとえば、echo -en 'a\nb\n' | sed '${/^$/d}'何も削除されません。echo -en 'a\nb\n\n' | sed '${/^$/d}'最後の行全体が空白なので、削除されます。
wisbucky

1

これは、ファイルからの読み取りまたはファイルへの出力ではなく、パイプ/リダイレクトで作業する必要がある場合に適したソリューションです。これは単一または複数の行で機能します。後続の改行があるかどうかに関係なく機能します。

# with trailing newline
echo -en 'foo\nbar\n' | sed '$s/$//' | head -c -1

# still works without trailing newline
echo -en 'foo\nbar' | sed '$s/$//' | head -c -1

# read from a file
sed '$s/$//' myfile.txt | head -c -1

詳細:

  • head -c -1文字が何であるかに関係なく、文字列の最後の文字を切り捨てます。文字列が改行で終わっていない場合は、文字を失うことになります。
  • そこで、この問題に対処するために、改行がない場合に末尾の改行を追加する別のコマンドを追加しますsed '$s/$//'。最初の$方法は、コマンドを最後の行にのみ適用することを意味します。s/$//「行末」を「基本的に何もしない」で置き換えることを意味します。ただし、末尾に改行が追加されないという副作用があります。

注:Macのデフォルトでheadは、この-cオプションはサポートされていません。代わりにbrew install coreutils使用できますghead


0

これを実行したいのはコードゴルフの場合だけで、コードをファイルからコピーしてecho -n 'content'>fileステートメントに貼り付けました。


途中です。ここで完全なアプローチ。
mklement0


0

同様の問題がありましたが、Windowsファイルで作業していて、それらのCRLFを保持する必要があります-Linuxでの私の解決策:

sed 's/\r//g' orig | awk '{if (NR>1) printf("\r\n"); printf("%s",$0)}' > tweaked

0
sed -n "1 x;1 !H
$ {x;s/\n*$//p;}
" YourFile

ファイル内の最後の\ nを削除する必要があります。巨大なファイルでは機能しない(sedバッファーの制限のため)


0

ルビー:

ruby -ne 'print $stdin.eof ? $_.strip : $_'

または:

ruby -ane 'q=p;p=$_;puts q if $.>1;END{print p.strip!}'
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.