Làm cách nào để thêm một dòng mới vào cuối tập tin?


190

Sử dụng các hệ thống kiểm soát phiên bản tôi cảm thấy khó chịu với tiếng ồn khi diff nói No newline at end of file.

Vì vậy, tôi đã tự hỏi: Làm thế nào để thêm một dòng mới vào cuối một tập tin để loại bỏ những tin nhắn?



1
Giải pháp tốt đẹp dưới đây mà vệ sinh tất cả các tập tin đệ quy. Trả lời bởi @Patrick Oscarity
Qwerty


Trong tương lai, các biên tập viên văn bản thường có các tùy chọn để đảm bảo có một dòng mới mà bạn và cộng tác viên của bạn có thể sử dụng để giữ sạch sẽ.
Nick T

Câu trả lời:


44

Để vệ sinh đệ quy một dự án tôi sử dụng oneliner này:

git ls-files -z | while IFS= read -rd '' f; do tail -c1 < "$f" | read -r _ || echo >> "$f"; done

Giải trình:

  • git ls-files -zliệt kê các tập tin trong kho lưu trữ. Nó lấy một mẫu tùy chọn làm tham số bổ sung có thể hữu ích trong một số trường hợp nếu bạn muốn hạn chế thao tác đối với các tệp / thư mục nhất định. Thay vào đó, bạn có thể sử dụng find -print0 ...hoặc các chương trình tương tự để liệt kê các tệp bị ảnh hưởng - chỉ cần đảm bảo rằng nó phát ra NULcác mục được phân tách.

  • while IFS= read -rd '' f; do ... done Lặp lại thông qua các mục, xử lý tên tệp một cách an toàn bao gồm khoảng trắng và / hoặc dòng mới.

  • tail -c1 < "$f" đọc char cuối cùng từ một tập tin.

  • read -r _ thoát với trạng thái thoát khác nếu thiếu một dòng mới.

  • || echo >> "$f" nối thêm một dòng mới vào tệp nếu trạng thái thoát của lệnh trước đó là khác không.


Bạn cũng có thể làm như thế này nếu bạn muốn vệ sinh một tập hợp con các tệp của mình:find -name \*.java | while read f; do tail -n1 $f | read -r _ || echo >> $f; done
Per Lundberg

@ StéphaneChazelas đề xuất tốt, sẽ cố gắng kết hợp điều này vào câu trả lời của tôi.
Patrick Oscarity

@PerLundberg bạn cũng có thể chuyển một mẫu git ls-filesmà vẫn sẽ cứu bạn khỏi việc chỉnh sửa các tệp không được theo dõi trong kiểm soát phiên bản.
Patrick Oscarity

@ StéphaneChazelas thêm vào IFS= để bỏ đặt dấu phân cách là tốt để duy trì khoảng trắng xung quanh. Các mục bị chấm dứt null chỉ có liên quan nếu bạn có tệp hoặc thư mục có dòng mới trong tên của chúng, có vẻ như rất xa, nhưng là cách chính xác hơn để xử lý trường hợp chung, tôi đồng ý. Cũng như một cảnh báo nhỏ: -dtùy chọn readkhông khả dụng trong POSIX sh.
Patrick Oscarity

Vâng, do đó zsh / bash của tôi . Xem thêm cách sử dụng của tôi tail -n1 < "$f"để tránh các vấn đề với tên tệp bắt đầu -( tail -n1 -- "$f"không hoạt động đối với tệp được gọi -). Bạn có thể muốn làm rõ rằng câu trả lời bây giờ là zsh / bash cụ thể.
Stéphane Chazelas

202

Ở đây bạn đi :

sed -i -e '$a\' file

Và thay thế cho OS X sed:

sed -i '' -e '$a\' file

Điều này thêm \nvào cuối của tập tin duy nhất nếu nó chưa kết thúc với một dòng mới. Vì vậy, nếu bạn chạy nó hai lần, nó sẽ không thêm một dòng mới:

$ 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

1
@jwd: Từ man sed: $ Match the last line.Nhưng có lẽ nó chỉ hoạt động một cách tình cờ. Giải pháp của bạn cũng hoạt động.
l0b0

1
Giải pháp của bạn cũng thanh lịch hơn, và tôi đã thử nghiệm và cam kết , nhưng làm thế nào để nó hoạt động? Nếu $khớp với dòng cuối cùng, tại sao nó không thêm một dòng mới vào một chuỗi đã chứa một dòng mới?
l0b0

27
Có hai ý nghĩa khác nhau của $. Bên trong một biểu thức chính, chẳng hạn như với biểu mẫu /<regex>/, nó có nghĩa "kết thúc dòng phù hợp" thông thường. Mặt khác, được sử dụng như một địa chỉ, sed cung cấp cho nó ý nghĩa "dòng cuối cùng trong tệp" đặc biệt. Mã này hoạt động vì sed theo mặc định sẽ thêm một dòng mới vào đầu ra của nó nếu nó chưa có ở đó. Mã "$ a \" chỉ nói "khớp với dòng cuối cùng của tệp và không thêm gì vào nó." Nhưng mặc nhiên, sed thêm dòng mới vào mỗi dòng mà nó xử lý (chẳng hạn như $dòng này ) nếu nó chưa có ở đó.
jwd

1
Về trang chủ: Câu trích dẫn mà bạn đang đề cập nằm trong phần "Địa chỉ". Đặt nó bên trong /regex/mang lại cho nó một ý nghĩa khác. Các trang web của FreeBSD có nhiều thông tin hơn, tôi nghĩ: freebsd.org/cgi/man.cgi?query=sed
jwd

2
Nếu tệp đã kết thúc trong một dòng mới, điều này sẽ không thay đổi nó, nhưng nó sẽ viết lại và cập nhật dấu thời gian của nó. Điều đó có thể hoặc không quan trọng.
Keith Thompson

39

Có một cái nhìn:

$ echo -n foo > foo 
$ cat foo
foo$
$ echo "" >> foo
$ cat foo
foo

vì thế echo "" >> noeol-filenên làm các trick. (Hoặc bạn có nghĩa là yêu cầu xác định các tệp này sửa chúng?)

chỉnh sửa đã xóa ""từ echo "" >> foo(xem bình luận của @ yuyichao) edit2 đã thêm ""lại ( nhưng xem bình luận của @Keith Thompson)


4
các ""là không cần thiết (ít nhất là cho bash) và tail -1 | wc -lcó thể được sử dụng để tìm ra các tập tin mà không có một dòng mới vào cuối
yuyichao

5
@yuyichao: ""Không cần thiết cho bash, nhưng tôi đã thấy các echotriển khai không in gì khi được gọi mà không có đối số (mặc dù không có cái nào tôi có thể tìm thấy bây giờ làm điều này). echo "" >> noeol-filecó lẽ là mạnh mẽ hơn một chút. printf "\n" >> noeol-filethậm chí còn hơn thế
Keith Thompson

2
@KeithThompson, csh's echolà một trong những được biết đến đầu ra gì khi không thông qua bất kỳ cuộc tranh cãi. Nhưng sau đó nếu chúng ta sẽ hỗ trợ vỏ phi Bourne-như thế nào, chúng ta nên làm cho nó echo ''thay vì echo ""như echo ""sẽ ouput ""<newline>với rchoặc esví dụ.
Stéphane Chazelas

1
@ StéphaneChazelas: Và tcsh, không giống như csh, in một dòng mới khi được gọi mà không có đối số - bất kể cài đặt của $echo_style.
Keith Thompson

16

Một giải pháp khác sử dụng ed. Giải pháp này chỉ ảnh hưởng đến dòng cuối cùng và chỉ khi \nbị thiếu:

ed -s file <<< w

Về cơ bản, nó hoạt động mở tệp để chỉnh sửa thông qua một tập lệnh, tập lệnh là wlệnh đơn , ghi tập tin trở lại đĩa. Nó được dựa trên câu này được tìm thấy trong ed(1)trang người đàn ông:

GIỚI HẠN
       (...)

       Nếu một tệp văn bản (không nhị phân) không bị chấm dứt bởi một ký tự dòng mới,
       sau đó ed nối thêm một vào việc đọc / viết nó. Trong trường hợp nhị phân
       tập tin, ed không nối thêm một dòng mới về đọc / viết.

1
Điều này không thêm một dòng mới cho tôi.
Olhovsky

4
Làm việc cho tôi; nó thậm chí còn in "Dòng mới được nối thêm" (ed-1.10-1 trên Arch Linux).
Stefan Majewsky 10/03/2015

12

Một cách đơn giản, di động, tuân thủ POSIX để thêm một dòng mới, vắng mặt cuối cùng vào một tệp văn bản:

[ -n "$(tail -c1 file)" ] && echo >> file

Cách tiếp cận này không cần phải đọc toàn bộ tập tin; nó chỉ đơn giản là có thể tìm kiếm EOF và làm việc từ đó.

Cách tiếp cận này cũng không cần tạo các tệp tạm thời phía sau lưng (ví dụ: sed -i), vì vậy các liên kết cứng không bị ảnh hưởng.

echo chỉ nối một dòng mới vào tệp khi kết quả của lệnh thay thế là một chuỗi không trống. Lưu ý rằng điều này chỉ có thể xảy ra nếu tệp không trống và byte cuối cùng không phải là dòng mới.

Nếu byte cuối cùng của tệp là một dòng mới, đuôi sẽ trả về nó, sau đó lệnh thay thế sẽ loại bỏ nó; kết quả là một chuỗi rỗng. Thử nghiệm -n thất bại và echo không chạy.

Nếu tệp trống, kết quả của việc thay thế lệnh cũng là một chuỗi trống và tiếng vang lại không chạy. Điều này là mong muốn, bởi vì một tệp trống không phải là tệp văn bản không hợp lệ, cũng không tương đương với tệp văn bản không trống với một dòng trống.


1
Lưu ý rằng nó không hoạt động yashnếu ký tự cuối cùng trong tệp là ký tự nhiều byte (ví dụ, trong ngôn ngữ UTF-8) hoặc nếu miền địa phương là C và byte cuối cùng trong tệp có tập bit thứ 8. Với các shell khác (trừ zsh), nó sẽ không thêm dòng mới nếu tệp kết thúc bằng byte NUL (nhưng sau đó, điều đó có nghĩa là đầu vào sẽ không phải là văn bản ngay cả sau khi thêm một dòng mới).
Stéphane Chazelas


1
Có thể chạy cái này cho mọi tập tin trong một thư mục và thư mục con không?
Qwerty

12

Thêm dòng mới bất kể:

echo >> filename

Đây là một cách để kiểm tra xem một dòng mới có tồn tại ở cuối trước khi thêm một dòng hay không, bằng cách sử dụng Python:

f=filename; python -c "import sys; sys.exit(open(\"$f\").read().endswith('\n'))" && echo >> $f

1
Tôi sẽ không sử dụng phiên bản python trong bất kỳ loại vòng lặp nào vì thời gian khởi động python chậm. Tất nhiên bạn có thể thực hiện vòng lặp trong python nếu bạn muốn.
Kevin Cox

2
Thời gian khởi động cho Python là 0,03 giây tại đây. Bạn có thực sự coi đó là vấn đề?
Alexander

3
Thời gian khởi động không thành vấn đề nếu bạn gọi python trong một vòng lặp, đó là lý do tại sao tôi nói hãy xem xét thực hiện vòng lặp trong python. Sau đó, bạn chỉ phải chịu chi phí khởi động một lần. Đối với tôi, một nửa chi phí khởi nghiệp là hơn một nửa thời gian của toàn bộ snipit, tôi sẽ xem xét chi phí đáng kể đó. (Một lần nữa, không liên quan nếu chỉ thực hiện một số lượng nhỏ tệp)
Kevin Cox

2
echo ""dường như trở nên mạnh mẽ hơn echo -n '\n'. Hoặc bạn có thể sử dụngprintf '\n'
Keith Thompson

2
Điều này làm việc tốt cho tôi
Daniel Gomez Rico

8

Giải pháp nhanh nhất là:

[ -n "$(tail -c1 file)" ] && printf '\n' >>file 

  1. Thực sự rất nhanh.
    Trên một tệp kích thước trung bình, seq 99999999 >fileviệc này mất vài giây.
    Các giải pháp khác mất nhiều thời gian:

    [ -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
    
  2. Hoạt động trong tro, bash, lksh, mksh, ksh93, attsh và zsh nhưng không yash.

  3. Không thay đổi dấu thời gian của tệp nếu không cần thêm dòng mới.
    Tất cả các giải pháp khác được trình bày ở đây thay đổi dấu thời gian của tệp.
  4. Tất cả các giải pháp trên là POSIX hợp lệ.

Nếu bạn cần một giải pháp di động để yash (và tất cả các shell khác được liệt kê ở trên), nó có thể phức tạp hơn một chút:

f=file
if       [ "$(tail -c1 "$f"; echo x)" != "$(printf '\nx')" ]
then     printf '\n' >>"$f"
fi

7

Cách nhanh nhất để kiểm tra nếu byte cuối cùng của tệp là một dòng mới là chỉ đọc byte cuối cùng đó. Điều đó có thể được thực hiện với tail -c1 file. Tuy nhiên, cách đơn giản để kiểm tra xem giá trị byte có phải là một dòng mới hay không, tùy thuộc vào lớp vỏ thông thường loại bỏ một dòng mới bên trong mở rộng lệnh không thành công (ví dụ) trong yash, khi ký tự cuối cùng trong tệp là UTF- 8 giá trị.

Cách chính xác, tuân thủ POSIX, tất cả (hợp lý) để tìm nếu byte cuối cùng của tệp là một dòng mới là sử dụng xxd hoặc hexdump:

tail -c1 file | xxd -u -p
tail -c1 file | hexdump -v -e '/1 "%02X"'

Sau đó, so sánh đầu ra ở trên 0Asẽ cung cấp một bài kiểm tra mạnh mẽ.
Nó rất hữu ích để tránh thêm một dòng mới vào một tập tin trống.
Tất nhiên, tệp sẽ không cung cấp ký tự cuối cùng 0Acủa:

f=file
a=$(tail -c1 "$f" | hexdump -v -e '/1 "%02X"')
[ -s "$f" -a "$a" != "0A" ] && echo >> "$f"

Ngắn và ngọt. Điều này mất rất ít thời gian vì nó chỉ đọc byte cuối cùng (tìm đến EOF). Nó không quan trọng nếu tập tin lớn. Sau đó chỉ thêm một byte nếu cần.

Không có tập tin tạm thời cần thiết cũng không được sử dụng. Không có liên kết cứng bị ảnh hưởng.

Nếu thử nghiệm này được chạy hai lần, nó sẽ không thêm một dòng mới.


1
@crw Tôi tin rằng nó bổ sung thông tin hữu ích.
sorontar

2
Lưu ý rằng các tiện ích POSIX xxdcũng không hexdump. Trong công cụ POSIX, có od -An -tx1giá trị hex của một byte.
Stéphane Chazelas

@ StéphaneChazelas Vui lòng gửi câu trả lời đó; Tôi đã đến đây để tìm kiếm bình luận này quá nhiều lần :)
kelvin

@kelvin, tôi đã cập nhật câu trả lời của mình
Stéphane Chazelas

Lưu ý rằng POSIX không đảm bảo giá trị của LF là 0x0a. Vẫn còn các hệ thống POSIX không có (các hệ thống dựa trên EBCDIC) mặc dù chúng cực kỳ hiếm ngày nay.
Stéphane Chazelas

4

Tốt hơn hết là bạn nên sửa trình soạn thảo của người dùng đã chỉnh sửa tệp lần cuối. Nếu bạn là người cuối cùng đã chỉnh sửa tệp - bạn đang sử dụng trình chỉnh sửa nào, tôi đoán là bạn đồng hành ..?


2
Vim là biên tập viên trong câu hỏi. Nhưng nói chung, bạn đã đúng, tôi không nên chỉ sửa các sympton;)
k0pernikus

6
đối với vim, bạn phải tránh đường và thực hiện điệu nhảy nhị phân khi lưu tệp để vim không thêm dòng mới vào cuối tệp - chỉ không thực hiện điệu nhảy đó. HOẶC, để chỉ cần sửa các tệp hiện có, hãy mở chúng trong vim và lưu tệp và vim sẽ 'sửa' dòng mới bị thiếu cho bạn (có thể dễ dàng viết kịch bản cho nhiều tệp)
AD7six

3
Tôi emacskhông thêm một dòng mới vào cuối tập tin.
enzotib

2
Cảm ơn vì nhận xét @ AD7six, tôi tiếp tục nhận được báo cáo ảo từ các khác biệt khi tôi cam kết mọi thứ, về cách tệp gốc không có dòng mới ở cuối. Bất kể tôi chỉnh sửa tệp bằng vim như thế nào, tôi không thể lấy nó để không đặt dòng mới ở đó. Vì vậy, nó chỉ là vim làm điều đó.
Steven Lu

1
@enzotib: Tôi có (setq require-final-newline 'ask)trong.emacs
Keith Thompson

3

Nếu bạn chỉ muốn nhanh chóng thêm một dòng mới khi xử lý một số đường ống, hãy sử dụng điều này:

outputting_program | { cat ; echo ; }

nó cũng tuân thủ POSIX.

Sau đó, tất nhiên, bạn có thể chuyển hướng nó đến một tập tin.


2
Thực tế là tôi có thể sử dụng điều này trong một đường ống là hữu ích. Điều này cho phép tôi đếm số lượng hàng trong tệp CSV, ngoại trừ tiêu đề. Và nó giúp có được số lượng dòng chính xác trên các tệp windows không kết thúc bằng dòng hoàn trả mới hoặc dòng vận chuyển. cat file.csv | tr "\r" "\n" | { cat; echo; } | sed "/^[[:space:]]*$/d" | tail -n +2 | wc -l
Kyle Tolle

3

Miễn là không có null trong đầu vào:

paste - <>infile >&0

... Sẽ đủ để luôn luôn nối một dòng mới vào phần cuối của một kẻ lưu manh nếu nó chưa có. Và nó chỉ cần đọc tệp đầu vào trong một lần để làm cho đúng.


Điều đó sẽ không hoạt động như vậy vì stdin và stdout chia sẻ cùng một mô tả tệp mở (vì vậy con trỏ trong tệp). Bạn cần paste infile 1<> infilethay thế.
Stéphane Chazelas

2

Mặc dù nó không trả lời trực tiếp câu hỏi, nhưng đây là một đoạn script liên quan tôi đã viết để phát hiện các tệp không kết thúc trong dòng mới. Nó rất nhanh.

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 }
'

Kịch bản perl đọc danh sách các tên tệp (được sắp xếp tùy chọn) từ stdin và với mỗi tệp, nó đọc byte cuối cùng để xác định xem tệp có kết thúc trong một dòng mới hay không. Nó rất nhanh vì nó tránh đọc toàn bộ nội dung của mỗi tệp. Nó xuất ra một dòng cho mỗi tệp mà nó đọc, có tiền tố là "error:" nếu xảy ra một số lỗi, "trống:" nếu tệp trống (không kết thúc bằng dòng mới!), "EOL:" ("kết thúc dòng ") nếu tệp kết thúc bằng dòng mới và" không EOL: "nếu tệp không kết thúc bằng dòng mới.

Lưu ý: tập lệnh không xử lý tên tệp có chứa dòng mới. Nếu bạn đang sử dụng hệ thống GNU hoặc BSD, bạn có thể xử lý tất cả các tên tệp có thể bằng cách thêm -print0 để tìm, -z để sắp xếp và -0 thành perl, như thế này:

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 }
'

Tất nhiên, bạn vẫn phải nghĩ ra cách mã hóa tên tệp với dòng mới trong đầu ra (còn lại là một bài tập cho người đọc).

Đầu ra có thể được lọc, nếu muốn, để nối thêm một dòng mới vào những tệp không có, đơn giản nhất là với

 echo >> "$filename"

Thiếu một dòng mới cuối cùng có thể gây ra lỗi trong các tập lệnh vì một số phiên bản shell và các tiện ích khác sẽ không xử lý đúng một dòng mới cuối cùng bị thiếu khi đọc một tệp như vậy.

Theo kinh nghiệm của tôi, việc thiếu một dòng mới cuối cùng là do sử dụng các tiện ích Windows khác nhau để chỉnh sửa các tệp. Tôi chưa bao giờ thấy vim gây ra một dòng mới cuối cùng bị thiếu khi chỉnh sửa một tệp, mặc dù nó sẽ báo cáo về các tệp đó.

Cuối cùng, có các tập lệnh ngắn hơn (nhưng chậm hơn) có thể lặp qua các đầu vào tên tệp của chúng để in các tệp không kết thúc trong dòng mới, chẳng hạn như:

/usr/bin/perl -ne 'print "$ARGV\n" if /.\z/' -- FILE1 FILE2 ...

1

Các vi/ vim/ exbiên tập viên tự động thêm <EOL>vào EOF trừ khi tập tin đã có nó.

Vì vậy, hãy thử một trong hai:

vi -ecwq foo.txt

tương đương với:

ex -cwq foo.txt

Kiểm tra:

$ printf foo > foo.txt && wc foo.txt
0 1 3 foo.txt
$ ex -scwq foo.txt && wc foo.txt
1 1 4 foo.txt

Để sửa nhiều tệp, hãy kiểm tra: Cách khắc phục 'Không có dòng mới ở cuối tệp' cho nhiều tệp? tại SO

Tại sao điều này rất quan trọng? Để giữ cho các tập tin của chúng tôi tương thích POSIX .


0

Để áp dụng câu trả lời được chấp nhận cho tất cả các tệp trong thư mục hiện tại (cộng với thư mục con):

$ find . -type f -exec sed -i -e '$a\' {} \;

Điều này hoạt động trên Linux (Ubuntu). Trên OS X có thể bạn phải sử dụng -i ''(chưa được kiểm tra).


4
Lưu ý rằng find .liệt kê tất cả các tệp, bao gồm các tệp trong .git. Để loại trừ:find . -type f -not -path './.git/*' -exec sed -i -e '$a\' {} \;
Friederbluemle 14/07/2015

Ước gì tôi đã đọc bình luận / suy nghĩ về nó trước khi tôi chạy nó. Ồ tốt
kstev

0

Ít nhất là trong các phiên bản GNU, chỉ cần grep ''hoặcawk 1 hợp thức hóa đầu vào của nó, thêm một dòng mới cuối cùng nếu chưa có. Họ sao chép tệp trong quá trình, việc này sẽ mất thời gian nếu lớn (nhưng nguồn không nên quá lớn để đọc?) Và cập nhật thời gian sửa đổi trừ khi bạn làm điều gì đó như

 mv file old; grep '' <old >file; touch -r old file

(mặc dù điều đó có thể ổn đối với một tệp bạn đang đăng ký vì bạn đã sửa đổi nó) và nó bị mất các liên kết cứng, quyền không thích hợp và ACL, v.v. trừ khi bạn thậm chí còn cẩn thận hơn.


Hoặc chỉ grep '' file 1<> file, mặc dù điều đó vẫn sẽ đọc và viết các tập tin đầy đủ.
Stéphane Chazelas

-1

Điều này hoạt động trong AIX ksh:

lastchar=`tail -c 1 *filename*`
if [ `echo "$lastchar" | wc -c` -gt "1" ]
then
    echo "/n" >> *filename*
fi

Trong trường hợp của tôi, nếu tệp bị thiếu dòng mới, wclệnh sẽ trả về giá trị 2và chúng tôi viết một dòng mới.


Phản hồi sẽ xuất hiện dưới hình thức tăng hoặc giảm, hoặc bạn sẽ được yêu cầu trong các bình luận để phác thảo câu trả lời / câu hỏi của bạn nhiều hơn, không cần phải hỏi nó trong cơ thể của câu trả lời. Giữ nó đến điểm, chào mừng đến với stackexchange!
k0pernikus

-1

Thêm vào câu trả lời của Patrick Oscarity , nếu bạn chỉ muốn áp dụng nó vào một thư mục cụ thể, bạn cũng có thể sử dụng:

find -type f | while read f; do tail -n1 $f | read -r _ || echo >> $f; done

Chạy này trong thư mục bạn muốn thêm dòng mới vào.


-1

echo $'' >> <FILE_NAME> sẽ thêm một dòng trống vào cuối tập tin.

echo $'\n\n' >> <FILE_NAME> sẽ thêm 3 dòng trống vào cuối tập tin.


StackExchange có một định dạng vui nhộn, tôi đã sửa nó cho bạn :-)
peterh

-1

Nếu tệp của bạn bị chấm dứt với các kết thúc dòng Windows\r\n và bạn đang ở trong Linux, bạn có thể sử dụng sedlệnh này . Nó chỉ thêm \r\nvào dòng cuối cùng nếu nó chưa có ở đó:

sed -i -e '$s/\([^\r]\)$/\1\r\n/'

Giải trình:

-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

Nếu dòng cuối cùng đã chứa \r\nthì regrec tìm kiếm sẽ không khớp, do đó sẽ không có gì xảy ra.


-1

Bạn có thể viết một fix-non-delimited-linekịch bản như:

#! /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

Trái với một số giải pháp được đưa ra ở đây, nó

  • nên hiệu quả ở chỗ nó không rẽ nhánh bất kỳ quá trình nào, chỉ đọc một byte cho mỗi tệp và không viết lại tệp đó (chỉ nối thêm một dòng mới)
  • sẽ không phá vỡ liên kết tượng trưng / liên kết cứng hoặc ảnh hưởng đến siêu dữ liệu (đồng thời, ctime / mtime chỉ được cập nhật khi thêm một dòng mới)
  • nên hoạt động tốt ngay cả khi byte cuối cùng là NUL hoặc là một phần của ký tự nhiều byte.
  • nên hoạt động tốt bất kể các ký tự hoặc không phải ký tự mà tên tệp có thể chứa
  • Nên xử lý chính xác các tệp không thể đọc được hoặc không thể đọc được hoặc không thể phát hiện được (và báo cáo lỗi tương ứng)
  • Không nên thêm một dòng mới vào các tệp trống (nhưng báo cáo lỗi về tìm kiếm không hợp lệ trong trường hợp đó)

Bạn có thể sử dụng nó như:

that-script *.txt

hoặc là:

git ls-files -z | xargs -0 that-script

POSIXly, bạn có thể làm một cái gì đó có chức năng tương đương với

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