バージョン管理システムを使用すると、diffがNo newline at end of file
。
だから私は疑問に思っていました:ファイルの最後に改行を追加してそれらのメッセージを取り除く方法は?
プロジェクトを再帰的にサニタイズするには、次のワンライナーを使用します。
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"
前のコマンドの終了ステータスがゼロ以外の場合、ファイルに改行を追加します。
ここに行きます :
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
見てください:
$ echo -n foo > foo
$ cat foo
foo$
$ echo "" >> foo
$ cat foo
foo
echo "" >> noeol-file
がうまくいくはずです。 (または、これらのファイルの識別との修正を要求するつもりでしたか?)
編集""
をecho "" >> foo
から削除しました(@yuyichaoのコメントを参照)edit2""
を再度追加しました(but@Keith Thompsonのコメントを参照)
ed
を使用した別のソリューション。このソリューションは最後の行にのみ影響し、_\n
_がない場合にのみ影響します。
_ed -s file <<< w
_
基本的に、スクリプトを介して編集するためにファイルを開き、スクリプトはファイルをディスクに書き戻す単一のw
コマンドです。 ed(1)
manページにあるこの文に基づいています:
制限 (...) テキスト(非バイナリ)ファイルが改行文字で終了していない場合は、 で編集されます。読み取り/書き込み時に1つ追加します。バイナリ ファイルの場合、edは読み取り/書き込み時に改行を追加しません。
関係なく改行を追加:
echo >> filename
Pythonを使用して、改行を追加する前に最後に改行が存在するかどうかを確認する方法は次のとおりです。
f=filename; python -c "import sys; sys.exit(open(\"$f\").read().endswith('\n'))" && echo >> $f
存在しない最後の改行をに追加する、シンプルで移植可能なPOSIX準拠の方法は、テキストファイルです。
[ -n "$(tail -c1 file)" ] && echo >> file
このアプローチでは、ファイル全体を読み取る必要はありません。 EOFにシークして、そこから作業することができます。
このアプローチでは、一時ファイルを背中に作成する必要もないため(例:sed -i)、ハードリンクは影響を受けません。
echoは、コマンド置換の結果が空でない文字列である場合にのみ、ファイルに改行を追加します。これは、ファイルが空でなく、最後のバイトが改行でない場合にのみ発生することに注意してください。
ファイルの最後のバイトが改行の場合、tailはそれを返し、コマンド置換はそれを取り除きます。結果は空の文字列です。 -nテストは失敗し、echoは実行されません。
ファイルが空の場合、コマンド置換の結果も空の文字列になり、再度エコーは実行されません。空のファイルは無効なテキストファイルではなく、空の行を含む空でないテキストファイルと同じではないため、これは望ましいことです。
最速のソリューションは次のとおりです。
[ -n "$(tail -c1 file)" ] && printf '\n' >>file
本当に速いです。
中サイズのファイル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
Ash、bash、lksh、mksh、ksh93、attsh、zshで機能しますが、yashでは機能しません。
Yash(および上記のその他すべてのシェル)に移植可能なソリューションが必要な場合は、少し複雑になる可能性があります。
f=file
if [ "$(tail -c1 "$f"; echo x)" != "$(printf '\nx')" ]
then printf '\n' >>"$f"
fi
ファイルの最後のバイトが改行かどうかをテストする最も速い方法は、その最後のバイトのみを読み取ることです。これは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で改行が追加されます。
最後にファイルを編集したユーザーのエディターを修正する方がよいでしょう。あなたがファイルを編集した最後の人である場合-どのエディタを使用していますか、テキストメイトだと思います...
入力にnullがない場合:
paste - <>infile >&0
...常に改行がまだない場合は、常に改行を末尾の末尾に追加するだけで十分です。そして、それを正しくするために、一度だけ入力ファイルを読む必要があります。
パイプラインを処理するときに改行をすばやく追加したい場合は、次のようにします。
outputting_program | { cat ; echo ; }
また、POSIXにも準拠しています。
その後、もちろん、ファイルにリダイレクトできます。
これは直接質問に答えるものではありませんが、改行で終わらないファイルを検出するために私が書いた関連スクリプトを次に示します。とても速いです。
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互換 。
ファイルが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
が含まれている場合、検索正規表現は一致しないため、何も起こりません。
承認された回答を現在のディレクトリ(およびサブディレクトリ)内のすべてのファイルに適用するには:
$ find . -type f -exec sed -i -e '$a\' {} \;
これはLinux(Ubuntu)で動作します。 OS Xでは、おそらく-i ''
(テストされていない)。
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
ここに記載されているいくつかのソリューションとは異なり、
たとえば、次のように使用できます。
that-script *.txt
または:
git ls-files -z | xargs -0 that-script
Patrick Oscity's answer に追加します。特定のディレクトリに適用したいだけの場合は、以下を使用することもできます。
find -type f | while read f; do tail -n1 $f | read -r _ || echo >> $f; done
これを、改行を追加するディレクトリ内で実行します。
echo $'' >> <FILE_NAME>
は、ファイルの最後に空白行を追加します。
echo $'\n\n' >> <FILE_NAME>
は、ファイルの最後に3つの空白行を追加します。
少なくともGNUバージョンでは、単にgrep ''
またはawk 1
入力を正規化し、まだ存在しない場合は最後の改行を追加します。彼らはプロセスでファイルをコピーしますが、サイズが大きい場合は時間がかかります(ただし、ソースが大きすぎて読み取ることができないはずですか?)。
mv file old; grep '' <old >file; touch -r old file
(ファイルを変更したため、チェックインしているファイルでは問題ないかもしれませんが)、さらに注意しない限り、ハードリンク、デフォルト以外のアクセス許可、ACLなどが失われます。
ここには多くの素晴らしい提案がありますが、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
これは、AIX kshで機能します。
lastchar=`tail -c 1 *filename*`
if [ `echo "$lastchar" | wc -c` -gt "1" ]
then
echo "/n" >> *filename*
fi
私の場合、ファイルに改行がない場合、wc
コマンドは2
の値を返し、改行を書き込みます。