Bash Globbing: Wildcards आणि Extglob समजून घ्या
Bash command सुरू होण्यापूर्वी wildcards filenames मध्ये expand करतो. nullglob, dotglob, globstar आणि extglob negation मुळे येणारे फरक उदाहरणांसह समजून घ्या.
कमांड सुरू होण्यापूर्वी Bash globbing काय करते
Bash globbing म्हणजे कमांड सुरू होण्यापूर्वी shell कडून *.log सारखा pattern वास्तविक filenames च्या sorted list मध्ये रूपांतरित करणे. rm *.log कधीही rm कडे pattern पाठवत नाही. Bash directory वाचतो, pattern शी जुळणारी names ठेवतो, त्यांना sort करतो आणि नंतर प्रत्येक name स्वतंत्र argument म्हणून rm चालवतो. Pattern वापरला गेला होता हे rm program ला कळत नाही.
Globbing संदर्भातील जवळजवळ प्रत्येक अनपेक्षित परिणाम या एका वस्तुस्थितीमुळे होतो. Matching shell करते. ती त्या क्षणी अस्तित्वात असलेल्या files विरुद्ध केली जाते. Command ला फक्त तयार झालेली list मिळते. याचे औपचारिक नाव pathname expansion आहे. दैनंदिन वापरात याला globbing म्हणतात. दोन्हींचा अर्थ समान आहे.
सरावासाठी एक छोटे directory tree तयार करा
खालील प्रत्येक उदाहरण समान files वर चालते. त्या files temporary directory मध्ये तयार करा. त्यामुळे आवश्यक files जवळपास नसतील आणि cleanup एका command ने करता येईल.
lab=$(mktemp -d)
cd "$lab"
mkdir -p logs archive/2025 archive/2026
touch app.log app.log.1 app.log.2 app.log.10 error.log debug.LOG
touch notes.txt notes.md README 'weekly report.log'
touch .env .apprc
touch logs/nginx.log logs/nginx.log.1
touch archive/2025/dec.log archive/2026/jan.logmktemp -d नवीन directory तयार करून त्याचा path परत देते आणि $(...) तो path lab variable मध्ये साठवते. ही syntax तुमच्यासाठी नवीन असल्यास, command चे output command substitution कसे capture करते यामध्ये संपूर्ण स्पष्टीकरण दिले आहे.
तुम्ही तयार केलेली रचना पहा आणि तुमचा shell तपासा, कारण शेवटची एक सूचना version वर अवलंबून आहे:
ls -a
bash --versionयेथील सर्व काही bash 4.0 आणि त्यानंतरच्या versions वर चालते. शेवटाजवळील globskipdots सूचना bash 5.2 आणि त्यानंतरच्या versions साठी लागू आहे. August 2026 पर्यंतच्या सध्याच्या Ubuntu आणि Debian releases मध्ये हीच version उपलब्ध आहे.
संपूर्ण मजकुरात वापरलेली preview command printf '%s\n' PATTERN आहे. ती प्रत्येक match स्वतंत्र line वर print करते आणि disk वरील कोणतीही गोष्ट बदलत नाही. त्यामुळे खात्री नसलेल्या कोणत्याही pattern वर ती सुरक्षितपणे चालवता येते.
तीन wildcards: star, question mark आणि brackets
*रिकाम्या stringसह कोणत्याही लांबीच्या characters च्या क्रमाशी जुळतो.?नेमक्या एका character शी जुळतो.[...]brackets मधील set मधून घेतलेल्या नेमक्या एका character शी जुळतो.
printf '%s\n' *.log
printf '%s\n' app.log.?
printf '%s\n' app.log.[0-9]
printf '%s\n' notes.*
printf '%s\n' *तुम्ही तयार केलेल्या file list शी दुसरी line तुलना करा. ? म्हणजे एकच character, त्यापेक्षा अधिक नाही. त्यामुळे दोन अंकी suffix त्या pattern मध्ये येत नाही. ते app.log.[0-9]* असे विस्तृत करा आणि फरक पाहण्यासाठी पुन्हा चालवा.
तिन्ही wildcards साठी दोन नियम लागू होतात. कोणताही wildcard कधीही / शी जुळत नाही. त्यामुळे pattern एका directory level च्या आतच राहतो. तसेच सुरुवातीचा . अक्षरशः type करावा लागतो. त्यामुळे * ने सुरू होणारा pattern प्रत्येक hidden name वगळतो. rm * तुमच्या dotfiles कडे दुर्लक्ष करतो, याचे कारण हा दुसरा नियम आहे.
Glob चे results current locale च्या collation order नुसार sorted येतात. हा character-by-character text sort असतो. त्यामुळे 10 suffix, 2 suffix च्या आधी sort होतो. तुलना करताना यांपैकी कोणताही suffix number म्हणून वाचला जात नाही. printf '%s\n' app.log.* आणि त्यानंतर ls -v app.log.* चालवा आणि दोन्हींची तुलना करा: ls -v embedded numbers त्यांच्या value नुसार sort करतो आणि pipeline मध्ये sort -V हेच काम करतो.
वर्णवर्ग आणि श्रेणी
कंसांच्या आत तुम्ही यादी, श्रेणी किंवा नकार लिहू शकता.
[ch]एक वर्ण जुळवते:cकिंवाh.[a-f]a ते f या श्रेणीतील एक वर्ण जुळवते.[!0-9]अंक नसलेले कोणतेही एक वर्ण जुळवते.[^0-9]याच अर्थाचे आहे.
printf '%s\n' [dn]*
printf '%s\n' *.[A-Z]*
printf '%s\n' [A-Z]*श्रेणी हा वेगवेगळ्या मशीनवर परिणाम बदलण्याचे मुख्य कारण आहे. श्रेणीचे निराकरण साध्या ASCII क्रमाऐवजी सध्याच्या locale च्या collation order नुसार होते. त्यामुळे काही प्रणालींवर [a-z] मोठी अक्षरेही जुळवते. Bash मध्ये साधा ASCII क्रम सक्तीने लागू करणारा globasciiranges पर्याय आहे. अलीकडील builds मध्ये तो default ने enable असतो. त्यामुळे अंदाज न बांधता shopt globasciiranges वापरून तुमची प्रणाली तपासा. सर्वत्र एकाच पद्धतीने जुळणी करायची असल्यास, स्वतःची क्रमवारी निश्चित करण्यासाठी LC_ALL=C स्वतंत्र ओळीवर, फाइलच्या सुरुवातीजवळ सेट करा. यामुळे प्रत्येक श्रेणी पुन्हा साध्या byte क्रमाने वापरली जाते.
ही assignment स्वतंत्र command असली पाहिजे. Prefix म्हणून लिहिल्यास LC_ALL=C ls [a-z]* फक्त ls साठी locale सेट करते. त्यापूर्वीच shell ने स्वतःच्या locale नुसार pattern expand केलेला असतो. त्यामुळे ls सुरू होण्यापूर्वीच तो विस्तार पूर्ण झालेला असतो. पहिल्या section मधील नियम येथे पुन्हा लागू होतो.
ब्रॅस expansion म्हणजे globbing नाही
ब्रॅस wildcard सारखे दिसतात, पण त्यांचे कार्य पूर्णपणे वेगळे असते. ब्रॅस expansion globbing च्या खूप आधी होते. ती disk वरील माहिती कधीही तपासत नाही.
echo file{1,2,3}.txt
echo {01..10}
echo {a..e}
echo {0..20..5}यापैकी एकही file अस्तित्वात नाही. तरीही expansion होते. हाच दोन्हींमधील मुख्य फरक आहे: glob filesystem ला प्रश्न विचारतो, तर brace फक्त text तयार करतो. म्हणूनच काहीतरी शोधण्याऐवजी तयार करण्यासाठी braces योग्य साधन आहेत.
mkdir -p site/{css,js,img}
cp app.log{,.bak}
touch report-{2025,2026}-{01,02}.csvcp app.log{,.bak} चे expansion दोन words मध्ये होते: app.log आणि app.log.bak. Comma च्या आधीचा रिकामा item मूळ text पुन्हा तयार करतो. {01..10} मधील zero padding कायम राहते, कारण कोणत्याही endpoint च्या सुरुवातीला zero असल्यास bash तयार होणाऱ्या प्रत्येक number ला समान width देते.
Expansion चा क्रम अनेकदा येणाऱ्या एका त्रुटीचे कारणही स्पष्ट करतो. Bash प्रथम braces चे expansion करते, त्यानंतर variables चे expansion करते आणि शेवटी pathname expansion करते.
n=5
echo {1..$n}$n चे number मध्ये रूपांतर होईपर्यंत brace expansion आधीच पूर्ण झालेले असते. त्यामुळे कोणतीही range तयार होत नाही. seq 1 "$n" वापरा किंवा for ((i=1; i<=n; i++)) या स्वरूपात लिहिलेला C-style loop वापरा.
कुठल्याही नमुन्याशी जुळणारे काहीही नसल्यास काय होते
ही पहिली अपयशाची स्थिती आहे आणि प्रत्येकाला एकदा तरी याचा सामना करावा लागतो. डीफॉल्टनुसार bash जुळणी न झालेला pattern तसाच ठेवतो. शब्द तुम्ही टाइप केल्याप्रमाणेच command पर्यंत पोहोचतो. त्यामुळे command ला *, ., t, m आणि p ही अक्षरे मिळतात आणि तो त्यांना filename म्हणून वापरण्याचा प्रयत्न करतो.
printf '%s\n' *.tmpprintf सोबत हे निरुपद्रवी आहे. परंतु वस्तू तयार करणाऱ्या command सोबत ते निरुपद्रवी नाही. touch *.tmp मध्ये कोणत्याही .tmp files नसतील, तर literal नाव *.tmp असलेली file तयार होते. ती नंतर delete करताना quoting आवश्यक असते, जेणेकरून shell तिचा पुन्हा विस्तार करणार नाही: rm -- '*.tmp'.
दोन shell options हा नियम बदलतात. जुळणारे काहीही नसलेल्या pattern वर प्रत्येक option वापरून पाहा आणि नंतर तो unset करा.
shopt -s nullglob
printf '%s\n' *.tmp
shopt -u nullglob
shopt -s failglob
printf '%s\n' *.tmp
shopt -u failglobnullglob मुळे जुळणी न झालेला pattern पूर्णपणे कशातही expand होत नाही. त्यामुळे command तुम्ही लिहिलेल्यापेक्षा कमी arguments सह चालतो. loop मध्ये हीच अपेक्षित वर्तणूक असते. nullglob बंद असल्यास, for f in *.tmp त्याचा body एकदा चालवतो आणि f मध्ये literal pattern ठेवतो. असे करणाऱ्या जवळपास प्रत्येक script मध्ये हा bug असतो. nullglob चालू असल्यास body शून्य वेळा चालतो.
nullglob सोबत स्वतंत्र धोका आहे, कारण सर्व arguments काढून टाकले तरी command चालतो. ls *.nope हे bare ls मध्ये बदलते आणि त्यामुळे संपूर्ण directory list होते. grep -l needle *.nope हे grep -l needle मध्ये बदलते. त्याच्याकडे वाचण्यासाठी कोणतीही file नसल्याने ते standard input वर प्रतीक्षा करते आणि frozen झाल्यासारखे दिसते. आवश्यक असलेल्या loop भोवती nullglob चालू करा आणि loop नंतर ते पुन्हा बंद करा.
failglob दुसरा मार्ग स्वीकारते. जुळणी न झालेला pattern ही error मानली जाते. bash तिची नोंद करते आणि command अजिबात चालत नाही. Interactive shell साठी ही चांगली setting आहे, कारण चुकीचा pattern literal नाव म्हणून rm कडे पाठवला जाण्यापासून ती थांबवते.
लपविलेल्या फाइल्स: dotglob
shopt -s dotglob
printf '%s\n' *
shopt -u dotglobतो block चालवा आणि यादीची तुलना आधीच्या साध्या printf '%s\n' * शी करा. dotglob सुरुवातीचा dot नियम काढून टाकतो. त्यामुळे * लपविलेल्या नावांनाही जुळवतो. . आणि .. या नोंदी dotglob वापरतानाही नेहमी वगळल्या जातात.
dotglob शिवाय, लपविलेल्या फाइल्सपर्यंत पोहोचण्याची नेहमीची पद्धत .* ही आहे. पूर्वी ती धोकादायक होती. 5.2 पूर्वीच्या bash मध्ये .* हे . आणि .. यांच्याशीही जुळत असे. त्यामुळे chmod -R 755 .* सारखी recursive command थेट parent directory मध्ये जात असे. Bash 5.2 मध्ये globskipdots हा पर्याय जोडला गेला. तो default ने enabled असतो आणि प्रत्येक expansion मधून . व .. बाहेर ठेवतो. त्यावर अवलंबून राहण्यापूर्वी shopt globskipdots चालवा, कारण जुन्या box वर हा पर्याय उपलब्ध नसेल.
globstar: ट्रीमध्ये सर्व स्तरांवरील जुळणाऱ्या नोंदी
shopt -s globstar
printf '%s\n' **/*.log
printf '%s\n' **/
shopt -u globstarglobstar सेट असल्यास, संपूर्ण path component बनवणारा ** कोणत्याही खोलीवरील files आणि directories शी जुळतो. यात शून्य स्तर खाली असणेही समाविष्ट आहे. त्यामुळे **/*.log सध्याच्या directory शी तसेच logs/ आणि archive/ अंतर्गत असलेल्या सर्व नोंदींशी जुळतो. शेवटी slash असलेला **/ केवळ directories शी जुळतो. त्यामुळे ट्रीची रचना पटकन पाहण्यासाठी ही सोपी पद्धत ठरते.
globstar सेट नसल्यास, ** सामान्य * प्रमाणे वागतो आणि एका directory मध्येच मर्यादित राहतो. पर्याय बंद ठेवून तोच pattern चालवा आणि तुलना करा. अन्य ठिकाणाहून copy केलेल्या recursive pattern मधून अपेक्षेपेक्षा खूपच कमी परिणाम मिळत असल्यास, याचे कारण बहुतेक वेळा हेच असते. प्रत्येक bash मध्ये globstar पूर्वनिर्धारितपणे बंद असतो.
एक मर्यादा लक्षात ठेवा: ** directories कडे निर्देश करणाऱ्या symbolic links चे अनुसरण करत नाही. symbolic links वापरून जोडलेल्या ट्रीसाठी त्याऐवजी find -L आवश्यक आहे.
nocaseglob, अक्षरांच्या आकाराबाबत भिन्न असलेल्या नावांसाठी
shopt -s nocaseglob
printf '%s\n' *.log
shopt -u nocaseglobnocaseglob मुळे फाइलनाव जुळवताना अक्षरांचा आकार दुर्लक्षित केला जातो. त्यामुळे uppercase .LOG suffix lowercase .log pattern शी जुळतो. या सूचीची पहिल्या section मधील सूचीशी तुलना करा. पर्याय लगेच पुन्हा बंद करा. तो सुरू ठेवला, तर त्या shell मध्ये त्यानंतर चालणाऱ्या प्रत्येक command साठी matching बदलते. त्यामुळे एका तासानंतर त्याचे debugging करणे गोंधळात टाकणारे ठरू शकते.
nocaseglob फक्त filename expansion साठी लागू होते. case statement किंवा double bracket test मधील pattern matching साठी संबंधित पर्याय nocasematch आहे. हे दोन्ही पर्याय स्वतंत्रपणे set करावे लागतात.
extglob: “हे नाही” असे सांगणारे patterns
Extended patterns defaultनुसार बंद असतात. ते स्वतंत्र ओळीवर सुरू करा:
shopt -s extglobयांचे पाच प्रकार आहेत. प्रत्येक प्रकार | ने विभक्त केलेली patterns ची यादी घेतो:
?(list)शून्य किंवा एक occurrence शी जुळते.*(list)शून्य किंवा अधिक occurrences शी जुळते.+(list)एक किंवा अधिक occurrences शी जुळते.@(list)दिलेल्या पर्यायांपैकी नेमक्या एका पर्यायाशी जुळते.!(list)कोणत्याही पर्यायाशी जुळत नाही अशा प्रत्येक गोष्टीशी जुळते.
Extended pattern वाचण्यासाठी option आधीपासून सुरू असलेला shell आवश्यक असतो. त्यामुळे खालील batch bash -O extglob ने सुरू केलेल्या छोट्या script मध्ये ठेवला आहे. -O flag नवीन shell ने file मधील एकच ओळ वाचण्यापूर्वी shopt option सेट करतो. त्यामुळे त्यातील प्रत्येक pattern योग्यरित्या parse होतो. Ordering महत्त्वाची का आहे, याचे कारण या section च्या शेवटच्या परिच्छेदात दिले आहे.
cat > patterns.sh <<'EOF'
printf '%s\n' @(app|error).log
printf '%s\n' app.log.+([0-9])
printf '%s\n' *.@(md|txt)
printf '%s\n' !(*.log)
printf '%s\n' !(*.log|*.md|README)
EOF
bash -O extglob patterns.sh+([0-9]) चा अर्थ एक किंवा अधिक digits असा आहे. त्यामुळे तो कोणत्याही लांबीच्या numbered suffix शी जुळतो. आधीच्या ? मर्यादेवरील हा उपाय आहे. दोन्ही patterns सलग चालवून फरक पाहणे उपयुक्त ठरते.
“everything except” साठी negation form वापरतात. extglob सुरू करण्याचे हेच मुख्य कारण आहे. याबाबत दोन गोष्टी अनेकांना अनपेक्षित वाटतात.
हे files प्रमाणेच directories शीही जुळते. त्यामुळे ls !(*.log) result मध्ये directory name ऐवजी त्या directory मधील contents दाखवते. स्वतःची names पाहण्यासाठी ls -d किंवा printf preview वापरा.
हे इतर प्रत्येक glob प्रमाणे dot rule पाळते. त्यामुळे !(*.log) हे .log ने समाप्त न होणाऱ्या प्रत्येक visible name शी जुळते. Hidden names साठी dotglob देखील सेट करावे लागते.
!(...) मधील ! हे glob negation आहे. त्याचा history expansion आणि bang character शी कोणताही संबंध नाही. ते स्वतंत्र feature आहे आणि वेगळ्या stage मध्ये चालते.
आणखी एक अडचण parse time ला येते. Bash एखादा command चालवण्यापूर्वी तो पूर्णपणे parse करतो. त्यामुळे extglob सुरू करणे आणि त्याच ओळीवर extended pattern वापरणे अयशस्वी ठरते. Option बंद असतानाच pattern parse होतो. shopt -s extglob स्वतंत्र ओळीवर, patterns च्या आधी आणि script च्या वरच्या भागाजवळ ठेवा. Function define करताना तिचा पूर्ण body parse केला जातो. त्यामुळे function call करण्यापूर्वी नव्हे, तर definition करण्यापूर्वी option सुरू केलेला असावा. वरील batch प्रमाणे shell bash -O extglob ने सुरू केल्यास parsing सुरू होण्यापूर्वीच ordering निश्चित होते.
शेल pattern expand करतो, command नाही
ही दुसरी अपयशाची पद्धत आहे. यामुळे अनेक प्रकारच्या त्रुटी समजतात.
grep -l needle *.log
find . -name '*.log'
find . -name *.logपहिल्या ओळीत matching bash करते आणि grep ला filenames ची यादी मिळते. दुसऱ्या ओळीत pattern ला quotes मध्ये ठेवले आहे. त्यामुळे find ला *.log ही पाच characters मिळतात आणि ते सुरुवातीच्या ठिकाणाखालील प्रत्येक depth वर स्वतः matching करते. तिसरी ओळ त्रुटीपूर्ण आहे. bash आधी current directory पुरता pattern expand करते. त्यामुळे find ला एका विशिष्ट नावाचा शोध घ्यायला सांगितले जाते. एखाद्या directory मध्ये एकच match असल्यास ते चुकीच्या गोष्टीचा शोध घेते आणि कोणतीही सूचना देत नाही. दोन किंवा अधिक matches असल्यास find usage error दाखवते, कारण अतिरिक्त नावे expression अपेक्षित असलेल्या ठिकाणी येतात.
नियम सोपा आहे. Pattern command साठी असेल, तर तो quotes मध्ये ठेवा. Pattern shell साठी असेल, तर तो unquoted ठेवा. हाच फरक tar आणि rsync मधील --exclude patterns तसेच grep मधील --include filters यांनाही लागू होतो.
Globs आणि regular expressions एकसारखे characters वापरत असले, तरी ते एकच नसतात. Glob मध्ये * म्हणजे characters ची कोणतीही सलग मालिका. Regular expression मध्ये * म्हणजे त्याच्या आधी आलेल्या घटकाच्या शून्य किंवा अधिक पुनरावृत्ती. त्यामुळे grep '*.log' चा अर्थ दिसतो त्यापेक्षा वेगळा असतो.
Variables साठीही operations चा हाच क्रम लागू होतो. Variable expansion नंतर globbing चालते. त्यामुळे pattern असलेला unquoted variable directory विरुद्ध expand होतो.
pat='*.log'
printf '%s\n' $pat
printf '%s\n' "$pat"Unquoted असल्यास bash variable चा result glob करते. Quoted असल्यास pattern literal राहतो. याचा आणखी एक संबंधित परिणाम लक्षात ठेवा: globbing मुळे तयार झालेले words spaces वर पुन्हा split होत नाहीत. त्यामुळे for f in *.log space असलेले filename एकाच नावाप्रमाणे हाताळते, तर for f in $(ls *.log) ते नाव दोन words मध्ये विभाजित करते. Glob वर थेट loop चालवा.
विध्वंसक pattern चालवण्यापूर्वी त्याचे पूर्वावलोकन करा
नवीन pattern साठी प्रथम rm ही command कधीही वापरू नका. Pattern एखाद्या निरुपद्रवी command सोबत चालवा, यादी वाचा आणि नंतर ओळीच्या सुरुवातीला असलेली commandच बदला.
target='!(*.log)'
printf '%s\n' $target
ls -ld -- $target
rm -- $targetPattern एकदा target मध्ये लिहिल्याने तीन ओळींमधील सुसंगतता टिकते. $target ला जाणूनबुजून quotes मध्ये ठेवलेले नाही, कारण त्यामुळे bash त्याचा विस्तार करू शकतो. printf '%s\n' प्रत्येक नाव स्वतंत्र ओळीत दाखवते आणि कोणताही बदल करत नाही. ls -ld directory मधील contents दाखवण्याऐवजी प्रत्येक entry स्वतः दाखवते. तसेच mode column मध्ये कोणत्या entries directories आहेत हे दिसते. Recursive delete करण्यापूर्वी हेच तपासणे आवश्यक आहे. drwxr-xr-x permission bits वाचणे मध्ये या column चे स्पष्टीकरण दिले आहे.
मागील command up arrow ने पुन्हा आणा आणि फक्त command बदला, म्हणजे pattern तसाच राहील. Pattern लक्षात ठेवून पुन्हा टाइप करताना चूक होण्याची शक्यता जास्त असते.
Pattern च्या आधी -- ठेवा. यामुळे options चा शेवट दर्शवला जातो. त्यामुळे - ने सुरू होणारे filename flag ऐवजी name म्हणून हाताळले जाते.
Script च्या एखाद्या भागासाठी globbing बंद करायचे असल्यास set -f ते बंद करते आणि set +f ते पुन्हा सुरू करते.
ही सवय महत्त्वाची आहे, कारण rm मध्ये undo सुविधा नाही. rm -rf ने हटवलेल्या files पुनर्प्राप्त करणे ही प्रक्रिया संथ आणि बहुतेक वेळा अपूर्ण असते. त्यामुळे पर्यायांपेक्षा preview घेण्यासाठी लागणारा वेळ खूपच कमी असतो.
काम पूर्ण झाल्यावर practice directory साफ करा. Path आधी print करा आणि तो वाचा, कारण चुकीच्या variable कडे निर्देश करणारे rm -rf हीच या section मधील संभाव्य चूक आहे.
cd ~
echo "$lab"
rm -rf -- "$lab"FAQ
find . -name *.log ला चुकीच्या फाइल्स का सापडतात?
कारण find सुरू होण्यापूर्वीच bash *.log चा विस्तार करते. Shell सध्याच्या directory विरुद्ध pattern जुळवते आणि मिळालेली filename find कडे पाठवते. त्यानंतर find प्रत्येक स्तरावर फक्त त्या नावाचा शोध घेते. दोन किंवा अधिक फाइल्स जुळल्यास, expression अपेक्षित असताना find ला अतिरिक्त arguments मिळतात आणि तो वापर-संबंधी त्रुटी दाखवतो. Pattern ला find . -name '*.log' म्हणून quote करा. त्यामुळे जुळवणी find स्वतःच्या नियमांनुसार आणि स्वतःच्या recursion सह करते.
Pattern अक्षरशः मजकूर म्हणून दिसत असल्यास त्याचा अर्थ काय?
याचा अर्थ कोणतीही जुळणी झाली नाही. डीफॉल्टनुसार, जुळणारा pattern नसल्यास bash तो बदलत नाही आणि मूळ अक्षरे command कडे पाठवतो. त्यानंतर command त्यांना filename म्हणून हाताळते. shopt -s failglob सेट केल्यास जुळणी न झालेला pattern error मध्ये बदलतो आणि command थांबतो. shopt -s nullglob सेट केल्यास त्याचा विस्तार रिकामा होतो. nullglob वापरताना कोणतेही arguments नसतानाही command योग्य राहते का ते तपासा. कारण ls *.nope मुळे केवळ ls उरते आणि ते संपूर्ण directory ची सूची दाखवते.
bash मध्ये एका pattern वगळता प्रत्येक file शी कशी जुळवणी करावी?
स्वतंत्र ओळीवर shopt -s extglob वापरून extended patterns सक्षम करा. त्यानंतर negation form वापरा. !(*.log) च्या शेवटी .log नसलेली नावे जुळवते आणि !(*.log|*.md) दोन्ही वगळते. dotglob देखील सेट केलेले नसल्यास hidden files निकालातून वगळल्या जातात. Directories देखील समाविष्ट होतात. त्यामुळे pattern rm कडे देण्यापूर्वी printf '%s\n' !(*.log) वापरून त्याचा परिणाम तपासा.
* hidden files का वगळते?
सुरुवातीचा dot अक्षरशः जुळवावा लागतो. त्यामुळे * सुरुवातीला dot असलेले नाव कधीही जुळवत नाही. Hidden names पर्यंत पोहोचण्यासाठी .* वापरा. किंवा प्रत्येक सामान्य pattern मध्ये त्यांचा समावेश करण्यासाठी shopt -s dotglob सेट करा. dotglob वापरताना . आणि .. या entries नेहमी वगळल्या जातात. .* अंतर्गत bash 5.2 आणि त्यानंतरच्या आवृत्त्यांमध्ये globskipdots option मुळे त्यांना वगळले जाते. हा option डीफॉल्टनुसार सुरू असतो.
** कार्य करण्यासाठी globstar आवश्यक आहे का?
होय. shopt -s globstar शिवाय bash ** ला सामान्य * म्हणून हाताळते. ते एका directory मधील जुळवणी करते आणि तिथेच थांबते. globstar सुरू असताना, संपूर्ण path component बनवणारा ** कोणत्याही खोलीवर जुळतो. त्यामुळे **/*.log वर्तमान directory आणि तिच्या खालील प्रत्येक subdirectory पर्यंत पोहोचते. **/ स्वतंत्रपणे वापरल्यास ते फक्त directories शी जुळते. लक्षात ठेवा की ** directories कडे निर्देश करणाऱ्या symbolic links चे अनुसरण करत नाही. त्यामुळे symlinks पासून बनलेल्या tree साठी find -L वापरा.