Apakah vi diam-diam menambahkan baris baru (LF) di akhir file?


36

Saya mengalami kesulitan memahami perilaku aneh: vi tampaknya menambahkan baris baru (ASCII: LF, karena ini adalah sistem Unix ( AIX )) di akhir file, ketika saya TIDAK secara spesifik mengetiknya.

Saya mengedit file seperti itu di vi (berhati-hati untuk tidak memasukkan baris baru di akhir):

# 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.

Saya berharap vi untuk menyimpannya "sebagaimana adanya", sehingga memiliki 39 byte: 10 karakter ASCII pada masing-masing dari tiga baris pertama (angka 1 hingga 9, diikuti oleh baris baru (LF pada sistem saya)) dan hanya 9 pada yang terakhir baris (karakter 1 hingga 9, tidak ada penghentian baris baru / LF).

Tapi itu muncul ketika saya menyimpannya 40 byte (bukan 39), dan od menunjukkan LF terminating :

# 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?

Jika saya membuat file dengan printf melakukan apa yang saya lakukan di dalam vi, itu berfungsi seperti yang diharapkan:

# ## 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.

Kedua file (foo (40 karakter) dan foo2 (39 karakter) tampak persis sama jika saya membukanya kembali dengan ...

Dan jika saya membuka foo2 (39 karakter, tidak ada penghentian baris baru) di vi dan hanya melakukan :wqtanpa mengeditnya , ia mengatakan itu menulis 40 karakter, dan linefeed muncul!

Saya tidak dapat memiliki akses ke vi yang lebih baru (saya melakukan ini pada AIX, vi (bukan Vim ) versi 3.10 saya pikir? (Tidak ada "-versi" atau cara lain untuk mengetahuinya)).

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

Apakah normal untuk vi (dan mungkin tidak dalam versi yang lebih baru? Atau Vim?) Untuk menambahkan baris baru secara diam-diam di akhir file? (Saya pikir ~ mengindikasikan bahwa baris sebelumnya TIDAK diakhiri dengan baris baru.)

-

Sunting: beberapa pembaruan tambahan dan sedikit ringkasan, dengan banyak terima kasih atas jawaban di bawah ini:

  • vi secara diam-diam menambahkan baris baru pada saat itu menulis file yang tidak memilikinya (kecuali file kosong).

  • itu hanya terjadi pada saat penulisan! (yaitu, sampai Anda: w, Anda dapat menggunakan: e untuk memverifikasi bahwa file tersebut masih saat Anda membukanya ... (yaitu: itu masih menunjukkan "nama file" [Baris terakhir tidak lengkap] N baris, karakter M). Ketika Anda menyimpan, baris baru ditambahkan secara diam-diam, tanpa peringatan khusus (ia mengatakan berapa banyak byte yang disimpannya, tetapi dalam kebanyakan kasus tidak cukup untuk mengetahui baris baru ditambahkan) (terima kasih kepada @jiliagre karena berbicara dengan saya tentang membuka pesan vi, itu membantu saya menemukan cara untuk mengetahui kapan perubahan itu benar-benar terjadi)

  • Ini (koreksi diam-diam) adalah perilaku POSIX ! (lihat jawaban @ barefoot-io untuk referensi)


Hanya untuk kelengkapan, versi AIX mana (versi lengkap).
EightBitTony

2
Saya tidak mengetahui vi AIX yang memiliki opsi ini - hanya muncul vim
Jeff Schaller

1
@ JeffSchaller: thx untuk tautannya. Sayangnya vi asli tidak memiliki ": set noeol" atau bahkan opsi -b untuk membuka dalam mode biner ...
Olivier Dulac

1
Anda mungkin bisa mendapatkan viversi atau setidaknya petunjuk tentang asalnya dengan menjalankan :veperintah.
jlliagre

1
@ThomasDickey Memang. Untuk beberapa alasan, IBM membuka exhalaman buku panduan tempat :verperintah biasanya didokumentasikan.
jlliagre

Jawaban:


28

Inilah viperilaku yang diharapkan .

File Anda memiliki baris terakhir yang tidak lengkap sehingga benar-benar berbicara (yaitu sesuai dengan standar POSIX), itu bukan file teks tetapi file biner.

vi yang merupakan editor file teks, bukan biner, memperbaikinya dengan anggun saat Anda menyimpannya.

Ini memungkinkan alat file teks lain suka wc, seddan suka untuk memberikan hasil yang diharapkan. Perhatikan bahwa vitidak ada masalah tentang masalah ini:


$ 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

Catatan, untuk mendapatkan beberapa petunjuk tentang viversi apa yang Anda jalankan, Anda dapat menggunakan :veperintah. Ini menunjukkan di sini saya menggunakan satu warisan SVR4 di sini, pasti tidak vim:

:ve
Version SVR4.0, Solaris 2.5.0

Rupanya, milik Anda menyatakan:

:ve
Version 3.10

Itu kemungkinan berarti AIX vididasarkan pada kode sumber SVR3.

Bagaimanapun, perilaku ini, dan [Incomplete last line]pesan peringatan telah ada dalam vikode sumber warisan Bill Joy sejak setidaknya 1979 dan AFAIK, dipertahankan di semua cabang yang dibuat dari rilis kode sumber Sistem V, dari mana Unix yang dipatenkan seperti AIX dibuat.

Berbicara secara kronologis, perilaku ini kemudian bukan konsekuensi dari kesesuaian POSIX tetapi lebih merupakan konsekuensi dari keputusan awal Bill Joy untuk membantu pengguna mengedit file teks palsu, dan kemudian, satu dekade kemudian, komite POSIX memutuskan untuk mempertahankan toleransi ini.

Jika Anda menggunakan edalih-alih vi, Anda akan melihat bahwa yang pertama lebih bertele-tele tentang masalah ini, setidaknya jika Anda edberasal dari SVR3 atau cabang sumber yang lebih baru:

$ ed file
'\n' appended
8
q

Perhatikan juga bahwa file kosong adalah file teks yang valid yang berisi garis nol. Karena tidak ada baris yang tidak ditentukan untuk diperbaiki, vitidak menambahkan baris baru saat menyimpan file.


1
Saya yakin Anda salah mengira vim untuk vi;) legacy vi jauh lebih sedikit bertele-tele daripada ini ...
Olivier Dulac

@OlivierDulac Saya tidak membingungkan mereka. Tes ini dilakukan dengan menggunakan warisan SVR4 viseperti halnya OP, meskipun pada Unix yang berbeda. Ini bukan vimatau klon lain. Jawaban diperbarui untuk memperjelas ini.
jlliagre

@ OlivierDulac Hmm, saya baru tahu Anda sebenarnya OP. Tampaknya AIX menggunakan cabang System V yang lebih lama untuk viimplementasinya. Mungkin SVR3. Apakah Anda yakin tidak ada [Incomplete last line]pesan saat Anda membuka file?
jlliagre

@OlivierDulac Tautan ini tampaknya menyiratkan pesan yang sama ini dapat ditampilkan oleh viimplementasi AIX : www-01.ibm.com/support/docview.wss?uid=isg1IZ27694
jlliagre

Saya akan mencoba melihat ini besok
Olivier Dulac

51

POSIX membutuhkan perilaku ini, jadi tidak aneh.

Dari manual POSIX vi :

FILE INPUT

Lihat bagian INPUT FILES dari perintah ex untuk deskripsi file input yang didukung oleh perintah vi.

Mengikuti jejak ke manual ex POSIX :

FILE INPUT

File input harus berupa file teks atau file yang akan menjadi file teks kecuali untuk baris terakhir yang tidak lengkap yang tidak lebih dari {LINE_MAX} -1 byte panjangnya dan tidak mengandung karakter NUL. Secara default, setiap baris terakhir yang tidak lengkap harus diperlakukan seolah-olah ia memiliki <newline> tertinggal. Pengeditan bentuk file lain secara opsional diizinkan oleh implementasi ex.

Bagian OUTPUT FILES dari manual vi juga mengarahkan ke ex:

FILE OUTPUT

Output dari ex adalah file teks.

Sepasang definisi POSIX:

3.397 File Teks

File yang berisi karakter yang disusun dalam nol atau lebih baris. Baris tidak mengandung karakter NUL dan panjangnya tidak boleh melebihi {LINE_MAX} byte, termasuk karakter <newline>. Meskipun POSIX.1-2008 tidak membedakan antara file teks dan file biner (lihat standar ISO C), banyak utilitas hanya menghasilkan output yang dapat diprediksi atau bermakna ketika beroperasi pada file teks. Utilitas standar yang memiliki batasan seperti itu selalu menentukan "file teks" di bagian STDIN atau INPUT FILES.

3.206 Baris

Urutan nol atau lebih karakter <newline> plus karakter terminasi <newline>.

Definisi-definisi ini dalam konteks kutipan halaman buku panduan ini berarti bahwa sementara implementasi ex / konforman harus menerima file teks yang salah jika hanya deformitas file tersebut merupakan baris akhir yang tidak ada, ketika menulis buffer file tersebut hasilnya harus berupa file teks yang valid.

Sementara posting ini telah merujuk pada edisi 2013 dari standar POSIX, ketentuan yang relevan juga muncul dalam edisi 1997 yang jauh lebih tua .

Terakhir, jika Anda menemukan bahwa penayangan iklan baris baru mantan tidak diterima, Anda akan merasa sangat dilanggar oleh Seventh Edition UNIX's (1979) intolerant ed. Dari manual :

Saat membaca file, ed membuang ASCII NUL karakter dan semua karakter setelah baris baru terakhir. Itu menolak untuk membaca file yang mengandung karakter non-ASCII.


terima kasih, itu menjawab pertanyaan saya. Saya hanya akan menunggu beberapa hari lagi kalau-kalau ada jawaban yang lebih baik muncul, tetapi sekarang saya merasa Anda bisa menjadi jawaban yang diterima.
Olivier Dulac

Sangat baik dilakukan pada jawaban yang terdokumentasi secara menyeluruh, langsung dari spesifikasi! :)
Wildcard

1
@Wildcard, perilaku mendahului spesifikasi.
jlliagre

@ jlliagre, kecuali Anda memiliki memoar dari Bill Joy atau mungkin pencipta ex(tidak tahu namanya), saya pikir spesifikasi POSIX sebagus yang bisa diharapkan. ;) Paling dekat dengan "sumber asli" pada titik ini, meskipun memang benar mereka mulai lebih atau kurang sebagai deskripsi fungsi yang ada.
Wildcard

3
@Wildcard exditulis bersama oleh Bill Joy dan Chuck Alley ( web.cecs.pdx.edu/~kirkenda/joy84.html .) Saya tidak mempertanyakan spesifikasi POSIX dan fakta virilis saat ini memang mengikutinya, saya hanya menyatakan perilakunya lama sebelum itu.
jlliagre

1

Saya tidak ingat perilaku lain yang ditambahkan baris baru di akhir file (menggunakan visejak pertengahan 80-an).

The ~menunjukkan bahwa garis di layar yang bukan bagian dari teks, bukan bahwa file tersebut tidak berakhir di baris baru. (Anda bisa mendapatkan kesulitan untuk melacak kesalahan jika Anda menempatkan ~pada baris terakhir skrip shell). Jika Anda memuat file pendek dengan baris baru di akhir, Anda akan melihat ~sendiri dan menyangkal bahwa menurut Anda itu menunjukkan teks yang tidak diakhiri baris baru.


yang mengejutkan saya adalah penambahan baris baru ... Saya berharap vi untuk tidak menambahkannya secara diam-diam, tetapi tampaknya memang demikian ... Saya sedang mencari penjelasan tentang sikap ini (fakta yang meresahkan adalah: Saya membuka foo2 (tanpa Trailing LF) dan hanya: wq, itu MENGUBAH isinya ... jadi itu menunjukkan sesuatu tetapi menyelamatkan hal lain ... aneh, untuk sedikitnya ^^
Olivier Dulac

di pendahulunya ( ed) Anda akan membuat garis dan mengeditnya, bukan dengan menambahkan karakter. Saya selalu berpikir tentang vi sebagai editor berorientasi garis juga. Tapi aku mengerti keterkejutanmu.
Anthon

1

Teks yang tidak memiliki baris baris terakhir yang dijalankan melalui shell whileloop akan mengakibatkan baris terakhir dibuang secara diam-diam.

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

Memastikan bahwa ada baris baru utama adalah standar yang benar dan waras dan tepat. Opsi lainnya melibatkan mengetahui dan memiliki waktu untuk mengaudit semua kode shell yang menyentuh teks yang tidak memiliki baris akhir, atau mengambil risiko kehilangan baris terakhir teks.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.