Có vi âm thầm thêm một dòng mới (LF) vào cuối tập tin?


36

Tôi gặp khó khăn khi hiểu một hành vi kỳ lạ: vi dường như thêm một dòng mới (ASCII: LF, vì nó là hệ thống Unix ( AIX )) ở cuối tệp, khi tôi KHÔNG gõ cụ thể.

Tôi chỉnh sửa tệp như vậy trong vi (chú ý không nhập dòng mới ở cuối):

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

Tôi hy vọng vi sẽ lưu nó "như hiện tại", vì vậy để có 39 byte: 10 ký tự ASCII trên mỗi ba dòng đầu tiên (số 1 đến 9, theo sau là một dòng mới (LF trên hệ thống của tôi)) và chỉ có 9 dòng cuối cùng dòng (ký tự 1 đến 9, không kết thúc dòng mới / LF).

Nhưng nó xuất hiện khi tôi lưu nó là 40 byte (thay vì 39) và od hiển thị một kết thúc 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?

Nếu tôi tạo tệp với printf thực hiện chính xác những gì tôi đã làm bên trong vi, nó sẽ hoạt động như mong đợi:

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

Cả hai tệp (foo (40 ký tự) và foo2 (39 ký tự) xuất hiện giống hệt nhau nếu tôi mở lại chúng bằng vi ...

Và nếu tôi mở foo2 (39 ký tự, không kết thúc dòng mới) trong vi và chỉ cần làm :wqmà không chỉnh sửa bất cứ điều gì , nó nói rằng nó viết 40 ký tự, và dòng cấp dữ liệu xuất hiện!

Tôi không thể truy cập vào một vi gần đây hơn (tôi làm điều này trên AIX, vi (không phải Vim ) phiên bản 3.10 Tôi nghĩ sao? (Không "đảo ngược" hoặc các phương tiện khác để biết về nó)).

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

Có phải bình thường đối với vi (và có lẽ không phải trong phiên bản gần đây hơn? Hoặc Vim?) Để âm thầm thêm một dòng mới ở cuối tệp? (Tôi nghĩ rằng ~ chỉ ra rằng dòng trước đó KHÔNG kết thúc bằng một dòng mới.)

-

Chỉnh sửa: một số cập nhật bổ sung và một chút tóm tắt, rất cảm ơn các câu trả lời dưới đây:

  • vi âm thầm thêm một dòng mới ở thời điểm nó ghi một tệp thiếu nó (trừ khi tệp trống).

  • nó chỉ làm như vậy tại thời điểm viết! (tức là cho đến khi bạn: w, bạn có thể sử dụng: e để xác minh rằng tệp vẫn còn khi bạn mở nó ... (nghĩa là: nó vẫn hiển thị "tên tệp" [Dòng cuối chưa hoàn thành] N dòng, ký tự M). Khi bạn lưu, một dòng mới được âm thầm thêm vào, không có cảnh báo cụ thể (nó cho biết nó tiết kiệm được bao nhiêu byte, nhưng trong hầu hết các trường hợp không đủ để biết một dòng mới đã được thêm vào) (cảm ơn @jiliagre đã nói chuyện với tôi về mở tin nhắn vi, nó giúp tôi tìm cách biết khi nào sự thay đổi thực sự xảy ra)

  • Đây (chỉnh sửa im lặng) là hành vi POSIX ! (xem câu trả lời @ Barefoot-io để tham khảo)


Chỉ để hoàn thiện, phiên bản nào của AIX (phiên bản đầy đủ).
EightBitTony

2
Tôi không biết vi của AIX có tùy chọn này - chỉ xuất hiện vim
Jeff Schaller

1
@JeffSchaller: thx cho liên kết. Thật không may, vi bản địa không có ": đặt noeol" hoặc thậm chí tùy chọn -b để mở ở chế độ nhị phân ...
Olivier Dulac

1
Bạn có thể có được viphiên bản hoặc ít nhất là một đầu mối về nguồn gốc của nó bằng cách chạy :velệnh.
jlliagre

1
@ThomasDickey Thật vậy. Vì một số lý do, IBM đã gỡ bỏ extrang thủ công nơi :verlệnh thường được ghi lại.
jlliagre

Câu trả lời:


28

Đây là vihành vi dự kiến .

Tệp của bạn có một dòng cuối cùng không đầy đủ vì vậy nói một cách nghiêm túc (nghĩa là theo tiêu chuẩn POSIX), nó không phải là tệp văn bản mà là tệp nhị phân.

vi đó là một trình soạn thảo tệp văn bản, không phải là tệp nhị phân, duyên dáng sửa nó khi bạn lưu nó.

Điều này cho phép các công cụ file văn bản khác như wc, sedvà thích để cung cấp các kết quả mong muốn. Lưu ý rằng vikhông im lặng về vấn đề:


$ 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

Lưu ý, để có được một số manh mối về viphiên bản bạn đang chạy, bạn có thể sử dụng :velệnh. Nó cho thấy ở đây tôi đang sử dụng một SVR4 cũ ở đây, chắc chắn là không vim:

:ve
Version SVR4.0, Solaris 2.5.0

Rõ ràng, bạn đang nói:

:ve
Version 3.10

Điều đó có thể có nghĩa là AIX vidựa trên mã nguồn SVR3.

Trong mọi trường hợp, hành vi này và [Incomplete last line]thông điệp cảnh báo đã có trong vimã nguồn của Bill Joy từ ít nhất là năm 1979 và AFAIK, được giữ lại trong tất cả các nhánh được tạo từ các bản phát hành mã nguồn System V, từ đó Unix được xây dựng độc quyền như AIX.

Nói theo thời gian, hành vi này sau đó không phải là hậu quả của việc tuân thủ POSIX mà là hậu quả của quyết định ban đầu của Bill Joy là hữu ích với người dùng chỉnh sửa các tệp văn bản không có thật, và sau đó, một thập kỷ sau, quyết định của ủy ban POSIX giữ cho dung sai này.

Nếu bạn sử dụng edthay vì vi, bạn sẽ nhận thấy rằng trước đây có nhiều vấn đề về vấn đề này, ít nhất là nếu bạn edđến từ SVR3 hoặc chi nhánh nguồn mới hơn:

$ ed file
'\n' appended
8
q

Cũng lưu ý rằng một tệp trống là một tệp văn bản hợp lệ xảy ra để chứa các dòng không. Vì sau đó không có dòng nào bị lỗi để sửa, vikhông nối thêm dòng mới khi lưu tệp.


1
Tôi tin rằng bạn nhầm vim với vi;) di sản vi ít dài dòng hơn thế này ...
Olivier Dulac

@OlivierDulac Tôi không nhầm lẫn họ. Thử nghiệm này đã được thực hiện bằng cách sử dụng di sản SVR4 vigiống như OP, mặc dù trên một Unix khác. Đây không phải là vimhoặc một bản sao khác. Trả lời cập nhật để làm rõ điều này.
jlliagre

@OlivierDulac Hmm, tôi chỉ nhận thấy bạn thực sự là OP. Có vẻ như AIX đang sử dụng một nhánh System V cũ hơn để vithực hiện. Có thể là SVR3. Bạn có chắc chắn không có [Incomplete last line]tin nhắn khi bạn mở tập tin?
jlliagre

@OlivierDulac Liên kết này dường như ngụ ý thông điệp này rất tương tự có thể được hiển thị bằng AIX vithực hiện: www-01.ibm.com/support/docview.wss?uid=isg1IZ27694
jlliagre

Tôi sẽ cố gắng để thấy điều này vào ngày mai
Olivier Dulac

51

POSIX yêu cầu hành vi này, vì vậy nó không phải là bất thường.

Từ hướng dẫn sử dụng POSIX vi :

ĐẦU VÀO PHIM

Xem phần INPUT FILES của lệnh ex để biết mô tả về các tệp đầu vào được hỗ trợ bởi lệnh vi.

Theo dấu vết đến sổ tay POSIX ex :

ĐẦU VÀO PHIM

Các tệp đầu vào phải là các tệp văn bản hoặc các tệp sẽ là các tệp văn bản ngoại trừ một dòng cuối cùng không đầy đủ không dài hơn {LINE_MAX} -1 byte và không chứa các ký tự NUL. Theo mặc định, bất kỳ dòng cuối cùng không đầy đủ sẽ được xử lý như thể nó có dấu <newline>. Việc chỉnh sửa các dạng tệp khác có thể tùy ý được cho phép bởi các triển khai cũ.

Phần OUTPUT FILES của hướng dẫn vi cũng chuyển hướng đến ex:

PHIM ĐẦU RA

Đầu ra từ ex sẽ là tập tin văn bản.

Một cặp định nghĩa POSIX:

3.397 tệp văn bản

Một tệp chứa các ký tự được tổ chức thành không hoặc nhiều dòng. Các dòng không chứa các ký tự NUL và không có ký tự nào có thể vượt quá {LINE_MAX} byte, bao gồm cả ký tự <newline>. Mặc dù POSIX.1-2008 không phân biệt giữa tệp văn bản và tệp nhị phân (xem tiêu chuẩn ISO C), nhiều tiện ích chỉ tạo ra đầu ra có thể dự đoán hoặc có ý nghĩa khi hoạt động trên tệp văn bản. Các tiện ích tiêu chuẩn có các hạn chế như vậy luôn chỉ định "tệp văn bản" trong phần STDIN hoặc INPUT PHIM.

3.206 Line

Một chuỗi gồm 0 hoặc nhiều ký tự không phải <dòng mới> cộng với ký tự <dòng mới> kết thúc.

Các định nghĩa này trong ngữ cảnh của các trích đoạn trang thủ công này có nghĩa là trong khi triển khai ex / vi tuân thủ phải chấp nhận tệp văn bản không đúng định dạng nếu biến dạng duy nhất của tệp đó là một dòng mới cuối cùng vắng mặt, khi viết bộ đệm của tệp đó, kết quả phải là tệp văn bản hợp lệ.

Mặc dù bài đăng này đã tham chiếu phiên bản 2013 của tiêu chuẩn POSIX, các quy định có liên quan cũng xuất hiện trong phiên bản 1997 cũ hơn nhiều .

Cuối cùng, nếu bạn thấy việc bổ sung dòng mới của ex không được chào đón, bạn sẽ cảm thấy bị vi phạm sâu sắc bởi phiên bản không khoan dung của Seventh Edition UNIX (1979). Từ hướng dẫn :

Khi đọc tệp, ed sẽ loại bỏ các ký tự ASCII NUL và tất cả các ký tự sau dòng mới nhất. Nó từ chối đọc các tệp chứa các ký tự không phải ASCII.


cảm ơn, điều đó không trả lời câu hỏi của tôi Tôi sẽ đợi thêm một vài ngày nữa trong trường hợp một số câu trả lời tốt hơn, nhưng ngay bây giờ tôi cảm thấy bạn có thể là câu trả lời được chấp nhận.
Olivier Dulac

Thực hiện rất tốt trên câu trả lời được ghi chép kỹ lưỡng, trực tiếp từ thông số kỹ thuật! :)
tự đại diện

1
@Wildcard, hành vi đi trước thông số kỹ thuật mặc dù.
jlliagre

@jlliagre, trừ khi bạn có một cuốn hồi ký từ Bill Joy hoặc có lẽ là người tạo ra ex(không biết tên anh ấy), tôi nghĩ thông số kỹ thuật POSIX tốt như có thể mong đợi. ;) Gần nhất với "nguồn gốc" vào thời điểm này, mặc dù đúng là họ đã bắt đầu như mô tả ít nhiều về chức năng hiện có.
tự đại diện

3
@Wildcard exđược Bill Joy và Chuck Alley đồng sáng tác ( web.cecs.pdx.edu/~kirkenda/joy84.html .) Tôi không đặt câu hỏi về thông số kỹ thuật POSIX và các vibản phát hành hiện tại thực hiện theo nó, tôi chỉ nêu hành vi từ lâu nó.
jlliagre

1

Tôi không nhớ bất kỳ hành vi nào khác mà một dòng mới được thêm vào cuối tệp (sử dụng vitừ giữa những năm 80).

Điều này ~cho biết rằng một dòng trên màn hình không phải là một phần của văn bản, không phải là tệp không kết thúc trong một dòng mới. (Bạn có thể gặp khó khăn trong việc theo dõi lỗi nếu bạn đặt một ~dòng script shell cuối cùng). Nếu bạn tải một tệp ngắn với một dòng mới ở cuối, bạn sẽ thấy ~chính mình và chứng minh rằng suy nghĩ của bạn cho biết văn bản không kết thúc dòng mới.


Điều làm tôi ngạc nhiên là việc bổ sung một dòng mới ... Tôi hy vọng vi sẽ không thêm nó một cách im lặng, nhưng có vẻ như nó ... Tôi đang tìm một lời giải thích về thái độ này (sự thật đáng lo ngại là: Tôi mở foo2 (không có theo dõi LF) và chỉ: wq, nó THAY ĐỔI nội dung của nó ... vì vậy nó cho tôi thấy một thứ nhưng lại tiết kiệm một thứ khác ... kỳ lạ, phải nói là ít nhất ^^
Olivier Dulac

trong tiền thân của nó ( ed) bạn sẽ tạo các dòng và chỉnh sửa chúng, không phải bằng cách nối thêm các ký tự. Tôi luôn nghĩ về vi như là một biên tập viên định hướng dòng là tốt. Nhưng tôi hiểu sự ngạc nhiên của bạn.
Anthon

1

Văn bản thiếu chính xác dòng mới cuối cùng chạy qua whilevòng lặp shell dẫn đến dòng cuối cùng bị loại bỏ trong âm thầm.

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

Đảm bảo rằng có một dòng mới cuối cùng là quyền mặc định đúng đắn và đúng đắn. Tùy chọn khác liên quan đến việc biết và có thời gian để kiểm tra tất cả các mã shell chạm vào văn bản thiếu dòng mới cuối cùng hoặc có nguy cơ mất dòng cuối cùng của văn bản.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.