Bash Globbing மற்றும் Wildcards பயன்பாடு: முழு விளக்கம்
Bash shell-இல் Wildcards எவ்வாறு கோப்புப் பெயர்களை விரிவுபடுத்துகிறது என்பதை அறியுங்கள். nullglob, dotglob, globstar மற்றும் extglob ஆகியவற்றைச் சரியாகப் பயன்படுத்தும் முறைகள்.
கட்டளையை இயக்குவதற்கு முன்பு Bash globbing என்ன செய்கிறது
Bash globbing என்பது, ஒரு கட்டளையை இயக்குவதற்கு முன்பாக *.log போன்ற ஒரு pattern-ஐ, உண்மையான கோப்புப் பெயர்களின் வரிசைப்படுத்தப்பட்ட பட்டியலாக shell மாற்றும் செயல்முறையாகும். rm *.log ஒருபோதும் rm-க்கு ஒரு pattern-ஐ அனுப்புவதில்லை. Bash அந்த directory-ஐ வாசித்து, பொருந்தக்கூடிய பெயர்களைத் தேர்ந்தெடுத்து, அவற்றை வரிசைப்படுத்தி, ஒவ்வொரு பெயரையும் தனித்தனி argument-ஆகக் கொண்டு rm-ஐ இயக்குகிறது. ஒரு pattern பயன்படுத்தப்பட்டது என்பதை rm நிரலால் கண்டறிய முடியாது.
Globbing தொடர்பான ஆச்சரியமான விஷயங்கள் அனைத்தும் இந்த ஒரே ஒரு உண்மையை அடிப்படையாகக் கொண்டவை. கோப்புப் பெயர்களைப் பொருத்தும் பணி shell-ஆல் செய்யப்படுகிறது; அந்த நேரத்தில் இருக்கும் கோப்புகளுடன் அவை ஒப்பிடப்படுகின்றன. கட்டளைக்கு அந்த இறுதிப் பட்டியல் மட்டுமே தெரியும். இதற்கான முறையான பெயர் pathname expansion என்பதாகும். Globbing என்பது அன்றாடப் பயன்பாட்டில் உள்ள சொல்; இவை இரண்டுமே ஒரே பொருளைக் குறிக்கின்றன.
பயிற்சிக்காக ஒரு சிறிய மர அமைப்பை உருவாக்குதல்
கீழே உள்ள அனைத்து உதாரணங்களும் ஒரே கோப்புகளைக் கொண்டே இயங்குகின்றன. இவற்றை ஒரு தற்காலிகக் கோப்பகத்தில் (temporary directory) உருவாக்கவும். இதனால் முக்கியமான கோப்புகள் பாதிக்கப்படாது, மேலும் ஒரு கட்டளையின் மூலம் அனைத்தையும் நீக்கிவிடலாம்.
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 ஒரு புதிய கோப்பகத்தை உருவாக்கி அதன் பாதையைத் தருகிறது, மேலும் $(...) அந்தப் பாதையை lab என்ற மாறியினுள் (variable) சேமிக்கிறது. இந்த syntax உங்களுக்குப் புதியது என்றால், command substitution எவ்வாறு கட்டளையின் வெளியீட்டைப் பெறுகிறது என்பதைப் பார்க்கவும்.
நீங்கள் உருவாக்கிய அமைப்பைப் பார்த்து, உங்கள் shell-ஐச் சரிபார்க்கவும். ஏனெனில் இறுதியில் உள்ள ஒரு குறிப்பு, நீங்கள் பயன்படுத்தும் பதிப்பைப் பொறுத்தது:
ls -a
bash --versionஇங்குள்ள அனைத்தும் bash 4.0 மற்றும் அதற்குப் பிந்தைய பதிப்புகளில் வேலை செய்யும். இறுதியில் உள்ள globskipdots குறிப்பு bash 5.2 மற்றும் அதற்குப் பிந்தைய பதிப்புகளுக்குப் பொருந்தும். ஆகஸ்ட் 2026 நிலவரப்படி, தற்போதைய Ubuntu மற்றும் Debian வெளியீடுகள் இதையே வழங்குகின்றன.
முழுவதிலும் பயன்படுத்தப்பட்டுள்ள முன்னோட்டக் கட்டளை (preview command) printf '%s\n' PATTERN ஆகும். இது ஒவ்வொரு பொருத்தத்தையும் தனித்தனி வரியாக அச்சிடும் மற்றும் வட்டில் (disk) எந்த மாற்றத்தையும் செய்யாது. எனவே, உங்களுக்குத் தெளிவில்லாத எந்தவொரு pattern-க்கும் இதைப் பயன்படுத்துவது பாதுகாப்பானது.
மூன்று வைல்டு கார்டுகள்: நட்சத்திரம், கேள்விக்குறி மற்றும் அடைப்புக்குறிகள்
*எந்தவொரு எழுத்துத் தொடரையும், காலியான தொடரையும் கூட பொருத்தும்.?சரியாக ஒரு எழுத்தை மட்டும் பொருத்தும்.[...]அடைப்புக்குறிக்குள் கொடுக்கப்பட்ட தொகுப்பில் உள்ள ஏதேனும் ஒரு எழுத்தை மட்டும் பொருத்தும்.
printf '%s\n' *.log
printf '%s\n' app.log.?
printf '%s\n' app.log.[0-9]
printf '%s\n' notes.*
printf '%s\n' *நீங்கள் உருவாக்கிய கோப்புப் பட்டியலுடன் இரண்டாவது வரியை ஒப்பிட்டுப் பாருங்கள். ? என்பது ஒரு எழுத்தை மட்டுமே குறிக்கும், எனவே இரண்டு இலக்க பின்னொட்டு (suffix) அந்த அமைப்பிற்குள் வராது. அதை app.log.[0-9]* என விரிவுபடுத்தி, மீண்டும் இயக்கி வித்தியாசத்தைப் பாருங்கள்.
இந்த மூன்று வைல்டு கார்டுகளுக்கும் இரண்டு விதிகள் பொருந்தும். எந்தவொரு வைல்டு கார்டும் /-ஐப் பொருத்தாது, எனவே ஒரு பேட்டர்ன் (pattern) ஒரு டைரக்டரி நிலைக்குள்ளேயே இருக்கும். மேலும், தொடக்கத்தில் உள்ள .-ஐ அப்படியே டைப் செய்ய வேண்டும், எனவே *-ல் தொடங்கும் பேட்டர்ன் மறைக்கப்பட்ட (hidden) கோப்புகளைத் தவிர்க்கும். இந்த இரண்டாவது விதியினால்தான் rm * உங்கள் டாட்-ஃபைல்களை (dotfiles) விட்டுவிடுகிறது.
கிளாப் (glob) முடிவுகள் தற்போதைய லோகேல் (locale)-ன் வரிசைமுறைப்படி வரிசைப்படுத்தப்படுகின்றன, இது எழுத்து வாரியான உரை வரிசையாகும். எனவே, 10 என்ற பின்னொட்டு 2-க்கு முன்னால் வரிசைப்படுத்தப்படும், ஏனெனில் ஒப்பீடு எதையும் எண்ணாகக் கருதாது. printf '%s\n' app.log.* மற்றும் ls -v app.log.* ஆகியவற்றை இயக்கி இரண்டையும் ஒப்பிடுங்கள்: ls -v உட்பொதிக்கப்பட்ட எண்களை அதன் மதிப்பின் அடிப்படையில் வரிசைப்படுத்தும், sort -V அதே வேலையை ஒரு பைப்லைனில் (pipeline) செய்யும்.
எழுத்து வகுப்புகள் மற்றும் வரம்புகள் (Character classes and ranges)
அடைப்புக்குறிக்குள் (brackets) நீங்கள் ஒரு பட்டியல், ஒரு வரம்பு அல்லது ஒரு எதிர்மறையை எழுதலாம்.
[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]*வரம்புகள் (Ranges) வெவ்வேறு கணினிகளுக்கு இடையே முரண்பாடுகளை உருவாக்கும் பகுதியாகும். ஒரு வரம்பு என்பது plain ASCII-க்கு பதிலாக தற்போதைய locale-ன் collation வரிசைப்படி தீர்மானிக்கப்படுகிறது. எனவே, சில கணினிகளில் [a-z] என்பது பெரிய எழுத்துக்களுடனும் (uppercase letters) பொருந்தக்கூடும். Bash-ல் globasciiranges என்ற விருப்பம் உள்ளது, இது plain ASCII வரிசையை கட்டாயமாக்குகிறது. சமீபத்திய பதிப்புகளில் இது இயல்பாகவே செயல்படுத்தப்பட்டுள்ளது, எனவே இதை ஊகிப்பதற்கு பதிலாக shopt globasciiranges மூலம் உங்கள் கணினியில் சரிபார்க்கவும். எல்லா இடங்களிலும் ஒரே மாதிரியான பொருத்தத்தை உறுதி செய்ய வேண்டிய ஸ்கிரிப்ட் என்றால், அதன் தொடக்கத்திற்கு அருகில் LC_ALL=C என்பதைத் தனி வரியாக அமைப்பதன் மூலம் வரிசையை நீங்களே நிர்ணயிக்கலாம். இது ஒவ்வொரு வரம்பையும் மீண்டும் plain byte வரிசைக்குக் கொண்டு வரும்.
அந்த ஒதுக்கீடு (assignment) ஒரு தனி கட்டளையாக இருக்க வேண்டும். ஒரு முன்னொட்டாக (prefix) எழுதப்பட்டால், LC_ALL=C ls [a-z]* என்பது ls-க்கு மட்டுமே locale-ஐ அமைக்கும். ஆனால், ls தொடங்குவதற்கு முன்பே shell அந்த pattern-ஐ அதன் சொந்த locale-ல் விரிவுபடுத்திவிடும். இது முதல் பகுதியில் நாம் பார்த்த விதியின் மீண்டும் ஒரு வெளிப்பாடாகும்.
Brace expansion என்பது globbing அல்ல
Braces ஒரு wildcard போலத் தோன்றும், ஆனால் அவை அவ்வாறு செயல்படுவதில்லை. Brace expansion என்பது globbing-க்கு முன்பே நடைபெறுகிறது, மேலும் இது disk-ஐ ஒருபோதும் அணுகாது.
echo file{1,2,3}.txt
echo {01..10}
echo {a..e}
echo {0..20..5}மேலே உள்ள கோப்புகள் எதுவும் இல்லை என்றாலும், 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} என்பது app.log மற்றும் app.log.bak என இரண்டு சொற்களாக விரிகிறது, ஏனெனில் கமாவுக்கு (comma) முன்னால் உள்ள காலி உருப்படி அசல் உரையை அப்படியே தருகிறது. {01..10} அதன் zero padding-ஐத் தக்கவைத்துக் கொள்கிறது, ஏனெனில் எல்லைகளில் ஏதேனும் ஒன்றில் பூஜ்ஜியம் (zero) இருந்தால், bash உருவாக்கும் அனைத்து எண்களையும் ஒரே அகலத்திற்கு மாற்றுகிறது.
Expansion வரிசை, பயனர்கள் அடிக்கடி சந்திக்கும் தோல்விக்கான காரணத்தையும் விளக்குகிறது. Bash முதலில் braces-ஐ விரிவுபடுத்துகிறது, பிறகு variables-ஐ மாற்றுகிறது, இறுதியாக pathname expansion-ஐச் செய்கிறது.
n=5
echo {1..$n}$n ஒரு எண்ணாக மாறுவதற்கு முன்பே brace expansion முடிந்துவிடுகிறது, எனவே எந்த range-ம் உருவாக்கப்படுவதில்லை. அதற்குப் பதிலாக seq 1 "$n"-ஐப் பயன்படுத்தவும், அல்லது for ((i=1; i<=n; i++)) என எழுதப்பட்ட C-style loop-ஐப் பயன்படுத்தவும்.
ஒரு pattern எதனுடனும் பொருந்தாதபோது என்ன நடக்கும்
இது முதல் தோல்வி நிலை, இது அனைவரையும் ஒருமுறை சிக்க வைக்கும். இயல்பாக, bash ஒரு பொருந்தாத pattern-ஐ அப்படியே விட்டுவிடும். நீங்கள் தட்டச்சு செய்தபடியே அந்தச் சொல் command-க்குச் செல்லும், எனவே command *, ., t, m மற்றும் p ஆகிய எழுத்துக்களைப் பெற்று, அவற்றை ஒரு கோப்பின் பெயராகக் கருத முயற்சிக்கும்.
printf '%s\n' *.tmpஇது printf உடன் இருக்கும்போது பாதிப்பில்லாதது. ஆனால், கோப்புகளை உருவாக்கும் command-களுடன் இது பாதிப்பை ஏற்படுத்தும். .tmp கோப்புகள் இல்லாத ஒரு directory-ல் touch *.tmp கட்டளையை இயக்கினால், அது நேரடியாக *.tmp என்ற பெயரில் ஒரு கோப்பை உருவாக்கும். அதை நீக்கும்போது, shell மீண்டும் அதை expand செய்யாமல் இருக்க, quotation marks பயன்படுத்த வேண்டும்: rm -- '*.tmp'.
இரண்டு shell விருப்பங்கள் இந்த விதியை மாற்றும். ஒவ்வொன்றையும் எதனுடனும் பொருந்தாத ஒரு pattern-ல் சோதித்துப் பார்த்துவிட்டு, பிறகு அதை unset செய்யவும்.
shopt -s nullglob
printf '%s\n' *.tmp
shopt -u nullglob
shopt -s failglob
printf '%s\n' *.tmp
shopt -u failglobnullglob, பொருந்தாத pattern-ஐ எதுவுமே இல்லாததாக மாற்றும், எனவே நீங்கள் குறிப்பிட்டதை விடக் குறைவான arguments-உடன் command இயங்கும். loop-ஐச் சுற்றி உங்களுக்குத் தேவைப்படுவது இதுவே. nullglob முடக்கப்பட்டிருந்தால், for f in *.tmp அதன் body-ஐ ஒருமுறை இயக்கும், அப்போது f அந்த literal pattern-ஐ வைத்திருக்கும்; இது பெரும்பாலான script-களில் ஒரு பிழையாகும். nullglob இயக்கப்பட்டிருந்தால், body பூஜ்ஜிய முறை மட்டுமே இயங்கும்.
nullglob அதன் சொந்த ஆபத்தைக் கொண்டுள்ளது, ஏனெனில் அனைத்து arguments-ம் நீக்கப்பட்டாலும் ஒரு command தொடர்ந்து இயங்கும். ls *.nope என்பது வெறும் ls ஆக மாறும், இது முழு directory-யையும் பட்டியலிடும். grep -l needle *.nope என்பது grep -l needle ஆக மாறும், அதற்கு வாசிக்க கோப்பு எதுவும் இல்லாததால், அது standard input-க்காகக் காத்திருக்கும் மற்றும் உறைந்து போனது போலத் தோன்றும். loop-க்குத் தேவைப்படும்போது மட்டும் nullglob-ஐ இயக்கிவிட்டு, முடிந்ததும் அதை அணைத்துவிடவும்.
failglob மற்றொரு வழியைப் பின்பற்றுகிறது: பொருந்தாத pattern ஒரு பிழையாகக் கருதப்படும், bash அதைத் தெரிவிக்கும், மேலும் command இயங்காது. இது ஒரு interactive shell-க்குச் சிறந்த அமைப்பாகும், ஏனெனில் தவறாகத் தட்டச்சு செய்யப்பட்ட pattern, rm-க்கு ஒரு literal பெயராகச் செல்வதைத் தடுக்கிறது.
மறைக்கப்பட்ட கோப்புகள்: dotglob
shopt -s dotglob
printf '%s\n' *
shopt -u dotglobஅந்தத் தொகுதியை இயக்கி, முந்தைய printf '%s\n' * வெளியீட்டுடன் ஒப்பிட்டுப் பார்க்கவும். dotglob என்பது புள்ளியுடன் தொடங்கும் கோப்புகளைத் தவிர்க்கும் விதியை நீக்குகிறது, எனவே * மறைக்கப்பட்ட கோப்புகளையும் கண்டறியும். dotglob செயல்பாட்டில் இருந்தாலும், . மற்றும் .. ஆகிய உள்ளீடுகள் எப்போதும் தவிர்க்கப்படும்.
dotglob இல்லாமல், மறைக்கப்பட்ட கோப்புகளை அணுக .* என்பது வழக்கமான வழியாகும், இது முன்பு ஆபத்தானதாக இருந்தது. bash 5.2-க்கு முந்தைய பதிப்புகளில், .* என்பது . மற்றும் .. ஆகியவற்றையும் உள்ளடக்கியது. இதனால் chmod -R 755 .* போன்ற recursive கட்டளைகளை இயக்கும்போது, அது தற்செயலாக parent directory-க்குள் சென்றுவிடும். Bash 5.2 பதிப்பில் globskipdots என்ற விருப்பம் சேர்க்கப்பட்டது; இது இயல்பாகவே செயல்பாட்டில் இருக்கும். இது . மற்றும் .. ஆகியவற்றை அனைத்து விரிவாக்கங்களிலிருந்தும் (expansion) விலக்கி வைக்கும். பழைய கணினிகளில் இந்த வசதி இருக்காது என்பதால், இதைப் பயன்படுத்துவதற்கு முன்பு shopt globskipdots கட்டளையை இயக்கி உறுதிப்படுத்திக் கொள்ளவும்.
globstar: கோப்பு முறைமை முழுவதும் தேடுதல்
shopt -s globstar
printf '%s\n' **/*.log
printf '%s\n' **/
shopt -u globstarglobstar அமைக்கப்பட்டிருக்கும்போது, ஒரு ** முழுமையான path component-ஆக அமையும் பட்சத்தில், அது எந்த ஆழத்திலும் உள்ள கோப்புகள் மற்றும் கோப்பகங்களுடன் (directories) பொருந்தும். இது பூஜ்ஜிய ஆழத்தையும் உள்ளடக்கும் என்பதால், **/*.log என்பது தற்போதைய கோப்பகம், logs/ மற்றும் archive/-க்கு கீழே உள்ள அனைத்தையும் உள்ளடக்கும். ஒரு **/-ன் இறுதியில் slash இருந்தால், அது கோப்பகங்களை மட்டுமே குறிக்கும்; ஒரு கோப்பு முறைமையின் அமைப்பை விரைவாகப் புரிந்துகொள்ள இது ஒரு சிறந்த வழியாகும்.
globstar அமைக்கப்படாதபோது, ** ஒரு சாதாரண * போலவே செயல்பட்டு, ஒரே கோப்பகத்திற்குள் மட்டும் தேடும். இந்த விருப்பத்தை (option) முடக்கிவிட்டு அதே pattern-ஐ இயக்கி ஒப்பிட்டுப் பாருங்கள்: எங்கிருந்தோ நகலெடுக்கப்பட்ட ஒரு recursive pattern, நீங்கள் எதிர்பார்த்ததை விட மிகக் குறைந்த முடிவுகளைத் தருகிறது என்றால், அதற்கு இதுவே காரணமாக இருக்கும். அனைத்து bash பதிப்புகளிலும் globstar இயல்பாகவே முடக்கப்பட்டிருக்கும்.
தெரிந்துகொள்ள வேண்டிய ஒரு வரம்பு: ** கோப்பகங்களுக்கான symbolic links-ஐப் பின்தொடராது. Symlinks மூலம் இணைக்கப்பட்ட ஒரு கோப்பு முறைமைக்கு அதற்குப் பதிலாக find -L தேவைப்படும்.
nocaseglob, எழுத்து வடிவ வேறுபாடுகளைக் கண்டுகொள்ளாத பெயர்களுக்கு
shopt -s nocaseglob
printf '%s\n' *.log
shopt -u nocaseglobnocaseglob என்பது கோப்புப் பெயர்களைப் பொருத்தும் போது எழுத்து வடிவத்தை (case) கருத்தில் கொள்ளாது. எனவே, ஒரு பெரிய எழுத்து .LOG பின்னொட்டு (suffix), சிறிய எழுத்து .log அமைப்பைப் (pattern) பொருத்தும். முதல் பகுதியில் உள்ள பட்டியலுடன் இதை ஒப்பிட்டுப் பாருங்கள். இந்த விருப்பத்தை உடனடியாக மீண்டும் அணைத்துவிடுங்கள். ஏனெனில், இதை அப்படியே விட்டுவிட்டால், அந்த shell-ல் அடுத்து வரும் அனைத்து கட்டளைகளுக்கும் பொருத்தும் முறை மாறிவிடும். ஒரு மணி நேரம் கழித்து இதற்கான பிழைத்திருத்தம் செய்வது குழப்பத்தை ஏற்படுத்தும்.
nocaseglob என்பது கோப்புப் பெயர் விரிவாக்கத்திற்கு (filename expansion) மட்டுமே பொருந்தும். case கூற்று அல்லது இரட்டை அடைப்புக்குறி சோதனையில் (double bracket test) உள்ள அமைப்புப் பொருத்தத்திற்குத் தொடர்புடைய விருப்பம் nocasematch ஆகும். இவை இரண்டும் தனித்தனியாக அமைக்கப்பட வேண்டும்.
extglob: "இதை தவிர" என்று குறிப்பிடும் patterns
Extended patterns இயல்பாகவே முடக்கப்பட்டிருக்கும். அவற்றை ஒரு தனி வரியில் இயக்கவும்:
shopt -s extglobஇதில் ஐந்து வடிவங்கள் உள்ளன, ஒவ்வொன்றும் | மூலம் பிரிக்கப்பட்ட patterns பட்டியலை ஏற்கும்:
?(list)பூஜ்ஜியம் அல்லது ஒரு முறை வருவதை பொருத்தும்.*(list)பூஜ்ஜியம் அல்லது அதற்கு மேற்பட்ட முறை வருவதை பொருத்தும்.+(list)ஒன்று அல்லது அதற்கு மேற்பட்ட முறை வருவதை பொருத்தும்.@(list)கொடுக்கப்பட்ட மாற்றுகளில் சரியாக ஒன்றை மட்டும் பொருத்தும்.!(list)கொடுக்கப்பட்ட மாற்றுகளில் எதனுடனும் பொருந்தாத எதையும் பொருத்தும்.
Extended pattern-ஐ ஏற்கனவே இந்த வசதி இயக்கப்பட்டிருக்கும் ஒரு shell மூலம் மட்டுமே படிக்க முடியும், எனவே கீழே உள்ள batch bash -O extglob மூலம் தொடங்கப்படும் ஒரு சிறிய script-ல் இருக்க வேண்டும். -O flag, புதிய shell கோப்பின் முதல் வரியைப் படிப்பதற்கு முன்பே shopt option-ஐ அமைத்துவிடும், இதனால் அதில் உள்ள அனைத்து patterns-ம் சரியாகப் பகுப்பாய்வு (parse) செய்யப்படும். இந்த வரிசைமுறை ஏன் முக்கியம் என்பதற்கான காரணம் இந்தப் பகுதியின் கடைசி பத்தியில் உள்ளது.
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]) என்பது ஒன்று அல்லது அதற்கு மேற்பட்ட எண்களைக் குறிக்கும், எனவே இது எந்த நீளத்திலான எண்ணிடப்பட்ட suffix-ஐயும் உள்ளடக்கும். இது முன்னதாகக் குறிப்பிட்ட ? வரம்பிற்கான தீர்வாகும், இதைச் சரிபார்க்க இரண்டு patterns-ஐயும் அடுத்தடுத்து இயக்கிப் பார்ப்பது நல்லது.
Negation வடிவம் என்பது "இதைத் தவிர மற்ற அனைத்தும்" என்ற கேள்விக்கான விடையாகும், இதற்காகவே extglob-ஐ இயக்க வேண்டும். இதைப் பற்றி இரண்டு விஷயங்கள் பயனர்களை ஆச்சரியப்படுத்தும்.
இது கோப்புகளைப் போலவே கோப்பகங்களையும் (directories) பொருத்தும், எனவே ls !(*.log) கோப்பகத்தின் பெயரைப் பட்டியலிடுவதற்குப் பதிலாக, அந்த கோப்பகத்திற்குள் உள்ளவற்றைப் பட்டியலிடும். பெயர்களை மட்டும் பார்க்க ls -d அல்லது printf preview-ஐப் பயன்படுத்தவும்.
இது மற்ற glob-களைப் போலவே dot விதியையும் பின்பற்றும், எனவே !(*.log) என்பது .log-ல் முடியாத அனைத்துத் தெரியும் பெயர்களையும் உள்ளடக்கும். மறைக்கப்பட்ட பெயர்களுக்கு dotglob-ஐயும் அமைக்க வேண்டும்.
!(...)-ல் உள்ள ! என்பது glob negation ஆகும். இதற்கும் history expansion and the bang character-க்கும் எந்தத் தொடர்பும் இல்லை; அது வேறு கட்டத்தில் இயங்கும் ஒரு தனி வசதியாகும்.
இன்னொரு சிக்கல், இது parse செய்யப்படும்போது நிகழ்கிறது. Bash ஒரு கட்டளையை இயக்குவதற்கு முன்பே அதை முழுமையாகப் பகுப்பாய்வு செய்யும், எனவே extglob-ஐ இயக்குவதும், அதே வரியில் extended pattern-ஐப் பயன்படுத்துவதும் தோல்வியடையும்: ஏனெனில் option முடக்கப்பட்டிருக்கும்போதே pattern பகுப்பாய்வு செய்யப்பட்டுவிடும். shopt -s extglob-ஐ எப்போதும் தனி வரியில், patterns-க்கு முன்னால், script-ன் தொடக்கத்திற்கு அருகில் வைக்கவும். ஒரு function-ன் உள்ளடக்கமானது அந்த function வரையறுக்கப்படும்போதே பகுப்பாய்வு செய்யப்படும், எனவே அந்த function-ஐ அழைப்பதற்கு முன்பல்ல, வரையறுப்பதற்கு முன்பே option இயக்கப்பட்டிருக்க வேண்டும். மேலே உள்ள batch-ல் உள்ளது போல, bash -O extglob மூலம் shell-ஐத் தொடங்குவது, எந்தவொரு பகுப்பாய்வும் தொடங்குவதற்கு முன்பே வரிசைமுறையைச் சரியாக அமைத்துவிடும்.
Shell pattern-ஐ விரிவுபடுத்துகிறது, command அல்ல
இது இரண்டாவது தோல்வி முறை, இது பல பிழைகளுக்குக் காரணமாக அமைகிறது.
grep -l needle *.log
find . -name '*.log'
find . -name *.logமுதல் வரி bash-ஐ matching செய்ய அனுமதிக்கிறது, மேலும் grep கோப்புப் பெயர்களின் பட்டியலைப் பெறுகிறது. இரண்டாவது வரி pattern-ஐ மேற்கோள் காட்டுகிறது (quote), எனவே find ஐந்து எழுத்துக்களைக் கொண்ட *.log-ஐப் பெற்று, தொடக்கப் புள்ளியின் கீழ் உள்ள அனைத்து ஆழத்திலும் தனது சொந்த matching-ஐச் செய்கிறது. மூன்றாவது வரி ஒரு பிழையாகும்: bash முதலில் pattern-ஐ தற்போதைய கோப்பகத்தில் (directory) மட்டும் விரிவுபடுத்துகிறது, எனவே find ஒரு குறிப்பிட்ட பெயரைத் தேடுமாறு அறிவுறுத்தப்படுகிறது. ஒரே ஒரு பொருத்தம் உள்ள கோப்பகத்தில், அது தவறான ஒன்றைத் தேடுகிறது. இரண்டு அல்லது அதற்கு மேற்பட்ட பொருத்தங்கள் இருந்தால், find ஒரு usage error-ஐக் காட்டுகிறது, ஏனெனில் அது expression-ஐ எதிர்பார்க்கும் இடத்தில் கூடுதல் பெயர்கள் வருகின்றன.
இதற்கான விதி சுருக்கமானது. Pattern command-க்காக இருந்தால், அதை மேற்கோள் காட்டவும் (quote). அது shell-க்காக இருந்தால், அதை அப்படியே விடவும். இதே பிரிவினை tar மற்றும் rsync-ல் உள்ள --exclude patterns-க்கும், grep-ல் உள்ள --include filters-க்கும் பொருந்தும்.
Globs மற்றும் regular expressions ஒரே மாதிரியான எழுத்துக்களைப் பகிர்ந்தாலும், அவை ஒன்றல்ல. ஒரு glob-ல், * என்பது எந்தவொரு எழுத்துத் தொடரையும் குறிக்கும். ஒரு regular expression-ல், * என்பது அதற்கு முன்னால் உள்ளவை பூஜ்ஜியம் அல்லது அதற்கு மேற்பட்ட முறை வருவதைக் குறிக்கும், எனவே grep '*.log' பார்ப்பதற்குத் தோன்றுவதிலிருந்து முற்றிலும் மாறுபட்ட ஒன்றைக் கோருகிறது.
Variables-ம் இதே செயல்பாட்டு வரிசையைப் பின்பற்றுகின்றன. Globbing என்பது variable expansion-க்கு பிறகு நடக்கும், எனவே pattern-ஐக் கொண்ட ஒரு unquoted variable கோப்பகத்திற்கு எதிராக விரிவுபடுத்தப்படுகிறது.
pat='*.log'
printf '%s\n' $pat
printf '%s\n' "$pat"மேற்கோள் காட்டப்படாத (unquoted) நிலையில், bash variable-ன் முடிவை glob செய்கிறது. மேற்கோள் காட்டப்பட்டால், pattern அப்படியே இருக்கும். இதனுடன் தொடர்புடைய ஒரு விளைவை நீங்கள் கவனத்தில் கொள்ள வேண்டும்: globbing மூலம் உருவாக்கப்படும் சொற்கள் மீண்டும் இடைவெளிகளால் (spaces) பிரிக்கப்படுவதில்லை, எனவே for f in *.log இடைவெளியைக் கொண்ட ஒரு கோப்புப் பெயரை ஒரே பெயராகக் கையாள்கிறது, அதே சமயம் for f in $(ls *.log) அந்தப் பெயரை இரண்டு சொற்களாகப் பிரிக்கிறது. Glob-ஐ நேரடியாக loop செய்யவும்.
ஒரு அழிவுத்தன்மை கொண்ட pattern-ஐ இயக்கும் முன் அதை முன்கூட்டியே சரிபார்த்தல்
புதிய pattern ஒன்றை முயற்சிக்கும்போது, rm கட்டளையை முதலில் பயன்படுத்த வேண்டாம். அந்த pattern-ஐ பாதிப்பில்லாத ஒரு கட்டளையுடன் இயக்கி, பட்டியலைச் சரிபார்த்து, அதன் பிறகு வரியின் தொடக்கத்தில் உள்ள கட்டளையை மட்டும் மாற்றவும்.
target='!(*.log)'
printf '%s\n' $target
ls -ld -- $target
rm -- $targetஅந்த pattern-ஐ target-ல் ஒருமுறை எழுதுவது, மூன்று வரிகளும் மாறுபடுவதைத் தடுக்கிறது. $target வேண்டுமென்றே மேற்கோள் குறிகள் (quotes) இன்றி விடப்பட்டுள்ளது, ஏனெனில் அப்போதுதான் bash அதை விரிவுபடுத்த (expand) முடியும். printf '%s\n' ஒவ்வொரு கோப்பின் பெயரையும் தனி வரியில் அச்சிடும், எதையும் மாற்றாது. ls -ld கோப்பகங்களின் (directories) உள்ளடக்கத்தைப் பட்டியலிடுவதற்குப் பதிலாக, ஒவ்வொரு உள்ளீட்டையும் அப்படியே காட்டும். அதன் mode நெடுவரிசை, எந்த உள்ளீடுகள் கோப்பகங்கள் என்பதைக் குறிக்கும்; ஒரு recursive delete-ஐச் செய்வதற்கு முன் இதைத்தான் சரிபார்க்க வேண்டும். drwxr-xr-x அனுமதி பிட்களை வாசித்தல் பகுதியில் அந்த நெடுவரிசை விளக்கப்பட்டுள்ளது.
முந்தைய வரியை மீண்டும் பெற மேல்நோக்கிய அம்புக்குறியைப் (up arrow) பயன்படுத்தி, கட்டளையை மட்டும் திருத்தவும்; அப்போதுதான் pattern மாறாமல் இருக்கும். நினைவகத்திலிருந்து ஒரு pattern-ஐ மீண்டும் தட்டச்சு செய்யும்போதே தவறுகள் நிகழ்கின்றன.
pattern-க்கு முன்னால் ---ஐச் சேர்க்கவும். இது விருப்பங்களின் (options) முடிவைக் குறிக்கிறது, எனவே --ல் தொடங்கும் ஒரு கோப்பின் பெயர், ஒரு flag-ஆகக் கருதப்படாமல், கோப்பின் பெயராகவே கருதப்படும்.
ஒரு script-ன் குறிப்பிட்ட பகுதியில் globbing-ஐ முடக்க விரும்பினால், set -f அதை முடக்கும், set +f அதை மீண்டும் இயக்கும்.
rm கட்டளைக்கு undo வசதி இல்லாததால், இந்த பழக்கம் முக்கியமானது. rm -rf மூலம் நீக்கப்பட்ட கோப்புகளை மீட்டெடுத்தல் என்பது மெதுவானது மற்றும் பெரும்பாலும் முழுமையற்றது, எனவே ஒரு preview-ஐச் செய்வது, தவறு நடந்த பின் சரிசெய்வதை விட மிகக் குறைந்த செலவே ஆகும்.
பயிற்சி முடிந்ததும், அந்தப் பயிற்சி கோப்பகத்தைச் சுத்தம் செய்யவும். முதலில் பாதையை அச்சிட்டுச் சரிபார்க்கவும், ஏனெனில் தவறான variable-ஐக் குறிக்கும் rm -rf கட்டளைதான் இந்த பகுதியில் நாம் தவிர்க்க நினைக்கும் விபத்தை ஏற்படுத்தும்.
cd ~
echo "$lab"
rm -rf -- "$lab"FAQ
find . -name *.log ஏன் தவறான கோப்புகளைக் கண்டறிகிறது?
ஏனெனில் find தொடங்குவதற்கு முன்பே bash *.log-ஐ விரிவுபடுத்துகிறது (expands). ஷெல் தற்போதைய கோப்பகத்தில் (directory) உள்ள கோப்புகளை அந்த pattern-உடன் ஒப்பிட்டு, கிடைக்கும் கோப்புப் பெயர்களை find-க்கு அனுப்புகிறது. இதனால் find ஒவ்வொரு ஆழத்திலும் அந்த ஒரு பெயரை மட்டுமே தேடுகிறது. ஒன்றுக்கும் மேற்பட்ட கோப்புகள் பொருந்தினால், expression-ஐ எதிர்பார்க்கும் find-க்கு கூடுதல் arguments கிடைப்பதால், அது பயன்பாட்டுப் பிழையை (usage error) காட்டுகிறது. pattern-ஐ find . -name '*.log' என்று மேற்கோள் காட்டினால், find தனது சொந்த விதிகள் மற்றும் recursion மூலம் பொருத்தமான கோப்புகளைத் தேடும்.
எனது pattern வெறும் உரையாக (literal text) காட்டப்பட்டால் அதன் பொருள் என்ன?
எந்தக் கோப்பும் பொருந்தவில்லை என்று பொருள். இயல்பாக, பொருந்தாத pattern-ஐ bash மாற்றாமல் அப்படியே கட்டளைக்கு அனுப்பும், அது அந்தப் பெயரை ஒரு கோப்புப் பெயராகக் கருதும். பொருந்தாத pattern-ஐப் பிழையாகக் கருதி கட்டளையை நிறுத்த shopt -s failglob-ஐ அமைக்கவும், அல்லது அது எதுவுமே இல்லை என விரிவடைய shopt -s nullglob-ஐப் பயன்படுத்தவும். nullglob பயன்படுத்தும்போது, எந்த arguments-ம் இல்லாத நிலையில் கட்டளை சரியாகச் செயல்படுமா என்று சரிபார்க்கவும், ஏனெனில் ls *.nope வெறும் ls ஆக மாறி முழு கோப்பகத்தையும் பட்டியலிடும்.
bash-ல் ஒரு குறிப்பிட்ட pattern-ஐத் தவிர மற்ற அனைத்துக் கோப்புகளையும் எவ்வாறு பொருத்துவது?
shopt -s extglob மூலம் extended patterns-ஐ இயக்கி, பின் negation வடிவத்தைப் பயன்படுத்தவும். !(*.log) என்பது .log-ல் முடியாத பெயர்களைப் பொருத்தும், !(*.log|*.md) இரண்டையும் தவிர்க்கும். dotglob அமைக்கப்பட்டால் ஒழிய மறைக்கப்பட்ட கோப்புகள் (hidden files) முடிவுகளில் வராது. கோப்பகங்களும் இதில் அடங்கும் என்பதால், rm-க்குக் கொடுக்கும் முன் printf '%s\n' !(*.log) மூலம் pattern-ஐச் சரிபார்க்கவும்.
* ஏன் மறைக்கப்பட்ட கோப்புகளைத் தவிர்க்கிறது?
தொடக்கத்தில் உள்ள புள்ளி (dot) அப்படியே பொருந்த வேண்டும், எனவே * புள்ளியில் தொடங்கும் பெயர்களைப் பொருத்துவதில்லை. மறைக்கப்பட்ட பெயர்களை அணுக .*-ஐப் பயன்படுத்தவும், அல்லது அனைத்து சாதாரண pattern-களிலும் அவற்றைச் சேர்க்க shopt -s dotglob-ஐ அமைக்கவும். dotglob-ன் கீழ் . மற்றும் .. எப்போதும் தவிர்க்கப்படும். bash 5.2 மற்றும் அதற்குப் பிந்தைய பதிப்புகளில், இயல்பாகவே செயல்பாட்டில் இருக்கும் globskipdots விருப்பத்தின் மூலம் .*-லிருந்து இவை விலக்கி வைக்கப்படுகின்றன.
** வேலை செய்ய globstar தேவையா?
ஆம். shopt -s globstar இல்லையென்றால், bash **-ஐ ஒரு சாதாரண * ஆகக் கருதி, ஒரு கோப்பகத்திற்குள் மட்டும் தேடி நிறுத்திவிடும். globstar செயல்பாட்டில் இருக்கும்போது, முழு path component-ஆக இருக்கும் ** எந்த ஆழத்திலும் பொருந்தும். எனவே **/*.log தற்போதைய கோப்பகம் மற்றும் அதன் கீழ் உள்ள அனைத்து துணைக்கோப்பகங்களையும் அடையும், **/ கோப்பகங்களை மட்டும் பொருத்தும். ** symbolic links-ஐப் பின்பற்றுவதில்லை என்பதை நினைவில் கொள்க, எனவே symlinks மூலம் உருவாக்கப்பட்ட tree-க்கு find -L-ஐப் பயன்படுத்தவும்.