बैश ग्लोब को एक स्ट्रिंग चर कैसे बनाया जाए?


14

व्यवस्था की सूचना

ओएस: ओएस एक्स

बैश: GNU बैश, संस्करण 3.2.57 (1) -release (x86_64-apple-darwin16)

पृष्ठभूमि

मैं अपने सभी गिट / नोडज प्रोजेक्ट से निर्देशिकाओं और फाइलों के एक सेट को बाहर करने के लिए टाइम मशीन चाहता हूं। मेरी परियोजना निर्देशिकाएं हैं ~/code/private/और ~/code/public/इसलिए मैं इसे करने के लिए बैश लूपिंग का उपयोग करने की कोशिश कर रहा हूं tmutil

मुद्दा

लघु संस्करण

यदि मेरे पास एक गणना स्ट्रिंग चर है k, तो मैं इसे फॉर-लूप से पहले या ठीक से गोलाकार कैसे बना सकता हूं:

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

नीचे दिए गए लंबे संस्करण में, आप देखेंगे k=$i/$j। इसलिए मैं लूप के लिए स्ट्रिंग को हार्डकोड नहीं कर सकता।

दीर्घ संस्करण

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

उत्पादन

उन्हें ग्लोब नहीं है। मुझे जो चाहिए वो नहीं।

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

बैश में एकल उद्धरण शेल प्रक्षेप को रोकते हैं, इसलिए आप अपने चर को डबल-कोट करने की कोशिश कर सकते हैं।
थॉमस एन

@ThomasN नहीं, यह काम नहीं करता है। kएक गणना स्ट्रिंग है, और मुझे लूप तक इस तरह से रहने की आवश्यकता है। कृपया मेरे लंबे संस्करण की जाँच करें।
जॉन सिउ

@ThomasN मैंने इसे स्पष्ट करने के लिए लघु संस्करण को अद्यतन किया।
जॉन सिउ

जवाबों:


18

आप मूल्यांकन के दूसरे दौर को बाध्य कर सकते हैं eval, लेकिन यह वास्तव में आवश्यक नहीं है। (और evalगंभीर समस्याएं होने लगती हैं जिस क्षण आपकी फ़ाइल के नाम में विशेष वर्ण होते हैं $।) समस्या ग्लोबिंग के साथ नहीं है, लेकिन टिल्ड विस्तार के साथ है।

चर विस्तार के बाद ग्लोबिंग होता है , यदि चर अयोग्य है , जैसा कि यहां (*) :

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

तो, एक ही नस में, यह वही है जो आपने किया था, और काम करता है:

$ 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

लेकिन टिल्ड के साथ यह नहीं है:

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

यह स्पष्ट रूप से बैश के लिए प्रलेखित है:

विस्तार का क्रम है: ब्रेस विस्तार; tilde विस्तार, पैरामीटर और चर विस्तार, ...

टिल्ड का विस्तार वैरिएबल विस्तार से पहले होता है इसलिए वैरिएबल के अंदर टिल्ड का विस्तार नहीं किया जाता है। इसके $HOMEबजाय आसान वर्कअराउंड का उपयोग या पूर्ण पथ है।

(* चर से ग्लब्स का विस्तार आमतौर पर वही नहीं होता जो आप चाहते हैं)


एक और बात:

जब आप यहां दिए गए पैटर्न पर लूप करते हैं:

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

ध्यान दें कि जैसा कि $excludeनिर्विवाद है, यह दोनों विभाजित है, और इस बिंदु पर ग्लोब भी है। इसलिए यदि वर्तमान निर्देशिका में कुछ पैटर्न से मेल खाता है, तो इसका विस्तार किया जाता है:

$ 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

इसके चारों ओर काम करने के लिए, एक विभाजित स्ट्रिंग के बजाय एक सरणी चर का उपयोग करें :

$ 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

एक अतिरिक्त बोनस के रूप में, सरणी प्रविष्टियों में विभाजन के साथ मुद्दों के बिना व्हाट्सएप भी हो सकता है।


कुछ इसी तरह से किया जा सकता है find -path, अगर आपको कोई आपत्ति नहीं है कि लक्षित फ़ाइलों को किस निर्देशिका स्तर पर होना चाहिए। उदाहरण के लिए /e2e/*.js:

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

हमें पहले के समान कारण के $HOMEबजाय उपयोग करना होगा ~, और कमांड लाइन $dirsपर अयोग्य होने की आवश्यकता है findइसलिए यह विभाजित हो जाता है, लेकिन $patternइसे उद्धृत किया जाना चाहिए ताकि यह गलती से शेल द्वारा विस्तारित न हो।

(मुझे लगता है कि आप -maxdepthजीएनयू के साथ खेल सकते हैं, यह सीमित करने के लिए कि खोज कितनी गहरी है, अगर आप परवाह करते हैं, लेकिन यह एक अलग मुद्दा है।)


क्या आप एक उत्तर के साथ हैं find? मैं वास्तव में उस मार्ग को भी खोज रहा हूं, क्योंकि फॉर-लूप जटिल हो रहा है। लेकिन '-पथ' को लेकर मुझे कठिनाई हो रही है।
जॉन सियु

टिल्ड '~' के बारे में आपकी जानकारी के रूप में आपके लिए क्रेडिट मुख्य मुद्दे पर अधिक प्रत्यक्ष है। मैं अंतिम स्क्रिप्ट और स्पष्टीकरण दूसरे उत्तर में पोस्ट करूंगा। लेकिन आपको पूरा श्रेय:
जॉन सिउ

@ जॉनसन, हाँ, खोज का उपयोग करते हुए जो पहली बार दिमाग में आया था। सटीक आवश्यकता के आधार पर यह प्रयोग करने योग्य भी हो सकता है। (या बेहतर भी, कुछ उपयोगों के लिए।)
ilkachachu

1
@kevinarpe, मुझे लगता है कि सरणियाँ मूल रूप से सिर्फ उसी के लिए होती हैं, और हाँ, "${array[@]}"(उद्धरण के साथ!) का दस्तावेजीकरण किया जाता है ( यहां और यहां देखें ) तत्वों का विस्तार करने के लिए अलग-अलग शब्दों के रूप में उन्हें अलग किए बिना।
ilkkachu

1
@sixtyfive, ठीक है, ग्लोब पैटर्न[abc] का एक मानक हिस्सा है , जैसे , मुझे नहीं लगता कि उन सभी को यहां कवर करना आवश्यक है। ?
इलकाचु

4

आप इसे बाद में कई मामलों में उपयोग करने के लिए एक स्ट्रिंग के बजाय एक सरणी के रूप में सहेज सकते हैं और इसे परिभाषित करते समय ग्लोबिंग होने दें। आपके मामले में, उदाहरण के लिए:

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

या बाद के उदाहरण में, आपको evalकुछ तार की आवश्यकता होगी

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
ध्यान दें कि $excludeवाइल्डकार्ड कैसे होते हैं, इस पर विभाजित + ग्लोब ऑपरेटर का उपयोग करने से पहले आपको ग्लोबिंग को अक्षम करना होगा और इसे पुनर्स्थापित करना होगा $i/$jऔर उपयोग नहीं करना चाहिए evalलेकिन उपयोग करना होगा"$i"/$j
स्टीफन चेज़लस

आप और इलकाचू दोनों ही अच्छा जवाब देते हैं। हालाँकि उनके जवाब ने इस मुद्दे की पहचान की। तो उसे श्रेय।
जॉन सिउ

2

@ilkkachu उत्तर ने मुख्य ग्लोबिंग समस्या को हल किया। उसे पूरा श्रेय।

V1

हालाँकि, excludeवाइल्डकार्ड (*) के साथ और बिना दोनों में प्रविष्टियाँ होने के कारण , और वे सभी में मौजूद नहीं हो सकते हैं, ग्लोबिंग के बाद अतिरिक्त जाँच की आवश्यकता होती है $i/$j। मैं अपने निष्कर्ष यहाँ साझा कर रहा हूँ।

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

आउटपुट स्पष्टीकरण

स्थिति को समझाने के लिए आंशिक उत्पादन निम्नलिखित है।

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

उपरोक्त स्वयं व्याख्यात्मक हैं।

/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

ऊपर दिखाया गया है क्योंकि अपवर्जित प्रविष्टि ( $j) में कोई वाइल्डकार्ड नहीं है, $i/$jएक सादा स्ट्रिंग संयोजन है। हालाँकि फ़ाइल / dir मौजूद नहीं है।

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

उपरोक्त शो में अपवर्जित प्रविष्टि के रूप में ( $j) में वाइल्डकार्ड होता है, लेकिन इसमें कोई फ़ाइल / निर्देशिका मिलान नहीं होता है, $i/$jबस मूल स्ट्रिंग को वापस करने की ग्लोबिंग होती है ।

V2

V2 एकल उद्धरण का उपयोग करें, evalऔर shopt -s nullglobस्वच्छ परिणाम प्राप्त करने के लिए। कोई फ़ाइल / dir अंतिम जाँच की आवश्यकता नहीं है।

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

एक समस्या यह है कि for j in $exclude, उस $excludeविस्तार के समय ग्लब्स का विस्तार हो सकता था $exclude(और evalउस पर कॉल करना परेशानी के लिए पूछ रहा है)। आप चाहते हैं कि ग्लोबिंग सक्षम हो for i in $dir, और for l in $k, लेकिन इसके लिए नहीं for j in $exclude। आप set -fपहले वाले से पहले चाहते हैं और set +fदूसरे के लिए। आमतौर पर, आप अपने स्प्लिट + ग्लोब ऑपरेटर को इस्तेमाल करने से पहले ट्यून करना चाहेंगे। किसी भी मामले में, आप के लिए विभाजन + ग्लोब नहीं चाहते हैं echo $l, इसलिए $lवहां उद्धृत किया जाना चाहिए।
स्टीफन चेज़लस 13

@ स्टीफनचैलेज आप v1 या v2 का उल्लेख कर रहे हैं? वी 2 के लिए, दोनों excludeऔर dirsएकल उद्धरण में (हैं ), so no globbing till eval`।
जॉन सिउ

ग्लोबिंग सूची के संदर्भों में निर्विवाद चर विस्तार पर होता है , (जिसे एक चर को छोड़ना होता है) जिसे हम कभी-कभी विभाजन + ग्लोब ऑपरेटर कहते हैं। अदिश चरों को असाइनमेंट में कोई ग्लोबिंग नहीं है। foo=*और foo='*'ऐसा ही है। लेकिन echo $fooऔर echo "$foo"नहीं हैं (जैसे गोले में bash, यह zsh, मछली या आरसी जैसे गोले में तय किया गया है, ऊपर लिंक भी देखें)। यहां आप उस ऑपरेटर का उपयोग करना चाहते हैं, लेकिन कुछ स्थानों में केवल विभाजित भाग है, और अन्य में केवल ग्लोब भाग है।
स्टीफन चेज़लस

@ स्टीफनचेज़ेल्लास जानकारी के लिए धन्यवाद !!! मुझे कुछ समय लगा लेकिन मैं अब चिंता को समझता हूं। यह बहुत मूल्यवान है !! धन्यवाद!!!
जॉन सिउ

1

के साथ zsh:

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

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

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

${^array}stringके रूप में विस्तार करना है $array[1]string $array[2]string...$=varचर पर शब्द विभाजन करना है (कुछ अन्य गोले डिफ़ॉल्ट रूप से करते हैं!), $~varचर पर ग्लोबिंग करता है (कुछ अन्य गोले भी डिफ़ॉल्ट रूप से (जब आप आमतौर पर उन्हें नहीं चाहते हैं, तो आपको $fऊपर उद्धृत करना होगा) अन्य गोले)।

(N)एक ग्लोब क्वालिफायर है जो उस विस्तार के परिणामस्वरूप होने वाले प्रत्येक ग्लब्स के लिए नलग्लोब को चालू करता है $^array1/$^array2। जब वे मेल नहीं खाते हैं तो ग्लब्स का विस्तार कुछ भी नहीं होता है। यह भी एक की तरह एक गैर-ग्लोब को चालू करने के लिए होता है ~/code/private/foo/Thumbs.db, जिसका अर्थ है कि यदि वह विशेष मौजूद नहीं है, तो यह शामिल नहीं है।


यह वास्तव में अच्छा है। मैंने परीक्षण किया और काम करता है। हालांकि, ऐसा लगता है कि एकल उद्धरण का उपयोग करते समय zsh newline के प्रति अधिक संवेदनशील है। जिस तरह excludeसे संलग्न है वह आउटपुट को प्रभावित कर रहा है।
जॉन सिउ

@ जॉनसन, ओह हां, आप सही कह रहे हैं। ऐसा लगता है कि विभाजन + ग्लोब और $^arrayदो अलग-अलग चरणों में किया जाना चाहिए ताकि यह सुनिश्चित हो सके कि खाली तत्वों को छोड़ दिया गया है (संपादित करें देखें)। यह एक बग की तरह थोड़ा सा दिखता है zsh, मैं उनकी मेलिंग सूची पर मुद्दा उठाऊंगा।
स्टीफन चेज़लस 12

मैं बैश के लिए एक v2 के साथ आता हूं, जो क्लीनर है, लेकिन फिर भी आपकी zsh स्क्रिप्ट की तरह कॉम्पैक्ट नहीं है, lol
जॉन सिउ
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.