Sistem Informasi
OS: OS X
bash: GNU bash, versi 3.2.57 (1) -release (x86_64-apple-darwin16)
Latar Belakang
Saya ingin mesin waktu untuk mengecualikan satu set direktori dan file dari semua proyek git / nodejs saya. Direktori proyek saya sedang dalam ~/code/private/
dan ~/code/public/
jadi saya mencoba menggunakan bash looping untuk melakukannya tmutil
.
Isu
Versi pendek
Jika saya memiliki variabel string terhitungk
, bagaimana cara membuatnya glob atau tepat sebelum for-loop:
i='~/code/public/*'
j='*.launch'
k=$i/$j # $k='~/code/public/*/*.launch'
for i in $k # I need $k to glob here
do
echo $i
done
Dalam versi panjang di bawah ini, Anda akan melihat k=$i/$j
. Jadi saya tidak bisa membuat hardcode string dalam for for.
Versi Panjang
#!/bin/bash
exclude='
*.launch
.classpath
.sass-cache
Thumbs.db
bower_components
build
connect.lock
coverage
dist
e2e/*.js
e2e/*.map
libpeerconnection.log
node_modules
npm-debug.log
testem.log
tmp
typings
'
dirs='
~/code/private/*
~/code/public/*
'
for i in $dirs
do
for j in $exclude
do
k=$i/$j # It is correct up to this line
for l in $k # I need it glob here
do
echo $l
# Command I want to execute
# tmutil addexclusion $l
done
done
done
Keluaran
Mereka tidak menggumpal. Bukan yang saya inginkan.
~/code/private/*/*.launch
~/code/private/*/.DS_Store
~/code/private/*/.classpath
~/code/private/*/.sass-cache
~/code/private/*/.settings
~/code/private/*/Thumbs.db
~/code/private/*/bower_components
~/code/private/*/build
~/code/private/*/connect.lock
~/code/private/*/coverage
~/code/private/*/dist
~/code/private/*/e2e/*.js
~/code/private/*/e2e/*.map
~/code/private/*/libpeerconnection.log
~/code/private/*/node_modules
~/code/private/*/npm-debug.log
~/code/private/*/testem.log
~/code/private/*/tmp
~/code/private/*/typings
~/code/public/*/*.launch
~/code/public/*/.DS_Store
~/code/public/*/.classpath
~/code/public/*/.sass-cache
~/code/public/*/.settings
~/code/public/*/Thumbs.db
~/code/public/*/bower_components
~/code/public/*/build
~/code/public/*/connect.lock
~/code/public/*/coverage
~/code/public/*/dist
~/code/public/*/e2e/*.js
~/code/public/*/e2e/*.map
~/code/public/*/libpeerconnection.log
~/code/public/*/node_modules
~/code/public/*/npm-debug.log
~/code/public/*/testem.log
~/code/public/*/tmp
~/code/public/*/typings
bash
shell-script
quoting
wildcards
John Siu
sumber
sumber
k
adalah string yang dihitung, dan saya membutuhkannya tetap seperti itu sampai loop. Silakan periksa versi panjang saya.Jawaban:
Anda dapat memaksakan putaran evaluasi lain
eval
, tetapi itu sebenarnya tidak perlu. (Daneval
mulai mengalami masalah serius saat nama file Anda mengandung karakter khusus seperti$
.) Masalahnya bukan dengan globbing, tetapi dengan ekspansi tilde.Globbing terjadi setelah ekspansi variabel, jika variabel tidak dikutip, seperti di sini (*) :
Jadi, dalam nada yang sama, ini mirip dengan apa yang Anda lakukan, dan bekerja:
Tetapi dengan tilde itu tidak:
Ini jelas didokumentasikan untuk Bash:
Ekspansi Tilde terjadi sebelum ekspansi variabel sehingga tildes di dalam variabel tidak diperluas. Solusi mudahnya adalah menggunakan
$HOME
atau path lengkap sebagai gantinya.(* meluaskan gumpalan dari variabel biasanya bukan yang Anda inginkan)
Hal lain:
Ketika Anda mengulangi pola, seperti di sini:
perhatikan bahwa seperti
$exclude
dikutip, keduanya terpecah, dan juga menggumpal pada saat ini. Jadi jika direktori saat ini berisi sesuatu yang cocok dengan polanya, itu diperluas menjadi:Untuk mengatasinya, gunakan variabel array alih-alih string yang dipisah:
Sebagai bonus tambahan, entri array juga dapat berisi spasi tanpa masalah dengan pemisahan.
Hal serupa dapat dilakukan dengan
find -path
, jika Anda tidak keberatan dengan level direktori apa file yang ditargetkan seharusnya. Misalnya untuk menemukan jalur yang diakhiri dengan/e2e/*.js
:Kita harus menggunakan
$HOME
alih-alih~
karena alasan yang sama seperti sebelumnya, dan$dirs
perlu tanda kutip padafind
baris perintah sehingga terpecah, tetapi$pattern
dikutip di harus dikutip sehingga tidak secara tidak sengaja diperluas oleh shell.(Saya pikir Anda bisa bermain dengan
-maxdepth
di GNU menemukan untuk membatasi seberapa dalam pencarian berjalan, jika Anda peduli, tapi itu sedikit masalah yang berbeda.)sumber
find
? Saya sebenarnya menjelajahi rute itu juga, karena for-loop semakin rumit. Tetapi saya mengalami kesulitan dengan '-path'."${array[@]}"
(dengan tanda kutip!) didokumentasikan (lihat di sini dan di sini ) untuk memperluas ke elemen sebagai kata-kata yang berbeda tanpa memisahkan mereka lebih jauh.[abc]
adalah bagian standar dari pola glob , seperti?
, saya rasa tidak perlu untuk menutupi semuanya di sini.Anda dapat menyimpannya sebagai array alih-alih string untuk digunakan nanti dalam banyak kasus dan membiarkan globbing terjadi ketika Anda mendefinisikannya. Dalam kasus Anda, misalnya:
atau dalam contoh nanti, Anda perlu
eval
beberapa stringsumber
$exclude
mengandung wildcard, Anda harus menonaktifkan globbing sebelum menggunakan operator split + glob di dalamnya dan mengembalikannya untuk$i/$j
dan tidak menggunakaneval
tetapi menggunakan"$i"/$j
Jawaban @ilkkachu memecahkan masalah globbing utama. Penghargaan penuh padanya.
V1
Namun, karena
exclude
mengandung entri baik dengan dan tanpa wildcard (*), dan juga mungkin tidak ada sama sekali, pemeriksaan tambahan diperlukan setelah globbing dari$i/$j
. Saya membagikan temuan saya di sini.Penjelasan Keluaran
Berikut ini adalah output parsial untuk menjelaskan situasi.
Di atas cukup jelas.
Di atas muncul karena entri mengecualikan (
$j
) tidak memiliki wildcard,$i/$j
menjadi penggabungan string sederhana. Namun file / dir tidak ada.Di atas muncul sebagai mengecualikan entri (
$j
) berisi wildcard tetapi tidak memiliki file / direktori yang cocok, globbing dari$i/$j
hanya mengembalikan string asli.V2
V2 menggunakan kutipan tunggal,
eval
danshopt -s nullglob
untuk mendapatkan hasil yang bersih. Tidak diperlukan pemeriksaan akhir file / dir.sumber
for j in $exclude
, gumpalan di dalam$exclude
dapat diperluas pada saat$exclude
ekspansi itu (dan memanggileval
yang meminta masalah). Anda ingin globbing diaktifkan untukfor i in $dir
, danfor l in $k
, tetapi tidak untukfor j in $exclude
. Anda ingin yang pertamaset -f
danset +f
yang lain. Secara umum, Anda ingin menyetel operator glob + split Anda sebelum menggunakannya. Bagaimanapun, Anda tidak ingin split + glob untukecho $l
, jadi$l
harus dikutip di sana.exclude
dandirs
berada dalam kutipan tunggal (), so no globbing till
eval`.foo=*
danfoo='*'
sama. Tapiecho $foo
danecho "$foo"
tidak (dalam cangkang sepertibash
, sudah diperbaiki dalam cangkang seperti zsh, ikan atau rc, lihat juga tautan di atas). Di sini Anda tidak ingin menggunakan operator yang, tetapi di beberapa tempat hanya bagian split, dan lain-lain hanya bagian gumpal.Dengan
zsh
:${^array}string
adalah untuk memperluas sebagai$array[1]string $array[2]string...
.$=var
adalah untuk melakukan pemisahan kata pada variabel (sesuatu yang dilakukan oleh shell lain secara default!),$~var
melakukan globbing pada variabel (sesuatu yang shell lain juga secara default (ketika Anda umumnya tidak menginginkannya, Anda harus mengutip$f
di atas dalam kerang lainnya)).(N)
adalah kualifikasi global yang mengaktifkan nullglob untuk masing-masing gumpalan yang dihasilkan dari$^array1/$^array2
ekspansi itu. Itu membuat gumpalan melebar ke nol ketika mereka tidak cocok. Itu juga terjadi untuk mengubah non-gumpal suka~/code/private/foo/Thumbs.db
menjadi satu, yang berarti bahwa jika itu tidak ada, itu tidak termasuk.sumber
exclude
tertutup mempengaruhi output.$^array
harus dilakukan dalam dua langkah terpisah untuk memastikan elemen kosong dibuang (lihat edit). Itu terlihat seperti bugzsh
, saya akan mengangkat masalah ini di milis mereka.