ওয়াইল্ডকার্ড সম্প্রসারণ থেকে আমি কীভাবে প্রথম ম্যাচটি পেতে পারি?


38

বাশ এবং জেড-এর মতো শেলগুলি ওয়াইল্ডকার্ডকে আর্গুমেন্টে প্রসারিত করে, যতগুলি আর্গুমেন্টের সাথে মেলে:

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

তবে আমি যদি চাই যে কেবল প্রথম ম্যাচটিই ফিরে আসুক, সমস্ত ম্যাচ নয়?

$ echo *.txt
1.txt

আমি শেল-নির্দিষ্ট সমাধানগুলিতে কিছু মনে করি না, তবে আমি এমন একটি সমাধান চাই যা ফাইলনামে হোয়াইটস্পেসের সাথে কাজ করে।


ls * .txt | মাথা -1?
আর্চেমার

1
@ আরচেমার: ফাইলের নামগুলিতে নতুন লাইনের সাথে কাজ করে না।
ফ্লিম

উত্তর:


25

ব্যাশের একটি দৃ way় উপায় হ'ল একটি অ্যারেতে প্রসারিত করা এবং কেবল প্রথম উপাদানটি আউটপুট করা:

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করে কিছু বিশ্বজুড়ে কিছু ম্যাচ প্রসারিত করতে পারি, তারপরে আমরা নিরাপদে একটি অ্যারেতে প্রসারিত করি এবং শেষ পর্যন্ত সম্পূর্ণ সেট করি যাতে উদ্ধৃতি শক্তিশালী।

আপনি আংশিকভাবে এটিকে (প্রোগ্রামগতভাবে একটি গ্লোব প্রসারিত করতে পারেন ) compgen -Gবাশের সাথে করতে পারেন, তবে এটি শক্তিশালী নয় কারণ এটি স্টাডআউটকে অকার্যকর করে তোলে।

যথারীতি, সমাপ্তি বরং ভরাট, এটি পরিবেশের ভেরিয়েবল সহ অন্যান্য জিনিসের সমাপ্তি ভঙ্গ করে ( ডিফল্ট আচরণ অনুকরণের বিশদটির জন্য এখানে_bash_def_completion() ফাংশনটি দেখুন )।

আপনি কেবলমাত্র compgenএকটি সমাপ্তির ফাংশনের বাইরেও ব্যবহার করতে পারেন :

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

একটি বিষয় লক্ষণীয় যে "~" কোনও গ্লোব নয়, এটি বাশ দ্বারা বিস্তারের পৃথক পর্যায়ে পরিচালিত হয়, যেমন $ ভেরিয়েবল এবং অন্যান্য বিস্তৃতি। compgen -Gকেবল ফাইলের নামটি গ্লোব্বিং করে compgen -Wতবে সম্ভবত ব্যাশের ডিফল্ট সম্প্রসারণ সমস্ত দেয়, সম্ভবত খুব বেশি বিস্তৃত (সহ ``এবং $())। ভিন্ন -G, -W হয় নিরাপদে উদ্ধৃত (আমি অসমতা ব্যাখ্যা না করতে পারেন)। যেহেতু এর উদ্দেশ্য -Wহ'ল এটি টোকেনগুলি প্রসারিত করে, এর অর্থ এটি এমন একটি ফাইল উপস্থিত না থাকলেও এটি "a" থেকে "a" প্রসারিত করবে, সুতরাং এটি সম্ভবত আদর্শ নয়।

এটি বোঝা সহজ, তবে এতে অযাচিত পার্শ্ব-প্রতিক্রিয়া থাকতে পারে:

_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মানগুলি নিরাপদে উদ্ধৃত করার জন্য ব্যবহারটি নোট করুন ।

একটি চূড়ান্ত বিকল্প হ'ল জিএনইউ ইউটিলিটিগুলির সাথে 0-সীমাবদ্ধ আউটপুট ব্যবহার করা ( ব্যাশ এফএকিউ দেখুন ):

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 $ ম্যাচগুলি পেতে, আপনি 21:39*.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কোনও ডিরেক্টরিতে কেবল একটি সংস্করণ থাকে তবে সেই ডিরেক্টরি সংস্করণটি চিত্র থেকে অন্য চিত্রে পরিবর্তিত হতে পারে 🤷


U&L এ আপনাকে স্বাগতম! এটি বেশিরভাগ ডিরেক্টরি নামগুলি পরিচালনা করে তবে এটি একটি নতুন লাইনের সাথে ডিরেক্টরিগুলির নাম হ্যান্ডেল করে না। এটির মতো ডিরেক্টরি তৈরি করার চেষ্টা করুন mkdir "$(echo one; echo two)"এবং দেখুন আমার অর্থ।
ফ্লিম

অন্যান্য বিকল্পের সাথে তুলনায় সুবিধা কী, বিশেষত যে সংস্করণটি ব্যবহার করে tail?
রালফ্রেডল

স্ট্যান্ডার্ডটি dirnameকেবল একটি একক পথের নাম নেয়, সুতরাং আপনি যদি না জানেন যে আপনার নির্দিষ্ট প্রয়োগটি এটি সমর্থন করে না তবে আপনি একাধিক পাথনামের উপর কাজ করে এটির উপর নির্ভর করতে পারবেন না।
কুসালানন্দ

@ ফ্লিম গুড পয়েন্ট; আমি মনে করি বেশিরভাগ বিকাশকারীদের যদি তাদের ফোল্ডারের কাঠামোতে নতুন লাইন থাকে তবে সেগুলি মোকাবেলায় আরও বড় সমস্যা হবে ... আমি এর সাথে কখনই ডিল করতে হয়নি এবং আমি যে অর্ধ-শালীন পাত্রে এবং সফটওয়্যার ব্যবহার করছি তার সাথে প্রত্যাশা করবেন না
অ্যান্ড্রু ওড্রি

@ রালফফ্রিডল ভাল প্রশ্ন; এটি মূলত এমন কোনও কিছুর বাইরে ফিল্টার করে যা কোনও বৈধ ডিরেক্টরি নয় (এবং এটি তালিকা / ট্র্যাভার্সের তালিকা তৈরি করে না ..)
অ্যান্ড্রু ওড্রি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.