web-dev-qa-db-ja.com

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

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

0013600   n   t  >  \n

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

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

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

Perl -pe 'chomp if eof' filename >filename2

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

Perl -pi -e 'chomp if eof' filename

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

これは、私が見たawkのWebサイトで「Perl冒asp」と説明されていました。

しかし、テストでは機能しました。

209
pavium

Shell コマンド置換 末尾の改行文字を削除するという事実を利用できます

Bash、ksh、zshで動作するシンプルなフォーム:

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

ポータブル(POSIX準拠)代替(やや効率が悪い):

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

注意:


他の回答のガイド

  • Perlが利用可能な場合は、 受け入れられた回答 に進みます-それはsimple and memory -efficient(入力ファイル全体を一度に読み取りません)。

  • それ以外の場合は、 ghostdog74'sAwk回答 -それはあいまいですが、メモリ効率も高いです; より読みやすい同等物(POSIX準拠)は次のとおりです。

    • awk 'NR > 1 { print prev } { prev=$0 } END { ORS=""; print }' in.txt
    • 出力レコードのセパレーター(END)の設定により、最後の行をOFSブロックで処理できるように、印刷は1行遅延され、末尾の\nなしで印刷されます。空の文字列に。
  • 冗長ではあるが、in-placeを実際に編集する高速で堅牢なソリューションが必要な場合(反対にオリジナルを置き換える一時ファイルを作成するには、 jrockway'sPerl script を検討してください。

53
mklement0

GNU coreutilsのheadを使用してこれを行うことができます。ファイルの末尾に関連する引数をサポートします。したがって、最後のバイトを使用しないでください:

head -c -1

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

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

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

[[ $(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'sanswertruncate --size=-1head -c-1を置き換えることができ、インプレース編集をサポートします。

45
Thor
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

16

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

CoreutilsからのGNUエコーを必要とする単一行ファイルの非常に簡単な方法:

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

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

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;
}

読み取りと追加のためにファイルを開きます。追加するために開くということは、ファイルの終わりまで既にseekedされていることを意味します。次に、tellを使用して、ファイルの末尾の数値位置を取得します。その番号を使用して1文字を検索し、その1文字を読み取ります。改行の場合、ファイルをその改行の前の文字に切り詰めます。それ以外の場合は、何もしません。

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

8
jrockway

素敵で整頓された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)

追伸「Perl golf」の精神で、これが私の最短のPythonソリューションです。ファイル全体を標準入力からメモリに丸、みし、すべての改行を最後から取り除き、結果を標準出力に書き込みます。 Perlほど簡潔ではありません。このようなちょっとしたトリッキーな高速処理でPerlに勝るものはありません。

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

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

import sys

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

さらに別のPerl WTDI:

Perl -i -p0777we's/\n\z//' filename
4
ysth
 $ Perl -e 'local $ /; $ _ = <>; s/\ n $ //; print- a-text-file.txt 

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

3
Sinan Ünür

高速な解決策は、gnuユーティリティの切り捨てを使用することです:

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

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

削除は非常に高速で、本当に適切に行われ、新しいファイルは不要であり、検索も最後から1バイトだけ読み取っています(末尾-c1)。

3
sorontar
Perl -pi -e 's/\n$// if(eof)' your_file
2
Vijay

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
cpit

Unixファイルタイプで、これが機能する最後の改行のみが必要であると仮定します。

sed -e '${/^$/d}'

複数の改行では機能しません...

*最後の行が空白行の場合のみ機能します。

2
LoranceStinson

さらに別の回答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
Nicholas Wilson

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.
1
Oleg Mazko

私は同様の問題を抱えていましたが、Windowsファイルで作業しており、それらのCRLFを維持する必要がありました-Linuxでの私のソリューション:

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

ルビー:

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

または:

Ruby -ane 'q=p;p=$_;puts q if $.>1;END{print p.strip!}'
0
peak

私がこれをやりたかったのはコードゴルフのときだけで、それからコードをファイルからコピーしてecho -n 'content'>fileステートメントに貼り付けました。

0
dlamblin
sed ':a;/^\n*$/{$d;N;};/\n$/ba' file
0
ghostdog74

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

# 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
wisbucky
sed -n "1 x;1 !H
$ {x;s/\n*$//p;}
" YourFile

ファイル内の最後の\ nを削除する必要があります。 sedバッファーの制限により、巨大なファイルで動作しない

0
NeronLeVelu