web-dev-qa-db-ja.com

ファイルの最後に改行を追加するにはどうすればよいですか?

バージョン管理システムを使用すると、diffがNo newline at end of file

だから私は疑問に思っていました:ファイルの最後に改行を追加してそれらのメッセージを取り除く方法は?

208
k0pernikus

プロジェクトを再帰的にサニタイズするには、次のワンライナーを使用します。

git ls-files -z | while IFS= read -rd '' f; do tail -c1 < "$f" | read -r _ || echo >> "$f"; done

説明:

  • git ls-files -z リポジトリ内のファイルを一覧表示します。追加のパラメーターとしてオプションのパターンを使用します。これは、操作を特定のファイル/ディレクトリーに制限したい場合に役立つことがあります。別の方法として、find -print0 ...または同様のプログラムで影響を受けるファイルを一覧表示します-それが NUL -delimitedエントリを出力することを確認してください。

  • while IFS= read -rd '' f; do ... doneエントリを反復処理し、空白や改行を含むファイル名を安全に処理します。

  • tail -c1 < "$f"は、ファイルから最後の文字を読み取ります。

  • read -r _末尾の改行がない場合、ゼロ以外の終了ステータスで終了します。

  • || echo >> "$f"前のコマンドの終了ステータスがゼロ以外の場合、ファイルに改行を追加します。

46
Patrick Oscity

ここに行きます

sed -i -e '$a\' file

OS Xの場合sed

sed -i '' -e '$a\' file

これにより、ファイルの最後に\nが追加されますonly改行で終わっていない場合。したがって、2回実行しても、改行は追加されません。

$ cd "$(mktemp -d)"
$ printf foo > test.txt
$ sed -e '$a\' test.txt > test-with-eol.txt
$ diff test*
1c1
< foo
\ No newline at end of file
---
> foo
$ echo $?
1
$ sed -e '$a\' test-with-eol.txt > test-still-with-one-eol.txt
$ diff test-with-eol.txt test-still-with-one-eol.txt
$ echo $?
0
215
l0b0

見てください:

$ echo -n foo > foo 
$ cat foo
foo$
$ echo "" >> foo
$ cat foo
foo

echo "" >> noeol-fileがうまくいくはずです。 (または、これらのファイルの識別の修正を要求するつもりでしたか?)

編集""echo "" >> fooから削除しました(@yuyichaoのコメントを参照)edit2""を再度追加しました(but@Keith Thompsonのコメントを参照)

41
sr_

edを使用した別のソリューション。このソリューションは最後の行にのみ影響し、_\n_がない場合にのみ影響します。

_ed -s file <<< w
_

基本的に、スクリプトを介して編集するためにファイルを開き、スクリプトはファイルをディスクに書き戻す単一のwコマンドです。 ed(1) manページにあるこの文に基づいています:

制限
(...)
 
テキスト(非バイナリ)ファイルが改行文字で終了していない場合は、
で編集されます。読み取り/書き込み時に1つ追加します。バイナリ
ファイルの場合、edは読み取り/書き込み時に改行を追加しません。
16
enzotib

関係なく改行を追加:

echo >> filename

Pythonを使用して、改行を追加する前に最後に改行が存在するかどうかを確認する方法は次のとおりです。

f=filename; python -c "import sys; sys.exit(open(\"$f\").read().endswith('\n'))" && echo >> $f
15
Alexander

存在しない最後の改行をに追加する、シンプルで移植可能なPOSIX準拠の方法は、テキストファイルです。

[ -n "$(tail -c1 file)" ] && echo >> file

このアプローチでは、ファイル全体を読み取る必要はありません。 EOFにシークして、そこから作業することができます。

このアプローチでは、一時ファイルを背中に作成する必要もないため(例:sed -i)、ハードリンクは影響を受けません。

echoは、コマンド置換の結果が空でない文字列である場合にのみ、ファイルに改行を追加します。これは、ファイルが空でなく、最後のバイトが改行でない場合にのみ発生することに注意してください。

ファイルの最後のバイトが改行の場合、tailはそれを返し、コマンド置換はそれを取り除きます。結果は空の文字列です。 -nテストは失敗し、echoは実行されません。

ファイルが空の場合、コマンド置換の結果も空の文字列になり、再度エコーは実行されません。空のファイルは無効なテキストファイルではなく、空の行を含む空でないテキストファイルと同じではないため、これは望ましいことです。

12
Barefoot IO

最速のソリューションは次のとおりです。

[ -n "$(tail -c1 file)" ] && printf '\n' >>file 

  1. 本当に速いです。
    中サイズのファイルseq 99999999 >fileこれにはミリ秒かかります。
    他の解決策には時間がかかります:

    [ -n "$(tail -c1 file)" ] && printf '\n' >>file  0.013 sec
    vi -ecwq file                                    2.544 sec
    paste file 1<> file                             31.943 sec
    ed -s file <<< w                             1m  4.422 sec
    sed -i -e '$a\' file                         3m 20.931 sec
    
  2. Ash、bash、lksh、mksh、ksh93、attsh、zshで機能しますが、yashでは機能しません。

  3. 改行を追加する必要がない場合は、ファイルのタイムスタンプを変更しません。
    ここに示す他のすべてのソリューションは、ファイルのタイムスタンプを変更します。
  4. 上記のすべてのソリューションは、有効なPOSIXです。

Yash(および上記のその他すべてのシェル)に移植可能なソリューションが必要な場合は、少し複雑になる可能性があります。

f=file
if       [ "$(tail -c1 "$f"; echo x)" != "$(printf '\nx')" ]
then     printf '\n' >>"$f"
fi
8
Isaac

ファイルの最後のバイトが改行かどうかをテストする最も速い方法は、その最後のバイトのみを読み取ることです。これはtail -c1 fileで実行できます。ただし、ファイルの最後の文字がUTF-の場合、シェルの通常のコマンド展開内の末尾の新しい行の削除に応じて、バイト値が新しい行かどうかをテストする単純な方法は(たとえば)yashで失敗します。 8値。

ファイルの最後のバイトが新しい行であるかどうかを確認する正しい、POSIX準拠のすべての(合理的な)シェルの方法は、xxdまたはhexdumpのいずれかを使用することです。

tail -c1 file | xxd -u -p
tail -c1 file | hexdump -v -e '/1 "%02X"'

次に、上記の出力を0Aと比較すると、堅牢なテストが提供されます。
空のファイルに新しい行を追加しないようにすると便利です。
もちろん0Aの最後の文字を提供できないファイル:

f=file
a=$(tail -c1 "$f" | hexdump -v -e '/1 "%02X"')
[ -s "$f" -a "$a" != "0A" ] && echo >> "$f"

短くて甘い。最後のバイトを読み取るだけなので、これにはほとんど時間がかかりません(EOFにシークします)。ファイルが大きいかどうかは関係ありません。次に、必要に応じて1バイトだけ追加します。

一時ファイルは不要であり、使用されません。ハードリンクは影響を受けません。

このテストを2回実行すると、notで改行が追加されます。

7
sorontar

最後にファイルを編集したユーザーのエディターを修正する方がよいでしょう。あなたがファイルを編集した最後の人である場合-どのエディタを使用していますか、テキストメイトだと思います...

4
AD7six

入力にnullがない場合:

paste - <>infile >&0

...常に改行がまだない場合は、常に改行を末尾の末尾に追加するだけで十分です。そして、それを正しくするために、一度だけ入力ファイルを読む必要があります。

3
user157018

パイプラインを処理するときに改行をすばやく追加したい場合は、次のようにします。

outputting_program | { cat ; echo ; }

また、POSIXにも準拠しています。

その後、もちろん、ファイルにリダイレクトできます。

3
MichalH

これは直接質問に答えるものではありませんが、改行で終わらないファイルを検出するために私が書いた関連スクリプトを次に示します。とても速いです。

find . -type f | # sort |        # sort file names if you like
/usr/bin/Perl -lne '
   open FH, "<", $_ or do { print " error: $_"; next };
   $pos = sysseek FH, 0, 2;                     # seek to EOF
   if (!defined $pos)     { print " error: $_"; next }
   if ($pos == 0)         { print " empty: $_"; next }
   $pos = sysseek FH, -1, 1;                    # seek to last char
   if (!defined $pos)     { print " error: $_"; next }
   $cnt = sysread FH, $c, 1;
   if (!$cnt)             { print " error: $_"; next }
   if ($c eq "\n")        { print "   EOL: $_"; next }
   else                   { print "no EOL: $_"; next }
'

Perlスクリプトは、stdinから(オプションでソートされた)ファイル名のリストを読み取り、すべてのファイルについて最後のバイトを読み取って、ファイルが改行で終わるかどうかを判断します。各ファイルの内容全体を読み取る必要がないため、非常に高速です。読み取ったファイルごとに1行を出力し、何らかのエラーが発生した場合は「error:」のプレフィックスを付け、ファイルが空の場合は「empty:」(改行で終わらない!)、「EOL:」(「end of line ")ファイルが改行で終わっている場合は" no EOL: "、ファイルが改行で終わっていない場合。

注:スクリプトは、改行を含むファイル名を処理しません。 GNUまたはBSDシステムを使用している場合、次のように、-print0を検索に、-zをソートに、-0をPerlに追加することにより、すべての可能なファイル名を処理できます。

find . -type f -print0 | sort -z |
/usr/bin/Perl -ln0e '
   open FH, "<", $_ or do { print " error: $_"; next };
   $pos = sysseek FH, 0, 2;                     # seek to EOF
   if (!defined $pos)     { print " error: $_"; next }
   if ($pos == 0)         { print " empty: $_"; next }
   $pos = sysseek FH, -1, 1;                    # seek to last char
   if (!defined $pos)     { print " error: $_"; next }
   $cnt = sysread FH, $c, 1;
   if (!$cnt)             { print " error: $_"; next }
   if ($c eq "\n")        { print "   EOL: $_"; next }
   else                   { print "no EOL: $_"; next }
'

もちろん、出力で改行を使用してファイル名をエンコードする方法を考え出す必要があります(読者のための演習として残しました)。

必要に応じて、出力をフィルタリングして、改行を持たないファイルに改行を追加できます。

 echo >> "$filename"

Shellや他のユーティリティの一部のバージョンでは、そのようなファイルを読み取るときに欠落している最終改行を適切に処理しないため、最終改行がないとスクリプトにバグが発生する可能性があります。

私の経験では、最後の改行の欠如は、さまざまなWindowsユーティリティを使用してファイルを編集したことが原因です。 vimがファイルの編集時に最終的な改行の欠落を引き起こすのを見たことはありませんが、そのようなファイルについては報告します。

最後に、次のように、改行で終わっていないファイルを印刷するためにファイル名入力をループすることができる、はるかに短い(遅い)スクリプトがあります。

/usr/bin/Perl -ne 'print "$ARGV\n" if /.\z/' -- FILE1 FILE2 ...

vi/vim/exエディターは、自動的に<EOL>をEOFに追加します(ファイルに既にない場合)。

だからどちらかを試してください:

vi -ecwq foo.txt

これは次と同等です:

ex -cwq foo.txt

テスト:

$ printf foo > foo.txt && wc foo.txt
0 1 3 foo.txt
$ ex -scwq foo.txt && wc foo.txt
1 1 4 foo.txt

複数のファイルを修正するには、次を確認してください。 多くのファイルで「ファイルの終わりに改行がない」を修正する方法

なぜこれがとても重要なのですか?ファイルを保持するには POSIX互換

1
kenorb

ファイルがWindows行末\r\nで終了し、Linuxを使用している場合は、このsedコマンドを使用できます。最後の行に\r\nがまだない場合にのみ追加します。

sed -i -e '$s/\([^\r]\)$/\1\r\n/'

説明:

-i    replace in place
-e    script to run
$     matches last line of a file
s     substitute
\([^\r]\)$    search the last character in the line which is not a \r
\1\r\n    replace it with itself and add \r\n

最後の行にすでに\r\nが含まれている場合、検索正規表現は一致しないため、何も起こりません。

0
masgo

承認された回答を現在のディレクトリ(およびサブディレクトリ)内のすべてのファイルに適用するには:

$ find . -type f -exec sed -i -e '$a\' {} \;

これはLinux(Ubuntu)で動作します。 OS Xでは、おそらく-i ''(テストされていない)。

0
friederbluemle

fix-non-delimited-lineのようなスクリプト:

#! /bin/zsh -
zmodload zsh/system || exit
ret=0
for file do
  (){
    sysseek -w end -1 || {
      syserror -p "Can't seek before the last byte: "
      return 1
    }
    read -r x || print -u0
  } <> $file || ret=$?
done
exit $ret

ここに記載されているいくつかのソリューションとは異なり、

  • プロセスをフォークせず、ファイルごとに1バイトだけを読み取り、ファイルを上書きしない(改行を追加するだけ)という点で効率的である必要があります。
  • シンボリックリンク/ハードリンクを壊したり、メタデータに影響を与えたりしません(また、ctime/mtimeは改行が追加されたときにのみ更新されます)
  • 最後のバイトがNULであるか、マルチバイト文字の一部であっても、正常に機能するはずです。
  • ファイル名に含まれる可能性のある文字または非文字に関係なく、正常に機能するはずです。
  • 正しく読めない、書けない、または検索できないファイルを処理する必要があります(それに応じてエラーを報告します)
  • 空のファイルに改行を追加しないでください(その場合、無効なシークに関するエラーが報告されます)

たとえば、次のように使用できます。

that-script *.txt

または:

git ls-files -z | xargs -0 that-script
0

Patrick Oscity's answer に追加します。特定のディレクトリに適用したいだけの場合は、以下を使用することもできます。

find -type f | while read f; do tail -n1 $f | read -r _ || echo >> $f; done

これを、改行を追加するディレクトリ内で実行します。

0
cipher

echo $'' >> <FILE_NAME>は、ファイルの最後に空白行を追加します。

echo $'\n\n' >> <FILE_NAME>は、ファイルの最後に3つの空白行を追加します。

0
user247137

少なくともGNUバージョンでは、単にgrep ''またはawk 1入力を正規化し、まだ存在しない場合は最後の改行を追加します。彼らはプロセスでファイルをコピーしますが、サイズが大きい場合は時間がかかります(ただし、ソースが大きすぎて読み取ることができないはずですか?)。

 mv file old; grep '' <old >file; touch -r old file

(ファイルを変更したため、チェックインしているファイルでは問題ないかもしれませんが)、さらに注意しない限り、ハードリンク、デフォルト以外のアクセス許可、ACLなどが失われます。

0

ここには多くの素晴らしい提案がありますが、1つのアイデアは改行を削除して追加することです。そうすれば、継続的にそれらを追加していないことがわかります。

ファイル「foo」を取得します。

新しい行がある場合は削除します。

  truncate -s $(($(stat -c '%s' foo)-1)) foo

次に1つ追加します。

  sed -i -e '$a\' foo

したがって、fooには常に少なくとも1つの新しい行が含まれます。

または、ファイルをテールして新しい行を探します。含まれていない場合は、1行追加します。

  grep -q "[^0-9a-z-]" <<< $(tail -1 ./foo) && echo " " >> ./foo
0
Mike Q

これは、AIX kshで機能します。

lastchar=`tail -c 1 *filename*`
if [ `echo "$lastchar" | wc -c` -gt "1" ]
then
    echo "/n" >> *filename*
fi

私の場合、ファイルに改行がない場合、wcコマンドは2の値を返し、改行を書き込みます。

0
Daemoncan