tar --exclude tidak mengecualikan. Mengapa?

71

Saya memiliki baris yang sangat sederhana ini dalam skrip bash yang dieksekusi dengan sukses (yaitu menghasilkan _data.tarfile), kecuali bahwa itu tidak mengecualikan sub-direktori yang dikatakan dikecualikan melalui --excludeopsi:

/bin/tar -cf /home/_data.tar  --exclude='/data/sub1/*'  --exclude='/data/sub2/*' --exclude='/data/sub3/*'  --exclude='/data/sub4/*'  --exclude='/data/sub5/*'  /data

Sebagai gantinya, ia menghasilkan _data.tarfile yang berisi semua yang ada di bawah / data, termasuk file dalam subdirektori yang ingin saya kecualikan.

Ada yang tahu kenapa? dan bagaimana cara memperbaikinya?

Pembaruan Saya menerapkan pengamatan saya berdasarkan tautan yang disediakan dalam jawaban pertama di bawah (dir level atas pertama, tidak ada spasi setelah dikecualikan terakhir):

/bin/tar -cf /home/_data.tar  /data  --exclude='/data/sub1/*'  --exclude='/data/sub2/*'  --exclude='/data/sub3/*'  --exclude='/data/sub4/*'  --exclude='/data/sub5/*'

Tapi itu tidak membantu. Semua sub-direktori "dikecualikan" hadir dalam _data.tarfile yang dihasilkan .

Ini membingungkan. Apakah ini bug dalam tar saat ini (GNU tar 1.23, pada CentOS 6.2, Linux 2.6.32) atau "sensitivitas ekstrim" tar terhadap spasi putih dan kesalahan ketik yang mudah dilewatkan lainnya, saya menganggap ini bug. Untuk sekarang.

Ini mengerikan : Saya mencoba wawasan yang disarankan di bawah ini (tidak tertinggal /*) dan masih tidak berfungsi dalam skrip produksi:

/bin/tar -cf /home/_data.tar  /data  --exclude='/data/sub1'  --exclude='/data/sub2'  --exclude='/data/sub3'  --exclude='/data/sub4'

Saya tidak dapat melihat perbedaan antara apa yang saya coba dan apa yang @Richard Perrin coba, kecuali untuk tanda kutip dan 2 spasi sebagai ganti 1. Saya akan mencoba ini (harus menunggu skrip nightly berjalan sebagai direktori yang akan didukung Facebook sangat besar) dan melaporkan kembali.

/bin/tar -cf /home/_data.tar  /data --exclude=/data/sub1 --exclude=/data/sub2 --exclude=/data/sub3 --exclude=/data/sub4

Saya mulai berpikir bahwa semua tar --excludekepekaan ini bukan tar melainkan sesuatu di lingkungan saya, tetapi kemudian apa yang bisa terjadi?

Berhasil! Variasi terakhir mencoba (tidak ada tanda kutip tunggal dan spasi tunggal alih-alih spasi ganda antara --excludes) yang diuji. Aneh tapi menerima.

Luar biasa! Ternyata versi yang lebih lama tar(1.15.1) hanya akan mengecualikan jika level atas dir terakhir pada baris perintah. Ini adalah kebalikan dari bagaimana versi 1.23 membutuhkan. FYI.

ateiob
sumber

Jawaban:

50

Jika Anda ingin mengecualikan seluruh direktori, pola Anda harus cocok dengan direktori itu, bukan file di dalamnya. Gunakan --exclude=/data/sub1sebagai ganti--exclude='/data/sub1/*'

Berhati-hatilah dengan mengutip pola untuk melindunginya dari ekspansi shell.

Lihat contoh ini, dengan masalah dalam doa terakhir:

$ for i in 0 1 2; do mkdir -p /tmp/data/sub$i; echo foo > /tmp/data/sub$i/foo; done
$ find /tmp/data
/tmp/data
/tmp/data/sub2
/tmp/data/sub2/foo
/tmp/data/sub0
/tmp/data/sub0/foo
/tmp/data/sub1
/tmp/data/sub1/foo
$ tar -zvcf /tmp/_data.tar /tmp/data --exclude='/tmp/data/sub[1-2]'
tar: Removing leading `/' from member names
/tmp/data/
/tmp/data/sub0/
/tmp/data/sub0/foo
$ tar -zvcf /tmp/_data.tar /tmp/data --exclude=/tmp/data/sub[1-2]
tar: Removing leading `/' from member names
/tmp/data/
/tmp/data/sub0/
/tmp/data/sub0/foo
$ echo tar -zvcf /tmp/_data.tar /tmp/data --exclude=/tmp/data/sub[1-2]
tar -zvcf /tmp/_data.tar /tmp/data --exclude=/tmp/data/sub[1-2]
$ tar -zvcf /tmp/_data.tar /tmp/data --exclude /tmp/data/sub[1-2]
tar: Removing leading `/' from member names
/tmp/data/
/tmp/data/sub2/
/tmp/data/sub2/foo
/tmp/data/sub0/
/tmp/data/sub0/foo
/tmp/data/sub2/
tar: Removing leading `/' from hard link targets
/tmp/data/sub2/foo
$ echo tar -zvcf /tmp/_data.tar /tmp/data --exclude /tmp/data/sub[1-2]
tar -zvcf /tmp/_data.tar /tmp/data --exclude /tmp/data/sub1 /tmp/data/sub2
R Perrin
sumber
Terima kasih atas jawaban yang sangat fokus dan jelas. Mengenai poin 1 Anda, saya mencoba mengikuti tips di utas LQ ini . Saya tidak yakin apa yang saya lewatkan, tetapi sekarang saya membaca poin ke-2 Anda, mungkin ini merupakan masalah jalur absolut dan relatif. Saya akan mencobanya dan melaporkan kembali. +1 untuk saat ini.
ateiob
Hal lain yang saya perhatikan adalah --exclude b(ruang bukan tanda sama) vs --exclude=b. Apakah ini ada bedanya? (seharusnya tidak IMHO)
ateiob
1
Tanda sama bisa sangat penting untuk menghindari ekspansi shell dari pola yang tidak dikutip. Jika Anda memiliki spasi, maka pola yang tidak dikutip dapat diperluas oleh shell ke argumen --exclude tunggal, dan ekspansi yang tersisa diberikan sebagai file untuk ditambahkan ke file tar. Contoh Anda di atas semuanya memiliki '=' - jika skrip tidak, dan tidak ada tanda kutip tunggal, maka itu dapat menjadi sumber masalah Anda.
R Perrin
BAIK. Saya menguji contoh Anda di kotak saya dan itu berfungsi, bahkan dengan banyak --exclude=pada baris yang sama. Jadi bedanya harus idiot /*yang saya tambahkan ke setiap sub-direktori. Saya akan menguji ini malam ini dalam skrip produksi dan melaporkan kembali. +1 lainnya.
ateiob
Bagi saya, jawaban dari @carlo adalah masalah khusus - tar bodoh tidak bisa mengambil --exclude sebagai opsi terakhir pada baris perintah - jelas menyebabkan banyak sakit kepala. Terima kasih semuanya.
moodboom
34

Mungkin versi Anda tarmengharuskan --excludeopsi harus ditempatkan di awal tarperintah.

Lihat: https://stackoverflow.com/q/984204

tar --exclude='./folder' --exclude='./upload/folder2' \
    -zcvf /backup/filename.tgz .

Lihat: http://mandrivausers.org/index.php?/topic/8585-multiple-exclude-in-tar/

tar --exclude=<first> --exclude=<second> -cjf backupfile.bz2 /home/*

Alternatif:

EXCLD='first second third'
tar -X <(for i in ${EXCLD}; do echo $i; done) -cjf backupfile.bz2 /home/*

Namun tartip perintah lainnya adalah dari sini :

tar cvfz myproject.tgz --exclude='path/dir_to_exclude1' \
                       --exclude='path/dir_to_exclude2' myproject
carlo
sumber
Lihat pembaruan saya di atas. Variasi terakhir mencoba (tanpa tanda kutip, spasi tunggal) berfungsi. Saya tidak tahu kenapa. +1 untuk tautan + jawaban yang dipikirkan dengan matang.
ateiob
FYI, di bawah debian, Jika saya tidak memperbaiki filter, seperti --exclude=mydir/*maka itu tidak berfungsi (menggunakan tar --exclude=maindir/mydir/* -cjf archive.tar2.bz2 maindir/*).
Olivier Pons
1
@OlivierPons daripada "under debian", atau mungkin dengan itu, letakkan versi tar ( tar --version); Debian mungkin akan dikirim dengan banyak versi tar yang berbeda selama bertahun-tahun.
msouth
1
Versi saya (1.29) hanya berfungsi dengan --excludesebelumnya -czf.
falsePockets
8

Untuk mengecualikan beberapa file, coba

--exclude=/data/{sub1,sub2,sub3,sub4}

Ini akan menghemat beberapa kode dan sakit kepala. Ini adalah solusi global, untuk semua jenis program / opsi. Jika Anda juga ingin memasukkan direktori induk dalam pilihan Anda (dalam data kasus ini), Anda harus menyertakan koma jejak. Misalnya:

umount /data/{sub1,sub2,}
tolga9009
sumber
3
Saya suka ikal. Saya menemukan bahwa banyak orang tidak tahu tentang mereka, bahkan dengan pengalaman bertahun-tahun unix. mv /very/very/very/very/long/path/to/a/file{,.bak}
msouth
5

Tautan ini mungkin bermanfaat. http://answers.google.com/answers/threadview/id/739467.html

Dua perbedaan langsung antara garis tidak bekerja dan beberapa kiat di tautan:

  1. Semua yang dikecualikan datang setelah direktori tingkat atas.
  2. Tidak dapat memiliki ruang APA PUN setelah yang terakhir --exclude.
BigG
sumber
Terima kasih. Jawabannya dengan -MAKmenarik perhatian saya dan sejauh ini saya dapat menemukan perbedaan berikut antara garis tidak berfungsi saya dan yang berikut: 1. Semua pengecualian datang setelah direktori tingkat atas. 2. Tidak dapat memiliki ruang APAPUN setelah yang terakhir --exclude. Saya akan menguji wawasan ini dan melaporkan kembali. +1 untuk saat ini.
ateiob
@ateiob Jika Anda mengetahuinya, dapatkah Anda mengirim jawaban di sini atau mengedit yang ini? Kami umumnya lebih suka tidak memiliki jawaban yang hanya tautan di tempat lain
Michael Mrozek
@Michael Mrozek Benar-benar. Inilah yang saya tulis dalam komentar saya. :)
ateiob
3

Solusinya mungkin menggunakan kombinasi find ... -prunedan taruntuk mengecualikan direktori yang ditentukan.

Pada Mac OS X, --excludeopsi GNU tartampaknya berfungsi sebagaimana mestinya.

Dalam kasus pengujian berikut, direktori /private/var/log/asldan /private/var/log/DiagnosticMessagesharus dikeluarkan dari arsip terkompresi /private/var/logdirektori.

# all successfully tested in Bash shell on Mac OS X (using gnutar and gfind)

# sudo port install findutils  # for gfind from MacPorts

sudo gnutar -czf ~/Desktop/varlog.tar.gz /private/var/log --exclude "/private/var/log/asl" --exclude "/private/var/log/DiagnosticMessages"

sudo gnutar -czf ~/Desktop/varlog.tar.gz  --exclude "/private/var/log/asl" --exclude "/private/var/log/DiagnosticMessages" /private/var/log

set -f # disable file name globbing
sudo gnutar -czf ~/Desktop/varlog.tar.gz  --exclude "/private/var/log/asl" --exclude "/private/var/log/Diagnostic*" /private/var/log

# combining GNU find and tar (on Mac OS X)

sudo gfind /private/var/log -xdev -type d \( -name "asl" -o -name "DiagnosticMessages" \) -prune -o -print0 | 
   sudo gnutar --null --no-recursion -czf ~/Desktop/varlog.tar.gz --files-from -

# exclude even more dirs
sudo gfind /private/var/log -xdev -type d \( -name "asl" -o -name "[Dacfks]*" \) -prune -o -print0 | 
    sudo gnutar --null --no-recursion -czf ~/Desktop/varlog.tar.gz --files-from -


# testing the compressed archive

gnutar -C ~/Desktop -xzf ~/Desktop/varlog.tar.gz

sudo gfind /private/var/log ~/Desktop/private \( -iname DiagnosticMessages -or -iname asl \)

sudo rm -rf ~/Desktop/varlog.tar.gz ~/Desktop/private
jon
sumber
Terima kasih +1 atas sarannya. Pada titik ini saya masih mencoba memahami mengapa fitur yang didokumentasikan dengan baik (dan matang) tidak berfungsi dalam skrip saya, setiap malam dijalankan oleh cron.
ateiob
3

Mungkin Anda dapat mencoba perintah dengan opsi lain:

--wildcards

Dan periksa apakah itu berjalan sebagaimana dimaksud.

Luis
sumber
Lihat pembaruan saya di atas. Variasi terakhir mencoba (tanpa tanda kutip, spasi tunggal) berfungsi. Saya tidak tahu kenapa. +1 untuk gagasan itu.
ateiob
3

Saya menggunakan mac, dan menemukan bahwa tidak termasuk tidak berfungsi kecuali folder tingkat atas adalah argumen terakhir

contoh perintah kerja:

tar czvf tar.tgz --exclude='Music' dir

FYI:

$: tar --version
bsdtar 2.8.3 - libarchive 2.8.3
jars99
sumber
Sama halnya dengan tar 1.27.1 melalui Ubuntu 14.04.
Greg Bell
3

Dalam kasus saya, itu tidak mengecualikan untuk alasan yang berbeda.

Jalur lengkap vs jalur relatif.

Baik exclude dan direktori harus menggunakan format path yang sama (yaitu path lengkap atau path relatif)

Contoh:

tar -cvf ctms-db-sync.tar --exclude='/home/mine/tmp/ctms-db-sync/sql' ctms-db-sync

Ini tidak akan berfungsi karena mengecualikan menggunakan jalur lengkap di mana sebagai target menggunakan jalur relatif

tar -cvf ctms-db-sync.tar --exclude='/home/mine/tmp/ctms-db-sync/sql' /home/mine/tmp/ctms-db-sync

Ini berfungsi karena keduanya menggunakan path lengkap

tar -cvf ctms-db-sync.tar --exclude='ctms-db-sync/sql' ctms-db-sync

Ini berfungsi karena keduanya menggunakan jalur relatif

hbt
sumber
1

Catatan tambahan untuk jawaban sempurna R Perrin :

Misalkan Anda tidak ingin mengarsipkan jalur absolut tetapi relatif, misalnya 'data' alih-alih '/ tmp / data'. Untuk mengecualikan jalur absolut argumen tar Anda akan berbeda berdasarkan pada implementasi tar (gnu tar vs bsd tar) yang Anda gunakan:

$ for i in 0 1 2; do
    for j in 0 1 2; do 
      mkdir -p /tmp/data/sub$i/sub$j
      echo foo > /tmp/data/sub$i/sub$j/foo
    done
  done

$ find /tmp/data/
/tmp/data/
/tmp/data/sub2
/tmp/data/sub2/sub2
/tmp/data/sub2/sub2/foo
/tmp/data/sub2/sub1
/tmp/data/sub2/sub1/foo
/tmp/data/sub2/sub0
/tmp/data/sub2/sub0/foo
/tmp/data/sub1
/tmp/data/sub1/sub2
/tmp/data/sub1/sub2/foo
/tmp/data/sub1/sub1
/tmp/data/sub1/sub1/foo
/tmp/data/sub1/sub0
/tmp/data/sub1/sub0/foo
/tmp/data/sub0
/tmp/data/sub0/sub2
/tmp/data/sub0/sub2/foo
/tmp/data/sub0/sub1
/tmp/data/sub0/sub1/foo
/tmp/data/sub0/sub0
/tmp/data/sub0/sub0/foo

$ cd /tmp/data; tar -zvcf /tmp/_data.tar --exclude './sub[1-2]'
./
./sub0/
./sub0/sub2/
./sub0/sub2/foo
./sub0/sub1/
./sub0/sub1/foo
./sub0/sub0/
./sub0/sub0/foo

# ATTENTION: bsdtar's behaviour differs from traditional tar (without a leading '^')!
$ cd /tmp/data; bsdtar -zvcf /tmp/_data.tar --exclude './sub[1-2]' .
a .
a ./sub0
a ./sub0/sub0
a ./sub0/sub0/foo

# FIX: Use a regex by adding a leading '^' will cause bsdtar to match only parent files and folders.
$ cd /tmp/data; bsdtar -zvcf /tmp/_data.tar --exclude '^./sub[1-2]' .
# ALTERNATIVE: bsdtar -C /tmp/data -zvcf /tmp/_data.tar --exclude '^./sub[1-2]' .
a .
a ./sub0
a ./sub0/sub2
a ./sub0/sub1
a ./sub0/sub0
a ./sub0/sub0/foo
a ./sub0/sub1/foo
a ./sub0/sub2/foo
Jakob
sumber
1

Baru saja terdeteksi pada tar (GNU tar) 1.29

Panggilan ini tidak mengecualikan dari file arsip yang ditentukan dengan --exclude-from:

/bin/tar --files-from ${datafile} --exclude-from ${excludefile} -jcf ${backupfile}

Panggilan ini bekerja dengan benar:

/bin/tar --exclude-from ${excludefile} --files-from ${datafile} -jcf ${backupfile}

Urutan parameter penting!

Alexander
sumber
0

Saya mencoba segala macam kombinasi termasuk beberapa jawaban yang terdaftar dan tidak bisa membuatnya untuk mengecualikan file yang terdaftar.

Jadi, muak mengejar jawaban atas apa yang seharusnya menjadi pekerjaan lima menit saya melakukan yang sebaliknya: membuat arsip folder yang ingin saya sertakan.

Saya melakukan ini dengan membuat arsip lalu menambahkannya :

tar -cvpf /path/to/mybackup.tar ./bin
tar rvf /path/to/mybackup.tar ./boot
tar rvf /path/to/mybackup.tar ./etc
tar rvf /path/to/mybackup.tar ./home
tar rvf /path/to/mybackup.tar ./lib
tar rvf /path/to/mybackup.tar ./sbin
tar rvf /path/to/mybackup.tar ./usr
tar rvf /path/to/mybackup.tar ./var

Beberapa catatan:

  • Saya menggunakan relatif bukan path absolut (yang juga memberi masalah) dengan menjalankan dari root filesystem.
  • Anda harus membuat arsip polos tar(dan bukan zip .tgz/ .tar.gz) - Anda bisa menggantinya nantigzip mybackup.tar
  • Pastikan Anda tidak meletakkan arsip di folder apa pun yang Anda sertakan atau Anda akan mendapatkan rekursi (cadangan sebagian juga termasuk dalam cadangan itu sendiri).
  • Perhatikan perbedaan dalam perintah pertama (buat) dari yang lain (tambahkan).
  • Anda dapat memeriksa bahwa file ditambahkan daripada cadangan ditimpa (mis. Setelah perintah kedua) jika Anda paranoid dengan menggunakan tar tvf mybackup.tar.
SharpC
sumber