Cara menggabungkan perintah 'tar' dengan 'find'

31

Perintah find memberikan output ini:

[root @ localhost /] # find var / log / -iname anaconda. *
var / log / anaconda.log
var / log / anaconda.xlog
var / log / anaconda.yum.log
var / log / anaconda.syslog
var / log / anaconda.program.log
var / log / anaconda.storage.log

Setelah digabungkan dengan tar, ia menampilkan output ini:

[root @ localhost /] # find var / log / -iname anaconda. * -exec tar -cvf file.tar {} \;
var / log / anaconda.log
var / log / anaconda.xlog
var / log / anaconda.yum.log
var / log / anaconda.syslog
var / log / anaconda.program.log
var / log / anaconda.storage.log

Tetapi sementara daftar file tar itu hanya menampilkan satu file

[root @ localhost /] # tar -tvf file.tar
-rw ------- root / root 208454 2012-02-27 12:01 var / log / anaconda.storage.log

Apa yang saya lakukan salah di sini?

Dengan xargs saya mendapatkan output ini:

[root @ localhost /] # find var / log / -iname anaconda. * | xargs tar -cvf file1.tar

Pertanyaan kedua

Saat mengetik / di depan var, berarti find /var/logmengapa ini memberikan tar mesaage ini : Menghapus awalan `/ 'dari nama anggota

[root @ localhost /] # find / var / log / -iname anaconda. * -exec tar -cvf file.tar {} \;
tar: Menghapus awalan `/ 'dari nama anggota
/var/log/anaconda.log
tar: Menghapus awalan `/ 'dari nama anggota
/var/log/anaconda.xlog
tar: Menghapus awalan `/ 'dari nama anggota
/var/log/anaconda.yum.log
tar: Menghapus awalan `/ 'dari nama anggota
/var/log/anaconda.syslog
tar: Menghapus awalan `/ 'dari nama anggota
/var/log/anaconda.program.log
tar: Menghapus awalan `/ 'dari nama anggota
/var/log/anaconda.storage.log

Dalam bentuk sederhana apa perbedaan antara dua berikut?

find var/log dan find /var/log

maks
sumber
Ini adalah topik semi-off, tetapi dengan findperintah, Anda harus mengutip istilah pencarian. Kadang-kadang berfungsi tanpa tetapi tidak selalu.
nerdwaller
1
Jika Anda menggunakannya {} +sebagai gantinya {} \;akan mengelompokkan hasil temuan ke dalam satu argumen
Jason S

Jawaban:

39

Catatan: Lihat jawaban @ Iain untuk solusi yang agak lebih efisien.

Catatan yang findakan memanggil -exectindakan untuk setiap file yang ditemukannya.

Jika Anda menjalankan tar -cvf file.tar {}untuk setiap findoutput file tunggal , ini berarti Anda akan menimpa file.tarsetiap waktu, yang menjelaskan mengapa Anda berakhir dengan satu arsip tersisa yang hanya berisi anaconda.storage.log- itu adalah findoutput file terakhir .

Sekarang, Anda sebenarnya ingin menambahkan file ke arsip alih-alih membuatnya setiap kali (inilah yang dilakukan -copsi). Jadi, gunakan yang berikut ini:

find var/log/ -iname "anaconda.*" -exec tar -rvf file.tar {} \;

The -rpilihan menambahkan ke arsip bukannya menciptakan itu setiap kali.

Catatan: Ganti -iname anaconda.*dengan -iname "anaconda.*". Tanda bintang adalah wildcard dan dapat diperluas oleh shell Anda findbahkan sebelum melihatnya. Untuk mencegah ekspansi ini, bungkus argumen dalam tanda kutip ganda.


Adapun tarmenghapus memimpin /: Arsip hanya boleh berisi nama file relatif . Jika Anda menambahkan file dengan informasi utama /, mereka akan disimpan sebagai nama file absolut , artinya secara harfiah /var/…di komputer Anda, misalnya.

IIRC ini hanyalah tindakan pencegahan untuk tarimplementasi selain GNU, dan ini lebih aman dengan cara ini karena Anda tidak akan menimpa data aktual /var/…saat Anda mengekstrak arsip jika berisi nama file relatif.

slhck
sumber
6
Tetapi perhatikan bahwa jika Anda mencoba tarmengarsipkan kaset yang sebenarnya dengan cara ini, menambahkan satu file setiap saat, memutar ulang kaset itu, kemudian membaca ulang semuanya setiap kali untuk sampai ke akhir, semuanya akan menjadi sangat lambat. Solusi Anda hanya cocok jika Anda menulis file tar ke disk.
Nicole Hamilton
2
Benar, tapi saya pikir kita bisa dengan aman mengabaikan situasi ini;)
slhck
@ slhck * adalah wildcard yang harus cocok dengan semua kemungkinan, bukan? tetapi di sini find /var/log/ -iname anaconda*tidak memberikan apa pun dan find /var/log/ -iname anaconda.*memberikan hasilnya, mengapa?
maks
Ketika wildcard dikonsumsi, itu tidak akan terlihat findlagi. Jadi jika sudah anaconda*, dan di folder Anda saat ini ada sesuatu yang dinamai, misalnya, anaconda5(cocok dengan wildcard ini), wildcard akan diperluas, dan findakan melihat -iname anaconda5sebagai gantinya -iname anaconda*. Mengapa yang pertama tidak berfungsi dan yang kedua tergantung pada file apa yang ada di direktori Anda saat ini. @max
slhck
2
Anda dapat menggunakan {} +alih-alih {} \;sehingga akan mengelompokkan hasil temuan ke dalam satu argumen
Jason S
41

Anda dapat menggunakan sesuatu seperti:

find var/log -iname 'anaconda.*' -print0 | tar -cvf somefile.tar --null -T -

The -print0dan -Tbekerja sama untuk memungkinkan nama file dengan spasi baris baru, dll. Final -memberitahu tar untuk membaca nama file input dari stdin.

Catatan yang -print0harus datang di akhir pernyataan Anda, per jawaban ini . Kalau tidak, Anda mungkin akan mendapatkan lebih banyak file dari yang Anda harapkan.

Peter Mortensen
sumber
2
Anda telah menghilangkan -nameopsi, menyebabkan solusi Anda ke tarseluruh direktori. Jika itu yang Anda inginkan, Anda bisa melakukannya dengan lebih mudah tar -cvf file.tar var/logtanpa menggunakan findsama sekali.
Nicole Hamilton
2
+1 Memipipkan daftar ke taradalah ide yang bagus. Ini jelas merupakan solusi terbaik jika Anda mengharapkan pathnames memiliki spasi. Saya bahkan akan menggambarkannya sebagai yang terbaik secara teknis, karena dapat diandalkan dan efisien. Tetapi membutuhkan pengetahuan khusus tambahan dari keduanya finddan tar. Saya lebih suka substitusi perintah hanya karena ini adalah alat yang lebih umum: Pelajari cara menggunakannya sekali, lalu gunakan di mana-mana. (Tapi saya akui, saya menggunakan Windows di shell yang selalu berfungsi.) Maaf jika saya tampak kasar.
Nicole Hamilton
2
Anda sudah mendapatkan +1 Anda. Berbahagialah. :) Baris perintah panjang selalu merupakan kutukan dari proses pembuatan i / f pada OS apa pun. Saya ingat berdebat dengan Mark Lucovsky di Microsoft pada awal 90-an bahwa batas 32K Unicode karakter mereka pada NT terlalu kecil dan membuatnya mengeluh. Saya tidak tahu berapa banyak lagi byte yang diperlukan untuk menyimpan panjang lebih lama daripada celana pendek di kernel . Mendesah. Solusi kasus yang lebih umum ketika daftar arg terlalu panjang adalah untuk berbuat lebih banyak di shell (jika mungkin; di tambang itu) atau digunakan xargs.
Nicole Hamilton
9
jika Anda menggunakan -print0opsi find , Anda juga membutuhkan --nullopsi tar .
mivk
2
Dan --no-unquoteternyata diperlukan juga: nama file yang mengandung backslash akan dinyatakan salah penanganan. (Tidak, ini bukan hipotetis - Saya benar-benar membuat arsip tar dari kode orang lain, berisi nama file dengan backslash dalam namanya, itulah yang saya temukan.)
hvd
12

Coba ini:

tar -cvf file.tar `find var/log/ -iname "anaconda.*"`

Anda mencoba menggunakannya finduntuk -exec tar. Tetapi cara -execopsi bekerja, ia menjalankan perintah itu sekali untuk setiap file yang cocok ditemukan, menyebabkan tarmenimpa file tar yang dihasilkannya setiap kali. Itu sebabnya Anda hanya berakhir dengan yang terakhir. Anda juga perlu memberi tanda kutip di sekitar pola yang Anda tentukan findagar shell tidak memperluasnya sebelum meneruskannya find.

Menggunakan substitusi perintah dengan backticks (atau menggunakan $(...)notasi jika Anda suka), seluruh daftar nama yang dihasilkan oleh findditempelkan kembali ke baris perintah sebagai argumen tar, menyebabkannya untuk menuliskan semuanya sekaligus.

Nicole Hamilton
sumber
2
Ini bisa berakhir buruk jika menemukan output file dengan spasi di nama mereka, baris baru atau karakter globbing. Ini pasti gagal - stdout perpipaan dari findjarang merupakan ide yang baik. mywiki.wooledge.org/ParsingLs
slhck
3
Sslhck, stdout perpipaan dari find sebenarnya biasanya ide yang baik, seperti yang dijelaskan dengan sangat jelas di halaman yang Anda tautkan dalam komentar Anda :). Ini sebenarnya adalah cara yang disarankan untuk melakukan sesuatu. Anda hanya harus menggunakan beberapa trik (seperti read -rdari -print0) seperti yang saya lakukan dalam jawaban saya.
terdon
4
@ slhck Inilah sebabnya nama file dan direktori di Unix dan Linux secara tradisional menghindari spasi dalam nama. Itu juga mengapa, di Windows, di mana nama dengan spasi umum, saya menambahkan notasi substitusi perintah tambahan ke Hamilton C shell saya sendiri menggunakan double backticks yang memperlakukan seluruh baris (mungkin termasuk spasi) sebagai kata tunggal untuk ditempelkan kembali ke perintah baris. Sayangnya, tidak ada shell Unix yang memiliki fitur itu.
Nicole Hamilton
1
Mereka mungkin secara tradisional menghindarinya, tetapi dengan file yang dibuat di ruang pengguna melalui GUI, Anda tidak bisa mengabaikan file dengan spasi lagi dan memperlakukan mereka sebagai warga negara kelas dua (hanya karena itu Unix). Sangat menyenangkan Anda memasukkannya ke dalam shell Anda, tetapi itu untuk Windows, dan shell Unix tidak terlalu membutuhkan fitur itu jika Anda hanya menggunakan sintaks yang tepat dan mengambil tindakan pencegahan yang tepat. Itulah sebabnya saya memposting komentar saya di tempat pertama.
slhck
2
Tidak, tetapi di tempat lain itu mungkin terjadi. Itu sebabnya merupakan ide bagus untuk memprogram pertahanan - lebih baik aman daripada menyesal. Juga, pengunjung yang menemukan pertanyaan ini mungkin tidak selalu memiliki masalah yang sama persis dan bertanya-tanya mengapa perintah yang mereka temukan di sini tampaknya berfungsi untuk kasus ini tetapi gagal untuk mereka. Saya akan menyerahkan kepada Anda untuk memperbaiki perintah, saya hanya berpikir itu penting untuk menyebutkannya karena banyak orang mengalami masalah ini cepat atau lambat.
slhck
6

pertanyaan 1

Perintah Anda gagal karena tarmengambil setiap file yang ditemukan dan mengarsipkannya file.tar. Setiap kali melakukannya, itu akan menimpa yang dibuat sebelumnya file.tar.

Jika yang Anda inginkan adalah satu arsip dengan semua file, maka jalankan saja tarsecara langsung, tidak perlu find(dan ya, ini berfungsi untuk file dengan spasi di namanya):

tar -vcf file.tar /var/log/anaconda*   

Pertanyaan 2

Kedua perintah ini sangat berbeda:

  • find var / log akan mencari direktori yang disebut var/log subdirektori dari direktori Anda saat ini , itu setara dengan find ./var/log(perhatikan ./).

  • find / var / log akan mencari direktori yang disebut /var/log subdirektori dari root/ ,.

/Pesan utama berasal dari tar, bukan find. Ini berarti bahwa itu menghapus yang pertama /dari nama file Anda untuk membuat path absolut menjadi relatif . Ini berarti bahwa file dari /var/log/anaconda.errorakan diekstraksi ./var/log/anaconda.errorketika Anda menghapus arsip.

terdon
sumber
1

Ada dua cara yang -execbisa berhasil. Satu cara menjalankan perintah berkali-kali - satu kali untuk setiap file; cara lain menjalankan perintah satu kali, termasuk semua file sebagai daftar parameter.

  • -exec tar -cvf file.tar {} ';'menjalankan tarperintah untuk setiap file, menimpa arsip setiap kali.
  • -exec tar -cvf file.tar {} '+' menjalankan tar perintah sekali, membuat arsip semua file yang ditemukan.
mwfearnley
sumber
1

Saya pikir menggunakan -exec untuk setiap file dapat membuat kompresi tar sangat lambat, jika Anda memiliki banyak file. Saya lebih suka menggunakan perintah:

find . -iname "*.jpg" | cpio -ov -H tar -F jpgs.tar
fabceolin
sumber
sampai mulai gagal dengan/bin/cpio: xxx: Cannot open: Too many open files
SYN