Perbedaan antara cat dan '>' menjadi nol file

23

Apakah kedua perintah ini berbeda tentang bagaimana mereka melakukan zero-ing out file? Apakah yang terakhir ini merupakan cara yang lebih singkat untuk melakukan yang pertama? Apa yang terjadi di balik layar?

Kedua

$ cat /dev/null > file.txt

$ > file.txt 

menghasilkan

-rw-r--r--  1 user  wheel  0 May 18 10:33 file.txt
KM.
sumber

Jawaban:

28

cat /dev/null > file.txtadalah penggunaan kucing yang tidak berguna .

Pada dasarnya cat /dev/nullhanya menghasilkan catkeluaran apa-apa. Ya itu berhasil, tetapi disukai oleh banyak orang karena menghasilkan proses eksternal yang tidak perlu.
Itu salah satu hal yang umum hanya karena itu umum.

Menggunakan just > file.txtakan bekerja pada sebagian besar shell, tetapi tidak sepenuhnya portabel. Jika Anda ingin sepenuhnya portabel, berikut ini adalah alternatif yang baik:

true > file.txt
: > file.txt

Keduanya :dan truetidak menghasilkan data, dan shell builtin (sedangkan catutilitas eksternal), sehingga mereka lebih ringan dan lebih 'tepat'.

 

Memperbarui:

Seperti yang disebutkan tylerl dalam komentarnya, ada juga >| file.txtsintaksisnya.

Kebanyakan shell memiliki pengaturan yang akan mencegah mereka memotong file yang sudah ada >. Anda harus menggunakannya >|sebagai gantinya. Ini untuk mencegah kesalahan manusia saat Anda benar-benar ingin menambahkannya >>. Anda dapat mengaktifkan perilaku set -C.

Jadi dengan ini, saya pikir metode yang paling sederhana, paling tepat, dan portabel untuk memotong file adalah:

:>| file.txt
Patrick
sumber
2
Perintah titik dua didefinisikan dalam POSIX . Ini adalah operasi nol yang ada untuk memperluas argumen commandline.
kojiro
3
LOL, "penyalahgunaan kucing"
KM.
2
@kojiro :juga diamanatkan oleh POSIX untuk menjadi built-in, dan pada kenyataannya berbeda dari trueyang dianggap sebagai "khusus" built-in .
jw013
2
jangan lupa tentang noclobber . >| fileadalah truncate yang lebih eksplisit.
tylerl
1
Tidak truetidak harus dibangun dan secara tradisional tidak. :dibangun di semua kulit keluarga Bourne. :adalah builtin khusus per POSIX (jadi : > fileakan keluar dari shell misalnya jika filetidak dapat dibuka untuk ditulis dalam shell POSIX) dan truetidak. POSIX bahkan menyebutkan bahwa :mungkin lebih efisien daripada truepada beberapa sistem.
Stéphane Chazelas
23

Dalam hal portabilitas:

                      Bourne POSIX  zsh    csh/tcsh  rc/es  fish
> file                Y      Y      N(1)   N(1)      N      N
: > file              N/Y(2) Y(3)   Y      Y(4)      N(5)   N(5)
true > file           Y(5)   Y      Y      Y(5)      Y(5)   Y(5)
cat /dev/null > file  Y(5)   Y      Y(5)   Y(5)      Y(5)   Y(5)
eval > file           Y(3,8) Y(3)   Y      Y(6)      Y      Y
cp /dev/null file (7) Y(5)   Y      Y(5)   Y(5)      Y(5)   Y(5)
printf '' > file      Y(5)   Y      Y      Y(5)      Y(5)   Y

Catatan:

  1. kecuali dalam shatau kshemulasi, untuk pengalihan tanpa perintah, di zsh, diasumsikan perintah default (pager untuk pengalihan stdin saja, catjika tidak), yang dapat disetel dengan variabel NULLCMD dan READNULLCMD. Itu terinspirasi dari fitur serupa di(t)csh
  2. Pengalihan awalnya tidak dilakukan :di UnixV7 seperti :yang ditafsirkan setengah jalan antara pemimpin komentar dan perintah nol. Kemudian mereka dan seperti untuk semua builtin, jika pengalihan gagal, yang keluar dari shell.
  3. :dan evalmenjadi built-in khusus, jika pengalihan gagal, yang keluar dari shell ( bashhanya melakukannya dalam mode POSIX).
  4. Menariknya, di (t)csh, itu mendefinisikan label nol (untuk goto), jadi goto ''akan ada cabang di sana. Jika pengalihan gagal, itu keluar dari shell.
  5. Kecuali / jika perintah yang sesuai tersedia di $PATH( :umumnya tidak, true, cat, cpdan printfumumnya (POSIX mengharuskan mereka)).
  6. Jika pengalihan gagal, itu keluar dari shell.
  7. Namun, jika filesymlink ke file yang tidak ada, beberapa cpimplementasi seperti GNU akan menolak untuk membuatnya.
  8. Versi awal dari shell Bourne tidak mendukung pengalihan bawaan

Dalam hal keterbacaan:

(bagian ini sangat subjektif)

  • > file. Itu >terlihat seperti prompt atau komentar. Juga pertanyaan yang akan saya tanyakan ketika membaca itu (dan kebanyakan shell akan mengeluh tentang hal yang sama) adalah output apa yang Anda arahkan? .
  • : > file. :dikenal sebagai perintah no-op. Sehingga dibaca langsung sebagai menghasilkan file kosong. Namun, di sini lagi, itu :dapat dengan mudah dilewatkan dan / atau dilihat sebagai prompt.
  • true > file: apa hubungannya boolean dengan redirection atau konten file? Apa yang dimaksud di sini? adalah hal pertama yang terlintas di pikiran saya ketika saya membaca itu.
  • cat /dev/null > file. Bergabung /dev/nullke file? catyang sering dianggap sebagai perintah untuk membuang isi file, yang masih bisa masuk akal: membuang isi yang file kosong kefile , sedikit seperti cara berbelit-belit untuk mengatakan cp /dev/null filetapi masih bisa dimengerti.
  • cp /dev/null file. Menyalin isi file kosong ke file. Masuk akal, meskipun seseorang tidak tahu bagaimana cpdimaksudkan untuk melakukan secara default mungkin berpikir Anda mencoba untuk membuat filesebuah nullperangkat juga.
  • eval > fileatau eval '' > file. Tidak menjalankan apa pun dan mengalihkan outputnya ke a file. Masuk akal bagi saya. Aneh bahwa itu bukan idiom yang umum.
  • printf '' > file: secara eksplisit tidak mencetak apa pun ke dalam file. Yang paling masuk akal bagi saya.

Dari segi kinerja

Perbedaannya adalah apakah kita menggunakan shell builtin atau tidak. Jika tidak, suatu proses harus bercabang dua, perintah dimuat dan dieksekusi.

evaldijamin akan dibangun di semua kerang. :bawaan di mana pun itu tersedia (suka Bourne / csh). truedibangun dalam cangkang mirip Bourne saja.

printfadalah built-in sebagian besar kerang mirip Bourne modern dan fish.

cpdan catumumnya tidak built-in.

Sekarang cp /dev/null filetidak meminta pengalihan shell, jadi hal-hal seperti:

find . -exec cp /dev/null {} \;

akan lebih efisien daripada:

find . -exec sh -c '> "$1"' sh {} \;

(meskipun tidak harus dari:

find . -exec sh -c 'for f do : > "$f"; done' sh {} +

).

Sendiri

Secara pribadi, saya menggunakan : > filekerang mirip Bourne, dan tidak menggunakan apa pun selain kerang seperti Bourne hari ini.

Stéphane Chazelas
sumber
Bagaimana dengan dd of=file count=0?
kojiro
2
@kojiro, Dengan beberapa implementasi dd(seperti setidaknya Solaris 10), count=0diabaikan. dd if=/dev/null of=fileakan lebih portabel. Bagaimanapun, itu independen dari shell.
Stéphane Chazelas
OK, tapi tidak kalah layaknya dimasukkan cp /dev/null file, bukan?
kojiro
2
@ Kojiro, cp /dev/null fileadalah idiom yang umum. Saya membatasi mereka, intinya bukan daftar semua cara yang mungkin.
Stéphane Chazelas
5

Anda mungkin ingin melihat truncate, yang melakukan hal itu: memotong file.

Sebagai contoh:

truncate --size 0 file.txt

Ini mungkin lebih lambat daripada menggunakan true > file.txt.

Namun poin utama saya adalah: truncatedimaksudkan untuk memotong file, sementara menggunakan> memiliki efek samping memotong file.

Fabian
sumber
2
Truncate bagus ketika Anda ingin memotong file ke sesuatu selain 0. Yang mengatakan, bahkan tanpa shell adalah pernyataan yang aneh: dapatkah Anda menggambarkan konteks di mana truncateakan tersedia, tetapi perpustakaan C >atau tidak unistdakan tersedia?
kojiro
Tidak juga. Mungkin ada solusi yang lebih elegan untuk setiap skrip atau bahasa pemrograman yang tersedia.
Fabian
3
truncateadalah utilitas FreeBSD, relatif baru-baru ini (2008) ditambahkan ke GNU coreutils (meskipun --sizegaya opsi panjang GNU adalah spesifik GNU), jadi itu tidak tersedia dalam sistem non-GNU-atau-FreeBSD, dan itu tidak tersedia di sistem GNU yang lebih lama, Saya tidak akan mengatakan itu portabel. cp /dev/null fileakan bekerja tanpa pengalihan shell dan akan lebih portabel.
Stéphane Chazelas
Oke, saya akan menghapus komentar portabilitas itu. Padahal definisi Anda tentang terbaru tampaknya berbeda.
Fabian
2

Jawabannya sedikit tergantung pada apa file.txtitu, dan bagaimana proses menulisnya!

Saya akan mengutip kasus penggunaan umum: Anda memiliki logfile yang sedang tumbuh dipanggil file.txt, dan ingin memutarnya.

Karena itu Anda menyalin, misalnya, file.txtke dalam file.txt.save, lalu memotong file.txt.

Dalam skenario ini, JIKA file tidak dibuka oleh another_process(mis: another_processbisa berupa program keluaran ke file itu, misalnya program mencatat sesuatu), maka 2 proposal Anda setara, dan keduanya berfungsi dengan baik (tetapi yang kedua lebih disukai sebagai pertama "cat / dev / null> file.txt" adalah Penggunaan Cat yang Tidak Berguna dan juga terbuka dan membaca / dev / null).

Tetapi masalah sebenarnya adalah jika other_processmasih aktif, dan masih memiliki pegangan terbuka ke file.txt.

Kemudian, 2 kasus utama muncul, tergantung pada seberapa other processmembuka file:

  • Jika other_processmembukanya dengan cara normal, maka pegangan akan tetap menunjuk ke lokasi sebelumnya dalam file, misalnya pada offset 1200 byte. Oleh karena itu, penulisan berikutnya akan dimulai pada offset 1200, dan dengan demikian Anda akan memiliki lagi file sebesar 1200bytes (+ apa pun yang ditulis_proses lain), dengan 1200 karakter null terkemuka! Bukan apa yang Anda inginkan , saya kira.

  • Jika other_processdibuka file.txtdi "append mode", maka setiap kali ia menulis, pointer akan secara aktif mencari ke akhir file. Karena itu, ketika Anda memotongnya, ia akan "mencari" sampai byte 0, dan Anda tidak akan memiliki efek samping yang buruk! Ini yang Anda inginkan (... biasanya!)

Perhatikan bahwa ini berarti Anda perlu, ketika Anda memotong file, untuk memastikan bahwa semua yang other_processmasih menulis ke lokasi itu telah membukanya dalam mode "append". Jika tidak, Anda harus menghentikannya other_process, dan mulai lagi, sehingga mereka mulai menunjuk di awal file alih-alih lokasi sebelumnya.

Referensi: /programming//a/16720582/1841533 untuk penjelasan yang lebih bersih, dan contoh singkat yang bagus tentang perbedaan antara mode log normal dan append logging di /programming//a/984761/1841533

Olivier Dulac
sumber
2
Sangat sedikit dari jawaban ini yang benar-benar relevan atau menjawab pertanyaan. Perbedaan antara a cat /dev/null > filedan a > fileadalah cat /dev/nulldan itu tidak membuat perbedaan pada file.
jw013
@ jw013: Benar! Tapi saya hanya ingin mengambil kesempatan pertanyaan untuk menyatakan kembali informasi "apa yang Anda inginkan / bukan apa yang Anda inginkan", karena itu tidak terlalu terkenal, dan bisa mengenai seseorang yang mencoba memutar log (kasus umum di mana Anda ingin potong file).
Olivier Dulac
1
Ada waktu dan tempat untuk semuanya. Informasi Anda mungkin berguna dalam beberapa konteks lain tetapi itu tidak termasuk di sini - Anda harus menemukan tempat yang lebih tepat untuk itu karena tidak ada orang yang mencoba memutar log akan mencari dalam pertanyaan pengalihan yang sama sekali tidak terkait ini. Di sini jawaban Anda setara dengan gulma digital, sama seperti tanaman labu yang berguna di tengah ladang jagung akan dianggap sebagai gulma.
jw013
1

Saya suka ini dan sering menggunakannya karena terlihat lebih bersih dan tidak seperti seseorang menekan kunci kembali secara tidak sengaja:

echo -n "" > file.txt

Harus jadi built-in juga?

awsm
sumber
3
Ada banyak cara untuk mem-zero-out file. Saya pikir KM. hanya tertarik untuk memahami perbedaan antara dua metode yang ditunjukkan dalam pertanyaan.
drs
6
Banyak echoimplementasi yang tidak mendukung -n(dan akan menampilkan di -n<SPC><NL>sini. printf '' > file.txtAkan lebih portabel (setidaknya di seluruh sistem modern / POSIX)
Stéphane Chazelas