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


36

私は奇妙な動作を理解するのに苦労しています: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バイト:最初の3行(数字1から9、それに続く改行(私のシステムではLF))のそれぞれに10 ASCII文字、最後に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 ...

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

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

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

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

-

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

  • viは、ファイルが空の場合を除いて、それが欠落しているファイルを書き込むときに、末尾の改行をサイレントに追加します。

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

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


完全を期すために、AIXのバージョン(フルバージョン)。
EightBitTony

2
AIXのviにこのオプションがあることを知らない-vimのみに
ジェフシャラー

1
@JeffSchaller:リンクのthx。残念ながら、ネイティブのviはありません「:セットnoeol」すら-bオプションをバイナリモードで開くように...
オリヴィエ・デュラック

1
コマンドをvi実行することで、バージョンまたは少なくともその起源に関する手掛かりを取得できる場合があります:ve
jlliagre

1
@ThomasDickey確かに。何らかの理由で、IBM ex:verコマンドが通常記載されているマニュアルページを削除しました。
jlliagre

回答:


28

これは予想される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]警告メッセージはvi、少なくとも1979年以降のレガシーBill Joyのソースコードにあり、AIXのような独自のUnixが構築されたSystem Vソースコードリリースから作成されたすべてのブランチに保持されています。

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

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

$ ed file
'\n' appended
8
q

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


1
私はあなたのviのための間違いのVimを信じて、非常に少ない冗長これよりVI)レガシー...
オリヴィエ・デュラック

@OlivierDulac私はそれらを混乱させていません。このテストはvi、異なるUnix上ではありますが、OPと同じようにSVR4レガシーを使用して行われました。これはvim別のクローンではありません。これを明確にするために更新された回答。
jlliagre

@OlivierDulacうーん、私はあなたが実際にOPであることに気付きました。AIXは、そのvi実装に古いSystem Vブランチを使用しているようです。おそらくSVR3。[Incomplete last line]ファイルを開いてもメッセージはありませんか?
jlliagre

@OlivierDulacこのリンクは、AIX vi実装によってこのまったく同じメッセージが表示されることを暗示しているようです:www-01.ibm.com/support/docview.wss
uid=

明日はこれを見ようと思います
オリヴィエデュラック

51

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

POSIX viのマニュアル

入力ファイル

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

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

入力ファイル

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

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

出力ファイル

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

POSIX定義のペア:

3.397テキストファイル

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

3.206ライン

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

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

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

最後に、exの改行アペンションが歓迎されない場合、Seventh Edition UNIX(1979)の不寛容なedに深く違反していると感じるでしょう。マニュアルから

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


おかげで、私の質問に答えることができます。より良い回答が得られた場合に備えて、あと数日待ちますが、今はあなたが受け入れられる答えになれると思います。
オリビエデュラック

仕様から直接、徹底的に文書化された答えで非常によくやった!:)
ワイルドカード

1
@Wildcard、動作は仕様に先行していました。
jlliagre

@ jlliagre、Bill Joyからの回顧録またはおそらくex彼の作成者(彼の名前は知らない)がいない限り、POSIXの仕様は期待どおりに優れていると思います。;)この時点で「元のソース」に最も近い。たとえそれが既存の機能の多かれ少なかれの説明として始まったのは事実だとしても。
ワイルドカード

3
@Wildcard exはBill JoyとChuck Alley(web.cecs.pdx.edu/~kirkenda/joy84.html)によって共同で作成されました。POSIXの仕様に疑問はなく、現在のviリリースがそれに従っているという事実は、単に動作を述べるだけです。ずっと前です。
jlliagre

1

ファイルの最後に改行が追加されるという他の動作は思い出せません(vi80年代半ば以降使用)。

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


私が驚いたのは、改行を追加することです... viが黙って追加しないことを期待していますが、そうなるようです...この態度の説明を探しています(厄介な事実は:foo2を開く(なしちょうどLFを、末尾)と:それは私に何かを示していますが、別のものが保存されます...奇妙な、^^控えめに言ってようWQを、それは...その内容を変更
オリヴィエ・デュラック

前身(ed)では、文字を追加するのではなく、行を作成して編集します。私も、viを行指向のエディターとして考えていました。しかし、私はあなたの驚きを理解しています。
アントン

1

シェルwhileループを介して実行される最終的な改行が不適切に欠けているテキストは、最後の行が静かに破棄されます。

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

究極の改行があることを保証することは、正しく健全で適切なデフォルトです。もう1つのオプションには、最終的な改行を含まないテキストに関係するすべてのシェルコードを認識し、監査する時間、またはテキストの最後の行を失う危険性があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.