Bas glob'u bir string değişkeni nasıl yapılır?


14

Sistem bilgisi

İşletim Sistemi: OS X

bash: GNU bash, sürüm 3.2.57 (1) -çalışma (x86_64-apple-darwin16)

Arka fon

Zaman makinesinin tüm git / nodejs projemden bir dizi dizin ve dosyayı hariç tutmasını istiyorum. Benim proje dizinleri vardır ~/code/private/ve ~/code/public/bu yüzden yapmak için bash döngü kullanmaya çalışıyorum tmutil.

Konu

Kısa versiyon

Bir varsa hesaplanan dize değişkeni k, nasıl içeri veya hemen önce topak yaparım bir for döngüsü:

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

Aşağıdaki uzun versiyonda göreceksiniz k=$i/$j. Bu yüzden for döngüsünde dizeyi sabit kodlayamıyorum.

Uzun versiyon

#!/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

Çıktı

Onlar globbed değil. İstediğim gibi değil.

~/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

Tek tırnak işaretleri Bash'de kabuk enterpolasyonunu durdurur, böylece değişkeninizi iki kez tırnak içine almayı deneyebilirsiniz.
Thomas N

@ThomasN hayır, bu işe yaramıyor. khesaplanmış bir dize ve döngü kadar bu şekilde kalmak gerekir. Lütfen uzun versiyonumu kontrol edin.
John Siu

@ThomasN Daha net olması için kısa sürümü güncelledim.
John Siu

Yanıtlar:


18

İle başka bir değerlendirme turu zorlayabilirsiniz eval, ama bu aslında gerekli değildir. (Ve evaldosya adlarınız gibi özel karakterler içerdiği anda ciddi sorunlar yaşamaya başlar $.) Sorun globbing ile değil, tilde genişletmeyle.

Globbing , değişken genişlemeden sonra , değişken burada belirtilmemişse gerçekleşir (*) :

$ x="/tm*" ; echo $x
/tmp

Yani, aynı şekilde, bu yaptıklarınıza benzer ve çalışır:

$ mkdir -p ~/public/foo/ ; touch ~/public/foo/x.launch
$ i="$HOME/public/*"; j="*.launch"; k="$i/$j"
$ echo $k
/home/foo/public/foo/x.launch

Ancak tilde ile:

$ i="~/public/*"; j="*.launch"; k="$i/$j"
$ echo $k
~/public/*/*.launch

Bu Bash için açıkça belgelenmiştir :

Genişlemelerin sırası: küme ayracı genişlemesi; tilde genişletme, parametre ve değişken genişletme, ...

Tilde genişletme, değişken genişletmeden önce gerçekleşir, bu nedenle değişkenlerin içindeki tiler genişletilmez. Kolay çözüm kullanmak $HOMEveya tam yol kullanmaktır.

(* globları değişkenlerden genişletmek genellikle istediğiniz şey değildir)


Başka bir şey:

Desenler üzerinde döngü yaptığınızda, burada olduğu gibi:

exclude="foo *bar"
for j in $exclude ; do
    ...

olarak not olduğunu $excludetırnaksız birlikte, gerek bölünmüş, ve ayrıca bu noktada globbed. Dolayısıyla, geçerli dizin kalıpla eşleşen bir şey içeriyorsa, buna genişletilir:

$ i="$HOME/public/foo"
$ exclude="*.launch"
$ touch $i/real.launch
$ for j in $exclude ; do           # split and glob, no match
    echo "$i"/$j ; done
/home/foo/public/foo/real.launch

$ touch ./hello.launch
$ for j in $exclude ; do           # split and glob, matches in current dir!
    echo "$i"/$j ; done
/home/foo/public/foo/hello.launch  # not the expected result

Bu sorunu çözmek için, bölünmüş bir dize yerine bir dizi değişkeni kullanın:

$ exclude=("*.launch")
$ exclude+=("something else")
$ for j in "${exclude[@]}" ; do echo "$i"/$j ; done
/home/foo/public/foo/real.launch
/home/foo/public/foo/something else

Ek bir bonus olarak, dizi girişleri bölme ile ilgili sorunlar olmadan boşluk da içerebilir.


find -pathHedeflenen dosyaların hangi dizin düzeyinde olması gerektiğine dikkat etmezseniz, benzer bir şey yapılabilir . Örneğin, biten herhangi bir yol bulmak için /e2e/*.js:

$ dirs="$HOME/public $HOME/private"
$ pattern="*/e2e/*.js"
$ find $dirs -path "$pattern"
/home/foo/public/one/two/three/e2e/asdf.js

Biz kullanmak zorunda $HOMEyerine ~daha önce olduğu gibi aynı nedenle ve $dirsihtiyaçlar kote olmayan edilecek findo bölünmüş alır böylece komut satırı, ancak $patternyanlışlıkla kabuk tarafından genişletilmediğinde böylece alıntı edilmelidir.

( -maxdepthEğer önemsiyorsanız, GNU bulmada aramanın ne kadar derin gittiğini sınırlamak için oynayabileceğinizi düşünüyorum , ancak bu biraz farklı bir konudur.)


Tek cevap siz misiniz find? Aslında bu yolu da araştırıyorum, çünkü for-loop karmaşıklaşıyor. Ama ben '-yolu' ile zorluk çekiyorum.
John Siu

Tilde '~' hakkındaki bilgileriniz ana konuyla ilgili daha doğrudan. Son senaryoyu ve açıklamayı başka bir cevapta göndereceğim. Ama size tam kredi: D
John Siu

@JohnSiu, evet, ilk akla gelen şey find'ı kullanmaktı. Tam ihtiyaca bağlı olarak da kullanılabilir. (veya bazı kullanımlar için daha iyi.)
ilkkachu

1
@kevinarpe, dizilerin temelde sadece bunun için olduğunu düşünüyorum ve evet, "${array[@]}"(tırnaklarla!) belgelendirilmiş ( buraya ve buraya bakınız ) öğeleri daha fazla bölmeden ayrı sözcükler olarak genişletmek için belgelenmiştir .
ilkkachu

1
@sixtyfive, iyi, [abc]standart bir parçası olan glob desen gibi ?, ben burada bütün bunların kapağını gitmek için gerekli olduğunu sanmıyorum.
ilkkachu

4

Daha sonra birçok durumda kullanmak için dizgi yerine dizi olarak kaydedebilir ve tanımladığınızda globbing'in gerçekleşmesine izin verebilirsiniz. Sizin durumunuzda, örneğin:

k=(~/code/public/*/*.launch)
for i in "${k[@]}"; do

veya sonraki örnekte, evalbazı dizelere ihtiyacınız olacak

dirs=(~/code/private/* ~/code/public/*)
for i in "${dirs[@]}"; do
    for j in $exclude; do
        eval "for k in $i/$j; do tmutil addexclusion \"\$k\"; done"
    done
done

1
Not nasıl $excludejoker içeren, sen kullanmadan önce devre dışı Genellemenin gerekiyordu bölünmüş + glob üzerine operatör ve için geri $i/$jve değil kullanmak evalancak kullanım"$i"/$j
Stéphane Chazelas

Hem sen hem de ilkkachu iyi cevap veriyor. Ancak cevabı konuyu belirledi. Bu yüzden ona kredi.
John Siu

2

@ilkkachu cevap ana globbing sorunu çözdü. Ona tam kredi.

V1

Bununla birlikte, excludehem joker karakterli hem de joker olmayan girişler (*) içermesi ve ayrıca hepsinde bulunmayabilmeleri nedeniyle, globbing işleminden sonra ekstra kontrol gereklidir $i/$j. Bulgularımı burada paylaşıyorum.

#!/bin/bash
exclude="
*.launch
.DS_Store
.classpath
.sass-cache
.settings
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="
$HOME/code/private/*
$HOME/code/public/*
"

# loop $dirs
for i in $dirs; do
    for j in $exclude ; do
        for k in $i/$j; do
            echo -e "$k"
            if [ -f $k ] || [ -d $k ] ; then
                # Only execute command if dir/file exist
                echo -e "\t^^^ Above file/dir exist! ^^^"
            fi
        done
    done
done

Çıktı Açıklaması

Durumu açıklamak için kısmi çıktı aşağıdadır.

/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/a.launch
    ^^^ Above file/dir exist! ^^^
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/b.launch
    ^^^ Above file/dir exist! ^^^
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/.DS_Store
    ^^^ Above file/dir exist! ^^^

Yukarıdakiler açıklayıcıdır.

/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/.classpath
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/.sass-cache
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/.settings
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/Thumbs.db
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/bower_components
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/build
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/connect.lock
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/coverage
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/dist

Dışlama girişinin ( $j) joker karakteri olmadığı $i/$jiçin düz bir dize birleşimi haline geldiği için yukarıdakiler görünür. Ancak dosya / dizin mevcut değil.

/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/e2e/*.js
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/e2e/*.map

Yukarıdaki exclude entry ( $j) olarak gösterilir joker karakter içerir, ancak dosya / dizin eşleşmesi yoktur $i/$j.

V2

V2 tek tırnak kullanın evalve shopt -s nullglobtemiz sonuç almak için. Hiçbir dosya / dir son kontrol gerektirmez.

#!/bin/bash
exclude='
*.launch
.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='
$HOME/code/private/*
$HOME/code/public/*
'

for i in $dirs; do
    for j in $exclude ; do
        shopt -s nullglob
        eval "k=$i/$j"
        for l in $k; do
            echo $l
        done
        shopt -u nullglob
    done
done

Bir sorun, içinde yer for j in $excludealan globların $excludebu $excludegenişleme sırasında genişleyebilmesidir (ve evalbunun için sorun istemektedir). Aşağıdakiler için etkin globbing istediğimiz for i in $dirve for l in $kfakat için for j in $exclude. set -fİkincisinden önce bir tane set +fve diğeri için bir tane istersiniz . Daha genel olarak, split + glob operatörünüzü kullanmadan önce ayarlamak istersiniz. Her durumda, split + glob için istemiyorsunuz echo $l, bu yüzden $lburada alıntılanmalıdır.
Stéphane Chazelas

@ StéphaneChazelas v1 veya v2'den bahsediyor musunuz? V2 için, her ikisi de excludeve dirstek tırnak ( ), so no globbing till eval`.
John Siu

Globbing, liste bağlamında sıralanmamış değişken genişlemesi üzerine gerçekleşir (bu, bir değişkeni bırakmadan) bazen split + glob operatörü olarak adlandırdığımız şeydir . Skaler değişkenlere verilen ödevlerde globbing yoktur. foo=*ve foo='*'aynı. Ancak echo $foove echo "$foo"değildir ( bashzsh, balık veya rc gibi kabuklarda sabitlenmiştir, ayrıca yukarıdaki bağlantıya bakın). Burada do o operatörünü kullanmak istiyorum, ancak bazı yerlerde sadece bölünmüş bölümünü ve diğerlerinde sadece glob parçası.
Stéphane Chazelas

@ StéphaneChazelas Bilgi için teşekkürler !!! Bir ara beni aldı ama şimdi endişeyi anlıyorum. Bu çok değerli !! Teşekkür ederim!!!
John Siu

1

İle zsh:

exclude='
*.launch
.classpath
.sass-cache
Thumbs.db
...
'

dirs=(
~/code/private/*
~/code/public/*
)

for f ($^dirs/${^${=~exclude}}(N)) {
  echo $f
}

${^array}stringolarak genişlemektir $array[1]string $array[2]string.... $=var, değişken (bir şey diğer kabukları varsayılan olarak bunu!) kelime bölme yapmaktır $~vargenellikle bunları istemiyorsanız, size alıntı zorunda olurdu (varsayılan olarak da (değişken bir şey diğer kabukları genelleme yapar $fyukarıdaki diğer kabuklar)).

(N)bu genişlemeden kaynaklanan globların her biri için nullglob'u açan bir glob niteleyicisidir $^array1/$^array2. Bu, kürelerin eşleşmedikleri zaman hiçbir şeye genişlememesini sağlar. Bu ~/code/private/foo/Thumbs.db, glob olmayan bir tanesini bire dönüştürmek anlamına gelir, yani bu belirli bir şey yoksa, dahil edilmez.


Bu gerçekten güzel. Test ettim ve çalışıyorum. Ancak, tek tırnak kullanılırken zsh satırsonuna daha duyarlı görünüyor. excludeEkteki yol çıktıyı etkilemektedir.
John Siu

@JohnSiu, oh evet, haklısın. Split + glob gibi görünüyor ve $^arrayboş öğelerin atıldığından emin olmak için iki ayrı adımda yapılması gerekiyor (düzenlemeye bakın). Bu biraz hata gibi görünüyor zsh, ben onların posta listesindeki sorunu gündeme getireceğim.
Stéphane Chazelas

Ben bash için bir v2 ile geliyor, ki bu daha temiz, ama yine de zsh betiğiniz kadar kompakt değil, lol
John Siu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.