वाइल्डकार्ड विस्तार से मुझे पहला मैच कैसे मिल सकता है?


38

बैश और ज़श जैसे गोले वाइल्डकार्ड को तर्कों में विस्तारित करते हैं, पैटर्न से मेल खाते हुए कई तर्क:

$ echo *.txt
1.txt 2.txt 3.txt

लेकिन क्या होगा अगर मैं केवल पहला मैच लौटा दूं, सभी मैच नहीं?

$ echo *.txt
1.txt

मुझे शेल-विशिष्ट समाधानों से ऐतराज नहीं है, लेकिन मैं एक ऐसा समाधान चाहूंगा जो कि फिल्टनाम में व्हॉट्सएप के साथ काम करता हो।


ls * .txt | सिर -1?
आर्केमेयर

1
@ आर्चर: फ़ाइल नाम में नई कहानियों के साथ काम नहीं करता है।
फ्लिम

जवाबों:


25

बैश में एक मजबूत तरीका एक सरणी में विस्तार करना है, और केवल पहले तत्व का उत्पादन करना है:

pattern="*.txt"
files=( $pattern )
echo "${files[0]}"  # printf is safer!

(आप यहां तक ​​कि बस echo $files, एक लापता सूचकांक को [0] माना जा सकता है।)

यह सुरक्षित रूप से अंतरिक्ष / टैब / न्यूलाइन और अन्य मेटाचैकर को संभालता है जब फाइलनाम का विस्तार करता है। ध्यान दें कि प्रभावी रूप से स्थानीय सेटिंग्स "पहले" क्या बदल सकती हैं।

आप इसे बश समापन समारोह के साथ अंतःक्रियात्मक रूप से भी कर सकते हैं :

_echo() {
    local cur=${COMP_WORDS[COMP_CWORD]}   # string to expand

    if compgen -G "$cur*" > /dev/null; then
        local files=( ${cur:+$cur*} )   # don't expand empty input as *
        [ ${#files} -ge 1 ] && COMPREPLY=( "${files[0]}" )
    fi
}
complete -o bashdefault -F _echo echo

यह _echoफ़ंक्शन को echoकमांड के लिए तर्कों को पूरा करने के लिए बाध्य करता है (सामान्य पूरा होने पर ओवरराइडिंग)। एक अतिरिक्त "*" ऊपर के कोड में जोड़ा गया है, आप बस आंशिक फ़ाइल नाम पर टैब हिट कर सकते हैं और उम्मीद है कि सही बात होगी।

कोड है थोड़ा घुमावदार, बल्कि सेट या मान से nullglob( shopt -s nullglob) हम जाँच compgen -Gकुछ मैचों के लिए ग्लोब विस्तार कर सकते हैं, तो हम एक सरणी में सुरक्षित रूप से विस्तार, और अंत में COMPREPLY ताकि हवाले से मजबूत है निर्धारित किया है।

आप आंशिक रूप से ऐसा कर सकते हैं (प्रोग्रामेटिक रूप से ग्लोब का विस्तार कर सकते हैं ) बैश के साथ compgen -G, लेकिन यह मजबूत नहीं है क्योंकि यह स्टडआउट के लिए अयोग्य है।

हमेशा की तरह, पूरा होने के बजाय भयावह है, यह पर्यावरण चर सहित अन्य चीजों के पूरा होने को तोड़ता है ( डिफ़ॉल्ट व्यवहार का अनुकरण करने के विवरण के लिए यहां_bash_def_completion() फ़ंक्शन देखें )।

तुम भी compgenएक पूरा समारोह के बाहर उपयोग कर सकते हैं :

files=( $(compgen -W "$pattern") )

ध्यान देने वाली बात यह है कि "~" एक ग्लोब नहीं है, इसे विस्तार के एक अलग चरण में बैश द्वारा नियंत्रित किया जाता है, क्योंकि $ चर और अन्य विस्तार हैं। compgen -Gबस ग्लोबिंग को नामांकित करता है, लेकिन compgen -Wआपको सभी बैश के डिफ़ॉल्ट विस्तार देता है, हालांकि संभवतः बहुत सारे विस्तार (सहित ``और $())। विपरीत -G, -W है सुरक्षित रूप से उद्धृत (मैं असमानता की व्याख्या नहीं कर सकते हैं)। चूंकि इसका उद्देश्य -Wयह है कि यह टोकन का विस्तार करता है, इसका मतलब है कि यह "ए" से "ए" तक विस्तृत होगा, भले ही ऐसी कोई फ़ाइल मौजूद न हो, इसलिए यह शायद आदर्श नहीं है।

यह समझना आसान है, लेकिन इसके अवांछित दुष्प्रभाव हो सकते हैं:

_echo() {
    local cur=${COMP_WORDS[COMP_CWORD]}
    local files=( $(compgen -W "$cur") ) 
    printf -v COMPREPLY %q "${files[0]}"  
}

फिर:

touch $'curious \n filename'

echo curious*tab

printf %qमानों को सुरक्षित रूप से उद्धृत करने के उपयोग पर ध्यान दें ।

एक अंतिम विकल्प GNU उपयोगिताओं के साथ 0-सीमांकित आउटपुट का उपयोग करना है ( bash FAQ देखें ):

pattern="*.txt"
while IFS= read -r -d $'\0' filename; do 
    printf '%q' "$filename"; 
    break; 
done < <(find . -maxdepth 1 -name "$pattern" -printf "%f\0" | sort -z )

यह विकल्प आपको सॉर्टिंग ऑर्डर पर थोड़ा अधिक नियंत्रण देता है (एक ग्लोब का विस्तार करते समय ऑर्डर आपके लोकेल के अधीन होगा / LC_COLLATEऔर केस को फोल्ड नहीं कर सकता है), लेकिन अन्यथा ऐसी छोटी समस्या के लिए एक बड़ा हथौड़ा है;;


20

Zsh में, [1] ग्लोब क्वालीफायर का उपयोग करें । ध्यान दें कि भले ही यह विशेष मामला अधिकांश एक मैच में लौटता है, लेकिन यह अभी भी एक सूची है, और ग्लोब संदर्भों में विस्तारित नहीं हैं, जो एक शब्द जैसे असाइनमेंट (एक तरफ असाइनमेंट) की अपेक्षा करते हैं।

echo *.txt([1])

Ksh या bash में, आप एक सरणी में मैचों की पूरी सूची को भर सकते हैं और पहले तत्व का उपयोग कर सकते हैं।

tmp=(*.txt)
echo "${tmp[0]}"

किसी भी शेल में, आप स्थितीय पैरामीटर सेट कर सकते हैं और पहले एक का उपयोग कर सकते हैं।

set -- *.txt
echo "$1"

यह स्थितिगत मापदंडों को स्पष्ट करता है। यदि आप ऐसा नहीं चाहते हैं, तो आप एक उपधारा का उपयोग कर सकते हैं।

echo "$(set -- *.txt; echo "$1")"

आप एक फ़ंक्शन का भी उपयोग कर सकते हैं, जिसमें स्थितीय मापदंडों का अपना सेट है।

set_to_first () {
  eval "$1=\"\$2\""
}
set_to_first f *.txt
echo "$f"

1
और पहली $ n $ मैचों प्राप्त करने के लिए, आप उपयोग कर सकते हैं*.txt([1,n])
Emre

6

प्रयत्न:

for i in *.txt; do printf '%s\n' "$i"; break; done
1.txt

एक नोट जो फ़ाइल नाम के विस्तार को वर्तमान लोकेल में प्रभाव के कोलाटिंग अनुक्रम के अनुसार क्रमबद्ध करता है।



1

मैं बस एक ही बात को आश्चर्यचकित करते हुए इस पुराने सवाल पर ठोकर खा गया। मैंने इसे समाप्त किया:

echo $(ls *.txt | head -n1)

आप निश्चित रूप से, किसी भी अन्य संख्या के headसाथ tailऔर उसके -n1साथ बदल सकते हैं ।


यदि आप उन फ़ाइलों के साथ काम कर रहे हैं, जिनके नाम में नई सुर्खियाँ हैं, तो उपरोक्त अभ्यस्त कार्य। न्यूलाइन्स के साथ काम करने के लिए आप इनमें से किसी का भी उपयोग कर सकते हैं:

  • ls -b *.txt | head -n1 | sed -E 's/\\n/\n/g' (बीएसडी पर काम नहीं करता है)
  • ls -b *.txt | head -n1 | sed -e 's/\\n/\'$'\n/g'
  • ls -b *.txt | head -n1 | perl -pe 's/\\n/\n/g'
  • echo -e "$(ls -b *.txt | head -n1)" (किसी विशेष चरित्र के साथ काम करता है)

3
नहीं, अगर फिल्म का नाम नया है तो वह असफल हो जाएगी।
आइजैक

7
हम किस तरह की पागल दुनिया में रहते हैं, जहां फ़ाइल नाम में नई सुर्खियाँ हैं?
बिलीनाह

-1

एक उपयोग मामला जिसका मैं अक्सर सामना करता हूं, ग्लोब विस्तार के बाद या तो शीर्ष / निचले निर्देशिका को परिभाषित कर रहा है (उदाहरण के लिए एसडीके से बना एक निर्देशिका या उपकरण का निर्माण)। इस स्थिति में मैं आमतौर पर उस निर्देशिका के नाम को शेल स्क्रिप्ट के भीतर कुछ स्थानों पर उपयोग के लिए एक चर में सहेजना चाहता हूं।

यह आदेश आमतौर पर मेरे लिए करता है:

export SDK_DIR=$(dirname /path/to/versioned/sdks/*/. | tail -n1)

अस्वीकरण: ग्लोब विस्तार आपके फ़ोल्डर्स को सेवर द्वारा क्रमबद्ध करने वाला नहीं है; आपको चेतावनी दी गई थी। यह बहुत अच्छा है यदि आपके पास Dockerfileनिर्देशिका का केवल एक ही संस्करण है, लेकिन वह निर्देशिका संस्करण छवि से छवि image में भिन्न हो सकता है


U & L में आपका स्वागत है! यह अधिकांश निर्देशिका नामों को संभालता है, लेकिन यह निर्देशिका नामों को एक नई रेखा के साथ नहीं संभालता है। इस तरह एक निर्देशिका बनाने का प्रयास करें mkdir "$(echo one; echo two)"और देखें कि मेरा क्या मतलब है।
फ्लिम

अन्य विकल्पों की तुलना में क्या लाभ है, विशेष रूप से संस्करण जो उपयोग करता है tail?
राल्फफ्राइडल

मानक dirnameकेवल एक ही मार्गनाम लेता है, इसलिए जब तक आप यह नहीं जानते कि यह आपके विशेष कार्यान्वयन का समर्थन करता है, तब तक आप कई पथनामों पर काम करने पर भरोसा नहीं कर सकते।
Kusalananda

@ फील गुड प्वाइंट; मुझे लगता है कि अधिकांश डेवलपर्स को इससे निपटने के लिए बड़ी समस्या होगी अगर उनके फ़ोल्डर की संरचना में नई सीमाएं हैं ... मुझे कभी भी इससे निपटना नहीं पड़ा है, और मैं किसी भी आधे-सभ्य कंटेनर और सॉफ़्टवेयर के साथ उम्मीद नहीं कर रहा हूं।
एंड्रयू ओडरी

@RalfFriedl अच्छा सवाल; यह अनिवार्य रूप से ऐसी किसी भी चीज़ को फ़िल्टर करता है जो एक वैध निर्देशिका नहीं है (और सूची / ट्रैवर्स और .. नहीं होगी)
एंड्रयू ओडरी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.