it-swarm-id.com

Bagaimana cara menambahkan baris baru ke akhir file?

Menggunakan sistem kontrol versi, saya jengkel mendengar suara berisik ketika diff mengatakan No newline at end of file.

Jadi saya bertanya-tanya: Bagaimana cara menambahkan baris baru di akhir file untuk menghilangkan pesan-pesan itu?

208
k0pernikus

Untuk membersihkan proyek secara rekursif, saya menggunakan oneliner ini:

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

Penjelasan:

  • git ls-files -z daftar file dalam repositori. Dibutuhkan pola opsional sebagai parameter tambahan yang mungkin berguna dalam beberapa kasus jika Anda ingin membatasi operasi ke file/direktori tertentu. Sebagai alternatif, Anda bisa menggunakan find -print0 ... atau program serupa untuk membuat daftar file yang terpengaruh - pastikan dipancarkan NUL - entri yang dibatasi.

  • while IFS= read -rd '' f; do ... done iterates melalui entri, aman menangani nama file yang menyertakan spasi putih dan/atau baris baru.

  • tail -c1 < "$f" membaca karakter terakhir dari sebuah file.

  • read -r _ keluar dengan status keluar bukan nol jika baris baru tertinggal tidak ada.

  • || echo >> "$f" menambahkan baris baru ke file jika status keluar dari perintah sebelumnya bukan nol.

46
Patrick Oscity

Ini dia :

sed -i -e '$a\' file

Dan sebagai alternatif untuk OS X sed:

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

Ini menambahkan \n hanya di akhir file jika file tersebut belum diakhiri dengan baris baru. Jadi, jika Anda menjalankannya dua kali, itu tidak akan menambah baris baru:

$ 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
215
l0b0

Lihat:

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

jadi echo "" >> noeol-file harus melakukan trik. (Atau apakah Anda bermaksud meminta untuk mengidentifikasi file-file ini dan memperbaikinya?)

sunting menghapus "" dari echo "" >> foo (lihat komentar @ yuyichao) (edit2 menambahkan "" lagi ( tetapi lihat komentar @Keith Thompson)

41
sr_

Solusi lain menggunakan ed. Solusi ini hanya memengaruhi baris terakhir dan hanya jika \n Tidak ada:

ed -s file <<< w

Ini pada dasarnya berfungsi membuka file untuk diedit melalui skrip, skrip adalah perintah w tunggal, yang menulis file kembali ke disk. Ini didasarkan pada kalimat yang ditemukan di ed(1) halaman manual:

 BATASAN 
 (...) 
 
 Jika file teks (non-biner) tidak diakhiri oleh karakter baris baru, 
 Maka ed menambahkan satu pada membaca/menulisnya. Dalam kasus file binary 
, Ed tidak menambahkan baris baru tentang membaca/menulis. 
16
enzotib

Tambahkan baris baru terlepas dari:

echo >> filename

Berikut adalah cara untuk memeriksa apakah ada baris baru di bagian akhir sebelum menambahkannya, dengan menggunakan Python:

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

Cara sederhana, portabel, kompatibel dengan POSIX untuk menambahkan baris akhir yang tidak ada ke file teks:

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

Pendekatan ini tidak perlu membaca seluruh file; itu hanya dapat berusaha untuk EOF dan bekerja dari sana.

Pendekatan ini juga tidak perlu membuat file temp di belakang Anda (mis. Sed -i), sehingga hardlink tidak terpengaruh.

echo menambahkan baris baru ke file hanya ketika hasil substitusi perintah adalah string yang tidak kosong. Perhatikan bahwa ini hanya dapat terjadi jika file tidak kosong dan byte terakhir bukan baris baru.

Jika byte terakhir file adalah baris baru, ekor mengembalikannya, lalu perintahkan strip penggantinya; hasilnya adalah string kosong. Tes -n gagal dan gema tidak berjalan.

Jika file kosong, hasil substitusi perintah juga merupakan string kosong, dan sekali lagi gema tidak berjalan. Ini diinginkan, karena file kosong bukan file teks yang tidak valid, juga tidak setara dengan file teks tidak kosong dengan baris kosong.

12
Barefoot IO

Solusi tercepat adalah:

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

  1. Sangat cepat.
    Pada file ukuran sedang seq 99999999 >file ini membutuhkan milidetik.
    Solusi lain membutuhkan waktu lama:

    [ -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. Bekerja di abu, bash, lksh, mksh, ksh93, attsh dan zsh tetapi tidak yash.

  3. Tidak mengubah stempel waktu file jika tidak perlu menambahkan baris baru.
    Semua solusi lain yang disajikan di sini mengubah stempel waktu file.
  4. Semua solusi di atas adalah POSIX yang valid.

Jika Anda membutuhkan solusi portabel untuk yash (dan semua cangkang lain yang tercantum di atas), mungkin akan sedikit lebih rumit:

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

Cara tercepat untuk menguji apakah byte terakhir file adalah baris baru adalah dengan hanya membaca byte terakhir. Itu bisa dilakukan dengan tail -c1 file. Namun, cara sederhana untuk menguji apakah nilai byte adalah baris baru, tergantung pada penghapusan Shell dari baris baru di dalam ekspansi perintah gagal (misalnya) di yash, ketika karakter terakhir dalam file adalah UTF- 8 nilai.

Cara shell yang benar, sesuai dengan POSIX, semua (wajar) untuk menemukan apakah byte terakhir dari file adalah baris baru adalah dengan menggunakan xxd atau hexdump:

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

Kemudian, membandingkan output di atas dengan 0A akan memberikan tes yang kuat.
Sangat berguna untuk menghindari menambahkan baris baru ke file kosong.
File yang gagal memberikan karakter terakhir 0A, tentu saja:

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

Pendek dan manis. Ini memakan waktu sangat sedikit karena hanya membaca byte terakhir (seek to EOF). Tidak masalah jika file itu besar. Maka hanya tambahkan satu byte jika diperlukan.

Tidak perlu file temp atau digunakan. Tidak ada hardlink yang terpengaruh.

Jika tes ini dijalankan dua kali, itu akan tidak menambah baris baru.

7
sorontar

Sebaiknya Anda memperbaiki editor pengguna yang terakhir mengedit file. Jika Anda adalah orang terakhir yang mengedit file - editor apa yang Anda gunakan, saya menebak teman teks ..?

4
AD7six

Asalkan tidak ada null dalam input:

paste - <>infile >&0

... akan cukup untuk selalu hanya menambahkan baris baru ke ujung ekor infile jika belum memilikinya. Dan itu hanya perlu membaca file input melalui satu waktu untuk memperbaikinya.

3
user157018

Jika Anda hanya ingin dengan cepat menambahkan baris baru saat memproses beberapa jalur pipa, gunakan ini:

outputting_program | { cat ; echo ; }

itu juga sesuai dengan POSIX.

Kemudian, tentu saja, Anda dapat mengarahkannya ke file.

3
MichalH

Meskipun tidak langsung menjawab pertanyaan, berikut adalah skrip terkait yang saya tulis untuk mendeteksi file yang tidak diakhiri dengan baris baru. Ini sangat cepat.

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

Skrip Perl membaca daftar nama file (secara opsional diurutkan) dari stdin dan untuk setiap file membaca byte terakhir untuk menentukan apakah file tersebut berakhir pada baris baru atau tidak. Ini sangat cepat karena menghindari membaca seluruh isi setiap file. Ini menghasilkan satu baris untuk setiap file yang dibacanya, diawali dengan "kesalahan:" jika beberapa jenis kesalahan terjadi, "kosong:" jika file kosong (tidak berakhir dengan baris baru!), "EOL:" ("akhir dari baris ") jika file berakhir dengan baris baru dan" tidak EOL: "jika file tidak berakhir dengan baris baru.

Catatan: skrip tidak menangani nama file yang berisi baris baru. Jika Anda menggunakan sistem GNU atau BSD, Anda dapat menangani semua nama file yang mungkin dengan menambahkan -print0 untuk menemukan, -z untuk mengurutkan, dan -0 ke Perl, seperti ini:

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

Tentu saja, Anda masih harus menemukan cara untuk menyandikan nama file dengan baris baru di output (dibiarkan sebagai latihan untuk pembaca).

Keluaran dapat difilter, jika diinginkan, untuk menambahkan baris baru ke file-file yang tidak memilikinya, paling sederhana dengan

 echo >> "$filename"

Kurangnya baris baru final dapat menyebabkan bug dalam skrip karena beberapa versi Shell dan utilitas lain tidak akan menangani baris baru akhir yang hilang saat membaca file seperti itu.

Dalam pengalaman saya, tidak adanya baris baru final disebabkan oleh penggunaan berbagai utilitas Windows untuk mengedit file. Saya belum pernah melihat vim menyebabkan baris baru yang hilang saat mengedit file, meskipun akan melaporkan file tersebut.

Akhirnya, ada skrip yang jauh lebih pendek (tetapi lebih lambat) yang dapat mengulangi input nama file mereka untuk mencetak file-file yang tidak berakhir pada baris baru, seperti:

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

Editor vi/vim/ex secara otomatis menambahkan <EOL> at EOF kecuali file sudah memilikinya.

Jadi cobalah:

vi -ecwq foo.txt

yang setara dengan:

ex -cwq foo.txt

Pengujian:

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

Untuk memperbaiki beberapa file, periksa: Bagaimana cara memperbaiki 'Tidak ada baris baru di akhir file' untuk banyak file? di SO

Mengapa ini sangat penting? Untuk menjaga file kami kompatibel POSIX .

1
kenorb

Jika file Anda diakhiri dengan Windows akhiran garis \r\n dan Anda berada di Linux, Anda dapat menggunakan perintah sed ini. Itu hanya menambahkan \r\n ke baris terakhir jika belum ada di sana:

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

Penjelasan:

-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

Jika baris terakhir sudah berisi \r\n maka regexp pencarian tidak akan cocok, oleh karena itu tidak ada yang terjadi.

0
masgo

Untuk menerapkan jawaban yang diterima ke semua file di direktori saat ini (plus subdirektori):

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

Ini berfungsi di Linux (Ubuntu). Pada OS X Anda mungkin harus menggunakan -i '' (belum diuji).

0
friederbluemle

Anda bisa menulis fix-non-delimited-line skrip seperti:

#! /bin/zsh -
zmodload zsh/system || exit
ret=0
for file do
  (){
    sysseek -w end -1 || {
      syserror -p "Can't seek before the last byte: "
      return 1
    }
    read -r x || print -u0
  } <> $file || ret=$?
done
exit $ret

Bertentangan dengan beberapa solusi yang diberikan di sini, itu

  • harus efisien karena tidak melakukan proses apa pun, hanya membaca satu byte untuk setiap file, dan tidak menulis ulang file tersebut (hanya menambahkan baris baru)
  • tidak akan merusak symlink/hardlink atau memengaruhi metadata (juga, waktu/mtime hanya diperbarui ketika baris baru ditambahkan)
  • harus berfungsi dengan baik meskipun byte terakhir adalah NUL atau merupakan bagian dari karakter multi-byte.
  • harus berfungsi dengan baik terlepas dari karakter apa atau non-karakter nama file mungkin berisi
  • Seharusnya menangani file yang tidak dapat dibaca atau tidak dapat ditulis atau tidak dicari dengan benar (dan melaporkan kesalahan yang sesuai)
  • Seharusnya tidak menambahkan baris baru ke file kosong (tetapi melaporkan kesalahan tentang pencarian yang tidak valid dalam kasus itu)

Anda dapat menggunakannya misalnya sebagai:

that-script *.txt

atau:

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

Menambahkan ke jawaban Patrick Oscity , jika Anda hanya ingin menerapkannya ke direktori tertentu, Anda juga dapat menggunakan:

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

Jalankan ini di dalam direktori yang Anda ingin tambahkan baris baru.

0
cipher

echo $'' >> <FILE_NAME> akan menambahkan baris kosong ke akhir file.

echo $'\n\n' >> <FILE_NAME> akan menambahkan 3 baris kosong ke akhir file.

0
user247137

Setidaknya dalam versi GNU, cukup grep '' atau awk 1 canoniskan inputnya, tambahkan baris baru akhir jika belum ada. Mereka menyalin file dalam proses, yang membutuhkan waktu jika besar (tapi sumbernya tidak boleh terlalu besar untuk dibaca?) Dan memperbarui modtime kecuali jika Anda melakukan sesuatu seperti

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

(walaupun itu mungkin baik-baik saja pada file yang Anda periksa karena Anda memodifikasinya) dan kehilangan tautan, izin nondefault dan ACL dll kecuali Anda bahkan lebih berhati-hati.

0

Banyak saran bagus di sini tetapi satu ide adalah menghapus baris baru dan menambahkannya sehingga Anda tahu Anda tidak terus menambahkannya:

Ambil file "foo":

Hapus baris baru jika ada:

  truncate -s $(($(stat -c '%s' foo)-1)) foo

Kemudian tambahkan satu:

  sed -i -e '$a\' foo

Karenanya foo akan selalu mengandung setidaknya satu baris baru ..

atau ekor file dan cari baris baru, jika tidak mengandung tambahkan satu ..

  grep -q "[^0-9a-z-]" <<< $(tail -1 ./foo) && echo " " >> ./foo
0
Mike Q

Ini bekerja di AIX ksh:

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

Dalam kasus saya, jika file tersebut tidak ada baris baru, perintah wc mengembalikan nilai 2 dan kami menulis baris baru.

0
Daemoncan