web-dev-qa-db-ja.com

viは黙ってファイルの最後に改行(LF)を追加しますか?

私は奇妙な動作を理解するのに問題があります:viは最後に改行(ASCII:LF、Unix( [〜#〜] aix [〜#〜] )システムであるため)を追加するようです特に入力しなかったときのファイル。

私はviでそのようにファイルを編集します(最後に改行を入力しないように注意してください)。

# vi foo   ## Which I will finish on the char "9" and not input a last newline, then `:wq`
123456789
123456789
123456789
123456789
~
~
  ## When I save, the cursor is just above the last "9", and no newline was added.

私はviがそれを「現状のまま」保存することを期待しているため、39バイトになる:10 ASCII最初の3行のそれぞれに文字(番号1から9)、その後に改行が続く(私の場合はLF system))と最後の行に9のみ(文字1〜9、終了改行/ LFなし)。

しかし、保存すると表示されます40バイト(39ではなく)、odは終了LFを示します

# wc foo
       4       4      40 foo  ## I expected 39 here! as I didn't add the last newline
# od -a toto
0000000    1   2   3   4   5   6   7   8   9  lf   1   2   3   4   5   6
0000020    7   8   9  lf   1   2   3   4   5   6   7   8   9  lf   1   2
0000040    3   4   5   6   7   8   9  lf
0000050
     ## An "lf" terminates the file?? Did vi add it silently?

私がvi内で行ったとおりに実行するprintfでファイルを作成すると、期待どおりに機能します。

# ## I create a file with NO newline at the end:
# printf "123456789\n123456789\n123456789\n123456789" > foo2
# wc foo2  ## This one is as expected: 39 bytes, exactly as I was trying to do above with vi.
       3       4      39 foo  ## As expected, as I didn't add the last newline

  ## Note that for wc, there are only three lines!
  ## (So wc -l doesn't count lines; it counts the [newline] chars... Which is rather odd.)

# root@SPU0WMY1:~  ## od -a foo2
0000000    1   2   3   4   5   6   7   8   9  lf   1   2   3   4   5   6
0000020    7   8   9  lf   1   2   3   4   5   6   7   8   9  lf   1   2
0000040    3   4   5   6   7   8   9
0000047                                ## As expected, no added LF.

両方のファイル(foo(40文字)とfoo2(39文字)は、vi ...

そしてfoo2(39文字、終端の改行なし)をviで開いてを実行すると、:wqまったく編集せずに、40文字を書き込むと表示され、改行が表示されます。

最近のviにアクセスできません(AIX、vi(これは Vim ではありません)バージョン3.10だと思いますか?(「-version」またはそれを知る他の方法はありません))。

# strings /usr/bin/vi | grep -i 'version.*[0-9]'
@(#) Version 3.10

ファイルの最後に静かに改行を追加するのは、vi(そしておそらく最新バージョンではないのか、それともVimなのか)にとって正常なことですか? (〜は、前の行が改行で終わっていないことを示していると思いました。)

-

編集:いくつかの追加の更新と少しの要約、以下の回答に感謝します:

  • viは、欠落しているファイルを書き込むときに(ファイルが空でない限り)末尾の改行を静かに追加します。

  • それは執筆の時だけそうします! (つまり、:wになるまで、:eを使用して、ファイルを開いたままであることを確認できます...(つまり、「filename」が表示されます[最後の行は完全ではありません] N行、M文字)。保存すると、特定の警告なしに、改行が静かに追加されます(保存されるバイト数は表示されますが、ほとんどの場合、改行が追加されたことを知るのに十分ではありません)(@jiliagreに感謝します。 viメッセージを開くと、変更が実際にいつ発生するかを知る方法を見つけるのに役立ちました)

  • これ(サイレント修正)は [〜#〜] posix [〜#〜] 動作です! (参照については@ barefoot-ioの回答を参照してください)

36
Olivier Dulac

これはviの予想される動作です。

ファイルの最後の行が不完全なので、厳密に言えば(つまり、POSIX標準に従って)、テキストファイルではなくバイナリファイルです。

viはバイナリファイルではなくテキストファイルエディターであり、保存時に適切に修正されます。

これにより、wcsedなどの他のテキストファイルツールが期待どおりの出力を提供できるようになります。 viはこの問題について黙っていないことに注意してください:


$ printf "one\ntwo" >file     # Create a unterminated file
$ cat file                    # Note the missing newline before the Prompt
one
two$ wc -l file               # wc ignores the incomplete last line
       1 file
$ sed '' file > file1
$ cat file1                   # so does a legacy sed
one
$ PATH=$(getconf PATH) sed  '' file
one                           # while a POSIX conformant sed warns you:
sed: Missing newline at end of file file.
two
$ vi file
one
two
~
~
~                             # vi tells you too about the issue
"file" [Incomplete last line] 2 lines, 7 characters

:w

"file" 2 lines, 8 characters  # and tells it writes two lines
                              # You'll even notice it writes one more
                              # character if you are a very shrewd observer :-)
:q
$ cat file                    # the file is now valid text
one
two
$ wc -l file                  # wc reports the expected number of lines
       2 file
$ sed '' file > file1         # sed works as expected
$ cat file1
one
two

実行しているviバージョンについての手がかりを得るには、:veコマンド。これは、ここではレガシーSVR4を使用していることを示していますが、vimではありません。

:ve
Version SVR4.0, Solaris 2.5.0

どうやら、あなたは言っています:

:ve
Version 3.10

それはおそらく、AIX viがSVR3ソースコードに基づいていることを意味します。

いずれにせよ、この動作と[Incomplete last line]警告メッセージは、少なくとも1979年以降の従来のBill Joyのviソースコードにあり、AFAIKは、AIXのような独自のUnixが構築されたSystem Vソースコードリリースから作成されたすべてのブランチに保持されています。

時系列的に言えば、この動作はPOSIX準拠の結果ではなく、偽のテキストファイルを編集するユーザーに役立つというビルジョイの元の決定の結果であり、10年後、この許容値を維持するというPOSIX委員会の決定の結果です。

edの代わりにviを使用すると、少なくともedがSVR3以降のソースブランチからのものである場合、前者の方が問題についてより冗長であることがわかります。

$ ed file
'\n' appended
8
q

空のファイルは、たまたま行が含まれていない有効なテキストファイルであることにも注意してください。その場合、修正する未終了の行がないため、viはファイルを保存するときに改行を追加しません。

28
jlliagre

POSIXはこの動作を必要とするため、決して珍しいことではありません。

POSIX viマニュアル から:

入力ファイル

Viコマンドでサポートされる入力ファイルの説明については、exコマンドの「入力ファイル」セクションを参照してください。

POSIX exマニュアル への道をたどる:

入力ファイル

入力ファイルは、テキストファイル、または{LINE_MAX} -1バイトより長くなく、NUL文字を含まない不完全な最終行を除いて、テキストファイルになるファイルでなければなりません。デフォルトでは、不完全な最後の行は、末尾に<改行>があるかのように扱われます。他の形式のファイルの編集は、オプションでex実装によって許可される場合があります。

ViマニュアルのOUTPUT FILESセクションもexにリダイレクトします。

出力ファイル

Exからの出力はテキストファイルです。

POSIX定義のペア:

.397テキストファイル

ゼロ以上の行に編成された文字を含むファイル。行にはNUL文字が含まれておらず、<改行>文字を含めて、長さが{LINE_MAX}バイトを超えることはできません。 POSIX.1-2008はテキストファイルとバイナリファイルを区別しませんが(ISO C標準を参照)、多くのユーティリティはテキストファイルを操作するときに予測可能な、または意味のある出力のみを生成します。このような制限のある標準ユーティリティは、STDINまたはINPUT FILESセクションで常に「テキストファイル」を指定します。

.206行

ゼロ個以上の非<newline>文字のシーケンスと終了の<newline>文字。

これらのマニュアルページの抜粋の文脈でのこれらの定義は、適合性のあるex/vi実装が不正な形式のテキストファイルを受け入れる必要があるのに対し、そのファイルの唯一の変形が最後の改行がない場合、そのファイルのバッファーを書き込むとき、結果は有効なテキストファイルでなければならないことを意味します。

この投稿はPOSIX標準の2013年版を参照していますが、 関連する規定ははるかに古い1997年版にも記載されています

最後に、元の改行アペンションが歓迎されないと感じると、セブンスエディションUNIX(1979)に不寛容なedに深く違反していると感じるでしょう。 マニュアルから

ファイルを読み取るとき、edはASCII NUL文字と最後の改行以降のすべての文字を破棄します。非ASCII文字を含むファイルの読み取りを拒否します。

51
Barefoot IO

Shell whileループを介して実行される最終的な改行が不適切に欠けているテキストは、最後の行が警告なしに破棄される原因となります。

$ (echo transaction 1; echo -n transaction 2) \
  | while read line; do echo $line; done
transaction 1
$ 

究極の改行が存在することを保証することは、適切で健全で適切なデフォルトです。もう1つのオプションには、最終的な改行が欠落しているテキストに触れるすべてのシェルコードを知っているか、監査する時間があるか、テキストの最後の行が失われるリスクがあります。

1
thrig

ファイルの最後に改行が追加される他の動作は思い出しません(80年代半ばからviを使用)。

~は、テキストの一部ではない画面上の行であることを示し、ファイルが改行で終わっていないことを示しています。 (シェルスクリプトの最終行に~を配置すると、エラーの追跡が難しくなる可能性があります)。末尾に改行を含む短いファイルをロードすると、~が自分で表示され、改行で終了していないテキストを示しているという考えが反証されます。

1
Anthon