バージョン管理システムを使用すると、diffが言うときにノイズに悩まされますNo newline at end of file
。
だから私は疑問に思っていた:それらのメッセージを取り除くためにファイルの最後に改行を追加する方法?
バージョン管理システムを使用すると、diffが言うときにノイズに悩まされますNo newline at end of file
。
だから私は疑問に思っていた:それらのメッセージを取り除くためにファイルの最後に改行を追加する方法?
回答:
プロジェクトを再帰的にサニタイズするには、このonelinerを使用します。
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"
前のコマンドの終了ステータスがゼロ以外の場合、ファイルに改行を追加します。
find -name \*.java | while read f; do tail -n1 $f | read -r _ || echo >> $f; done
git ls-files
、バージョン管理で追跡されないファイルを編集することからあなたを救うパターンを渡すこともできます。
IFS=
てセパレータを設定解除すると、周囲の空白を保持するのに適しています。nullで終了するエントリは、名前に改行が含まれるファイルまたはディレクトリがある場合にのみ関連します。改行はかなりフェッチされているように見えますが、一般的なケースを処理するより適切な方法です、私は同意します。小さな警告として:-d
オプションread
はPOSIX shでは利用できません。
tail -n1 < "$f"
で始まるファイル名の問題を回避するための私の使用も参照してください-
(tail -n1 -- "$f"
と呼ばれるファイルでは機能しません-
)。答えがzsh / bash固有になったことを明確にしたいかもしれません。
どうぞ。
sed -i -e '$a\' file
また、OS Xの場合sed
:
sed -i '' -e '$a\' file
これは\n
、ファイルが改行で終了していない場合にのみ、ファイルの最後に追加されます。したがって、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
man sed
:$ Match the last line.
しかし、たぶん偶然にしか機能しないかもしれません。ソリューションも機能します。
$
ます。formなどの正規表現内/<regex>/
では、通常の「行末に一致」という意味があります。それ以外の場合、アドレスとして使用されるsedは、特別な「ファイルの最終行」の意味を与えます。デフォルトではsedが出力に改行を追加します(まだ存在しない場合)。コード「$ a \」は、「ファイルの最後の行に一致し、何も追加しない」というだけです。しかし、暗黙的に、sedは、処理していないすべての行(この$
行など)に改行を追加します(まだない場合)。
/regex/
すると、別の意味が与えられます。FreeBSDのマンページはもう少し参考になると思います:freebsd.org/cgi/man.cgi
ご覧ください:
$ echo -n foo > foo
$ cat foo
foo$
$ echo "" >> foo
$ cat foo
foo
そのecho "" >> noeol-file
トリックを行う必要があります。(または、これらのファイルの識別と修正を依頼するつもりでしたか?)
編集""
から削除echo "" >> foo
(@yuyichaoのコメントを参照)
edit2が""
再度追加(ただし @Keith Thompsonのコメントを参照)
""
は(少なくともbashには)必要ではなくtail -1 | wc -l
、最後に改行なしでファイルを見つけるために使用できます
""
bashには必要echo
ありませんが、引数なしで呼び出されたときに何も表示されない実装を見てきました(ただし、これを実行できるものはありません)。 echo "" >> noeol-file
おそらくわずかに堅牢です。 printf "\n" >> noeol-file
さらにそうです。
csh
's echo
は、引数が渡されない場合は何も出力しないことがわかっています。私たちは非ボーンのようなシェルをサポートするつもりならしかし、その後、我々はそれが作るべきecho ''
代わりのecho ""
ようにecho ""
出力リレーう""<newline>
とrc
か、es
例えば。
tcsh
、とは異なりcsh
、引数なしで呼び出された場合、-の設定に関係なく、改行を出力します$echo_style
。
を使用する別のソリューションed
。このソリューションは、最後の行にのみ影響し、\n
欠落している場合のみ:
ed -s file <<< w
基本的には、スクリプトを介して編集用にファイルを開き、スクリプトはw
ファイルをディスクに書き戻す単一のコマンドです。これは、ed(1)
manページにある次の文に基づいています。
制限事項 (...) テキスト(非バイナリ)ファイルが改行文字で終了していない場合、 edは、読み取り/書き込み時に1を追加します。バイナリの場合 ファイル、edは読み取り/書き込み時に改行を追加しません。
存在しない最終的な改行をテキストファイルに追加するための、POSIX準拠のシンプルでポータブルな方法は次のとおりです。
[ -n "$(tail -c1 file)" ] && echo >> file
この方法では、ファイル全体を読み取る必要はありません。単にEOFをシークし、そこから動作することができます。
このアプローチでは、背中の後ろに一時ファイルを作成する必要もありません(たとえば、sed -i)。そのため、ハードリンクは影響を受けません。
echoは、コマンド置換の結果が空でない文字列である場合にのみ、ファイルに改行を追加します。これは、ファイルが空でなく、最後のバイトが改行でない場合にのみ発生することに注意してください。
ファイルの最後のバイトが改行の場合、tailはそれを返し、コマンド置換はそれを取り除きます。結果は空の文字列です。-nテストは失敗し、エコーは実行されません。
ファイルが空の場合、コマンド置換の結果も空の文字列になり、再びエコーは実行されません。空のファイルは無効なテキストファイルではなく、空の行を持つ空でないテキストファイルと同等ではないため、これは望ましいことです。
yash
ファイルの最後の文字がマルチバイト文字(たとえば、UTF-8ロケール)の場合、またはロケールがCで、ファイルの最後のバイトに8番目のビットが設定されている場合は機能しないことに注意してください。他のシェル(zshを除く)では、ファイルがNULバイトで終了した場合、改行は追加されません(ただし、これは、改行が追加された後でも入力が非テキストになることを意味します)。
関係なく改行を追加します。
echo >> filename
Pythonを使用して、改行を追加する前に改行が最後に存在するかどうかを確認する方法を次に示します。
f=filename; python -c "import sys; sys.exit(open(\"$f\").read().endswith('\n'))" && echo >> $f
echo ""
は、より堅牢なようですecho -n '\n'
。または使用することができますprintf '\n'
最速のソリューションは次のとおりです。
[ -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回実行すると、別の改行は追加されません。
xxd
またhexdump
、POSIXユーティリティでもないことに注意してください。POSIXツールチェストでod -An -tx1
は、バイトの16進数値を取得する必要があります。
ファイルを最後に編集したユーザーのエディターを修正することをお勧めします。あなたがファイルを編集した最後の人である場合-あなたはどのエディタを使用していますか、私はテキストメイトを推測しています..?
emacs
ファイルの末尾に改行を追加しないでください。
(setq require-final-newline 'ask)
ている.emacs
パイプラインを処理するときに、すぐに改行を追加するだけの場合は、これを使用します。
outputting_program | { cat ; echo ; }
POSIXにも準拠しています。
その後、もちろん、ファイルにリダイレクトできます。
cat file.csv | tr "\r" "\n" | { cat; echo; } | sed "/^[[:space:]]*$/d" | tail -n +2 | wc -l
入力にヌルがない場合:
paste - <>infile >&0
... infileがまだない場合は、常にinfileの末尾にのみ改行を追加すれば十分です。そして、入力ファイルを一度だけ読むだけで正しくなります。
paste infile 1<> infile
代わりに必要になります。
質問に直接回答するわけではありませんが、改行で終わらないファイルを検出するために作成した関連スクリプトを次に示します。とても速いです。
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:」(「ファイルが改行で終わる場合は「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"
シェルやその他のユーティリティの一部のバージョンでは、そのようなファイルを読み取るときに、不足している最終改行を適切に処理できないため、最終改行がないとスクリプトにバグが生じる可能性があります。
私の経験では、最終的な改行がないのは、さまざまな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
複数のファイルを修正するには、次を確認してください:多くのファイルの「ファイルの終わりに改行なし」を修正する方法?SOで
なぜこれがとても重要なのですか?ファイルをPOSIX互換に保つため。
受け入れられた回答を現在のディレクトリ(およびサブディレクトリ)内のすべてのファイルに適用するには:
$ find . -type f -exec sed -i -e '$a\' {} \;
これはLinux(Ubuntu)で機能します。OS Xでは、おそらく-i ''
(未テスト)を使用する必要があります。
find .
ファイルを含むすべてのファイルがリストされていることに注意してください.git
。除外するには:find . -type f -not -path './.git/*' -exec sed -i -e '$a\' {} \;
少なくともGNUバージョンでは、入力を単純grep ''
またはawk 1
正規化し、まだ存在しない場合は最終的な改行を追加します。彼らはプロセスでファイルをコピーしますが、これは大きければ時間がかかりますが(ソースは大きすぎて読まないはずですか?)、あなたが何かをしない限りmodtimeを更新します
mv file old; grep '' <old >file; touch -r old file
(ただし、チェックインしているファイルについては、変更したため大丈夫かもしれませんが)さらに注意しない限り、ハードリンク、デフォルト以外のパーミッション、ACLなどを失います。
grep '' file 1<> file
、それでもファイルを完全に読み書きしますが。
これはAIX kshで機能します。
lastchar=`tail -c 1 *filename*`
if [ `echo "$lastchar" | wc -c` -gt "1" ]
then
echo "/n" >> *filename*
fi
私の場合、ファイルに改行がない場合、wc
コマンドは値を返し2
、改行を書き込みます。
Patrick Oscityの回答に追加するだけで、特定のディレクトリに適用したいだけなら、以下を使用することもできます。
find -type f | while read f; do tail -n1 $f | read -r _ || echo >> $f; done
改行を追加するディレクトリ内でこれを実行します。
echo $'' >> <FILE_NAME>
ファイルの最後に空白行を追加します。
echo $'\n\n' >> <FILE_NAME>
ファイルの最後に3つの空白行を追加します。
ファイルが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
場合、検索正規表現は一致しないため、何も起こりません。
次のfix-non-delimited-line
ようなスクリプトを書くことができます。
#! /bin/zsh -
zmodload zsh/system || exit
ret=0
for file do
if sysopen -rwu0 -- "$file"; then
if sysseek -w end -1; then
read -r x || print -u0
else
syserror -p "Can't seek in $file before the last byte: "
ret=1
fi
else
ret=1
fi
done
exit $ret
ここで与えられたソリューションのいくつかに反して、それ
たとえば、次のように使用できます。
that-script *.txt
または:
git ls-files -z | xargs -0 that-script
POSIXly、機能的に同等の何かをすることができます
export LC_ALL=C
ret=0
for file do
[ -s "$file" ] || continue
{
c=$(tail -c 1 | od -An -vtc)
case $c in
(*'\n'*) ;;
(*[![:space:]]*) printf '\n' >&0 || ret=$?;;
(*) ret=1;; # tail likely failed
esac
} 0<> "$file" || ret=$? # record failure to open
done