rsync menggunakan regex untuk memasukkan hanya beberapa file

11

Saya mencoba menjalankan rsync untuk menyalin beberapa file secara rekursif ke jalur berdasarkan pola nama file mereka, case- sensitive . Inilah yang telah saya lakukan untuk menjalankan rsync:

$ rsync -avvz --include ='*/' --include='.*[Nn][Aa][Mm][E].*' --exclude='*' ./a/ ./b/

Tidak ada yang disalin, hasil debug menunjukkan:

[sender] hiding file 1Name.txt because of pattern *
[sender] hiding file 1.txt because of pattern *
[sender] hiding file 2.txt because of pattern *
[sender] hiding file Name1.txt because of pattern *
[sender] hiding directory test1 because of pattern *
[sender] hiding file NaMe.txt because of pattern *

Saya telah mencoba menggunakan: --include='*[Nn][Aa][Mm][E]*'dan kombinasi lainnya tetapi tetap tidak berhasil.

Adakah ide tentang cara menggunakan regex untuk memasukkan beberapa file?

pengguna1957413
sumber
4
Mengapa Anda menggunakan ini --exclude='*'?
2
jadi itu mengecualikan semua yang bukan bagian dari termasuk.
'menyembunyikan file 1Name.txt karena pola ' ini menunjukkan: - "apakah itu - kecuali aturan harus ada dalam perintah?" atau Jika Anda ingin mengecualikan beberapa file maka mengapa " ".
Akshay Patil

Jawaban:

5

rsync tidak berbicara regex. Anda dapat meminta find dan grep, meskipun mendapat sedikit rahasia. Untuk menemukan file target:

find a/ |
grep -i 'name'

Tetapi mereka semua diawali dengan "a /" - yang masuk akal, tetapi yang ingin kita akhiri adalah daftar pola sertakan yang dapat diterima oleh rsync, dan karena awalan "a /" tidak berfungsi untuk rsync I ' akan menghapusnya dengan memotong:

find . |
grep -i 'name' |
cut -d / -f 2-

Masih ada masalah - kami masih akan kehilangan file di subdirektori, karena rsync tidak mencari direktori dalam daftar pengecualian. Saya akan menggunakan awk untuk menambahkan subdirektori dari semua file yang cocok ke daftar pola sertakan:

find a/ |
grep -i 'name' |
cut -d / -f 2- |
awk -F/ '{print; while(/\//) {sub("/[^/]*$", ""); print}}'

Yang tersisa adalah mengirim daftar ke rsync - kita dapat menggunakan argumen --include-from = - untuk memberikan daftar pola ke rsync pada input standar. Jadi, semuanya:

find a/ |
grep -i 'name' |
cut -d / -f 2- |
awk -F/ '{print; while(/\//) {sub("/[^/]*$", ""); print}}' |
rsync -avvz --include-from=- --exclude='*' ./a/ ./b/

Perhatikan bahwa direktori sumber 'a' dirujuk melalui dua jalur berbeda - "a /" dan "./a/". Ini halus tetapi penting. Untuk membuat semuanya lebih konsisten, saya akan membuat satu perubahan terakhir, dan selalu merujuk ke direktori sumber sebagai "./a/". Namun, ini berarti perintah cut harus diubah karena akan ada tambahan "./" di bagian depan hasil dari find:

find ./a/ |
grep -i 'name' |
cut -d / -f 3- |
awk -F/ '{print; while(/\//) {sub("/[^/]*$", ""); print}}' |
rsync -avvz --include-from=- --exclude='*' ./a/ ./b/
sqweek
sumber
Mencoba menjalankannya, mengalami masalah dengan perintah cut. Tampaknya itu -tadalah saklar yang valid.
sunting: yang saya maksud -t bukan saklar yang valid
maaf, seharusnya -d. saya mulai menggunakan sed dan kemudian berubah untuk memotong karena saya pikir itu lebih jelas, tetapi lupa mengedit perintah saya: S
Tindak lanjut: Mencoba mengedit skrip untuk mengambil argumen ($ 1 = path_to_search, $ 2 sebagai pola untuk egrep) karena saya mencocokkan nama file + campuran ekstensi. Bagian-bagian itu berfungsi dengan baik, saya mendapat daftar yang diharapkan, tetapi rsync gagal menyalin. Tampaknya hanya berfungsi dengan direktori karakter nama tunggal seperti pada contoh (a) tebakan saya adalah bahwa perintah cut harus dimodifikasi untuk memotong karakter berdasarkan dir induk / atau sumber? Agak kehilangan cara untuk melakukan itu:
user1957413
Ah ya, Anda benar sekali. Ini harus bekerja pada nama direktori dengan panjang berapa pun, tetapi akan gagal segera setelah Anda merujuk ke direktori di luar direktori saat ini (karena akan ada jumlah garis miring yang berbeda di bagian awalan). Untuk memperbaikinya, mungkin paling mudah untuk menggunakan sed daripada memotong, seperti: sed "s#^$1/*##" buuuut yang akan merusak jalur yang berisi #. Untuk memperbaikinya kita harus mengutip nama direktori yang masuk: prefix=$(echo "$1" | sed 's#/#\\/#g')dan kemudian sed "s/^$prefix\\/*//" The subleties dari bash quoting sedikit mimpi buruk;)
sqweek
7

Saya akan menyarankan untuk menggunakan opsi filter rsync. Misalnya, ketikkan saja:

rsync -vam -f'+ *[Nn][Aa][Mm][E]*' -f'+ */' -f'- *' a b

aturan filter pertama memberitahu rsync pola apa yang akan dimasukkan. Aturan kedua diperlukan untuk memberitahu rsync untuk memeriksa semua direktori pada traversal-nya. Untuk mencegah dir kosong dari inklusi mereka dikeluarkan secara eksplisit oleh -mopsi. Aturan filter terakhir memberitahu rsync untuk membuang semua pola yang tersisa yang masih belum cocok sejauh ini.

sparkie
sumber
Manis. Ini berhasil juga. Saya mendapatkan folder a di dalam b, yang diperbaiki dengan menggunakan a / b / sebagai sumber dan tujuan. Terima kasih!
user1957413
Gunakan -f '+ * [Nn] [Aa] [Mm] [E] **' (dua bintang di bagian akhir) untuk memasukkan isi dari semua direktori dengan nama tertentu.
fobia
2

Jika Anda menggunakan ZSH maka Anda dapat menggunakan flag (#i) untuk mematikan sensitivitas case. Contoh:

$ touch NAME
$ ls (#i)*name*
NAME

ZSH juga mendukung pengecualian, yang ditentukan seperti jalur biasa tetapi mereka memiliki inisial ~

$ touch aa ab ac
$ ls *~*c
aa ab

Anda dapat membuat pengecualian:

$ ls *~*c~*b
aa

Akhirnya Anda dapat menentukan jenis file apa yang ingin Anda kembalikan (direktori, file, dll). Ini dilakukan dengan (/) untuk direktori dan (.) Untuk file.

$ touch file
$ mkdir dir
$ ls *(.)
file

Berdasarkan semua ini, saya akan melakukan perintah itu sebagai:

rsync -avvz *(/) (#i)*name* ./a/ ./b/

(Saya tidak melihat perlunya pengecualian dengan pemilih ini)

Matthew Franglen
sumber
1

@ sqweek jawaban di atas luar biasa, meskipun saya curiga ia memiliki bug dalam awkskripnya untuk membuat direktori induk, karena itu memberi saya misalnya:

$ echo a/b/c/d | awk -F/ '{print; while(/\//) {sub("/[^/]*", ""); print}}'
a/b/c/d
a/c/d
a/d
a

Saya bisa memperbaikinya dengan menggunakan gensub:

$ echo a/b/c/d | awk -F/ '{print; while(/\//) { $0=gensub("(.*)/[^/]*", "\\1", "g"); print}}'
a/b/c/d
a/b/c
a/b
a

Jadi, solusi lengkapnya, dengan awksedikit perubahan, adalah:

find ./a/ |
grep -i 'name' |
cut -d / -f 3- |
awk -F/ '{print; while(/\//) { $0=gensub("(.*)/[^/]*", "\\1", "g"); print}}' |
rsync -avvz --include-from=- --exclude='*' ./a/ ./b/
Ryan Williams
sumber
Terima kasih. Mengedit jawaban saya dengan perbaikan yang setara dengan menjangkar regex ke akhir baris ( sub("/[^/]*$")).
sqweek
0

Dicoba dengan skrip C # karena ini adalah bahasa yang paling banyak saya alami. Saya dapat membuat daftar file yang ingin saya sertakan, tetapi seseorang rsync masih memberitahu saya untuk mendaki. Itu membuat folder, tetapi mengabaikan file. Inilah yang saya dapatkan ..

Pertama isi direktori:

~/mono$ ls -l
total 24
drwxr-xr-x 5 me me 4096 Jan 15 00:36 a
drwxr-xr-x 2 me me 4096 Jan 15 00:36 b
drwxr-xr-x 3 me me 4096 Jan 14 00:31 bin
-rw-r--r-- 1 me me 3566 Jan 15 00:31 test.cs
-rwxr-xr-x 1 me me 4096 Jan 15 00:31 test.exe
-rwxr--r-- 1 me me  114 Jan 14 22:40 test.sh

Maka output dari skrip C #:

~/mono$ mono test.exe

/a/myfile/myfileseries.pdf
/a/myfile2/testfile.pdf

Dan hasil debug:

~/mono$ mono test.exe | rsync -avvvz --include='*/' --include-from=- --exclude='*' ./a/ ./b/
[client] add_rule(+ */)
[client] parse_filter_file(-,20,3)
[client] add_rule(+ /a/myfile/myfileseries.pdf)
[client] add_rule(+ /a/myfile2/testfile.pdf)
[client] add_rule(- *)
sending incremental file list
[sender] make_file(.,*,0)
[sender] hiding file 1Name.txt because of pattern *
[sender] showing directory myfile2 because of pattern */
[sender] make_file(myfile2,*,2)
[sender] hiding file 1.txt because of pattern *
[sender] hiding file 2.txt because of pattern *
[sender] hiding file Name1.txt because of pattern *
[sender] showing directory test1 because of pattern */
[sender] make_file(test1,*,2)
[sender] hiding file NaMe.txt because of pattern *
[sender] showing directory myfile because of pattern */
[sender] make_file(myfile,*,2)
send_file_list done
send_files starting
[sender] hiding file myfile/myfileseries.pdf because of pattern *
[sender] hiding file myfile2/testfile.pdf because of pattern *
[sender] hiding file test1/test.txt because of pattern *
pengguna1957413
sumber
0

[EDIT] Ini hanya berfungsi secara lokal. Untuk jalur jarak jauh, struktur direktori harus dibuat terlebih dahulu.

Lebih sederhana dari pada jawaban yang diterima; Gunakan --file-from, yang meliputi direktori induk secara otomatis dan printf path file dengan% P

find /tmp/source -wholename '*[Nn][Aa][Mm][E]*' -printf '%P\n' | rsync -vzrm --exclude='*/' --files-from=- /tmp/source/ /tmp/target/

Jadi, Anda hanya perlu menggunakan finddan rsync.

fobia
sumber