मैं बैश में पथ स्ट्रिंग से फ़ाइल प्रत्यय और पथ भाग को कैसे निकालूं?


396

इस तरह के एक स्ट्रिंग फ़ाइल पथ को देखते हुए /foo/fizzbuzz.bar, मैं कैसे fizzbuzzकहा स्ट्रिंग के सिर्फ हिस्से को निकालने के लिए बैश का उपयोग करेगा ?


जानकारी आप में पा सकते हैं बैश मैनुअल , के लिए रंग-रूप ${parameter%word}और ${parameter%%word}भाग मिलान अनुभाग अनुगामी में।
1ac0

जवाबों:


573

यहाँ यह बैश में # और% ऑपरेटरों के साथ कैसे करना है।

$ x="/foo/fizzbuzz.bar"
$ y=${x%.bar}
$ echo ${y##*/}
fizzbuzz

${x%.bar}${x%.*}एक डॉट के ${x%%.*}बाद सब कुछ हटाने या पहली डॉट के बाद सब कुछ हटाने के लिए भी हो सकता है ।

उदाहरण:

$ x="/foo/fizzbuzz.bar.quux"
$ y=${x%.*}
$ echo $y
/foo/fizzbuzz.bar
$ y=${x%%.*}
$ echo $y
/foo/fizzbuzz

दस्तावेज को बाश मैनुअल में पाया जा सकता है । भाग मिलान अनुभाग के लिए देखें ${parameter%word}और ${parameter%%word}अनुगामी करें।


मैंने इसे एक का उपयोग करते हुए समाप्त कर दिया क्योंकि यह सबसे अधिक लचीला था और कुछ अन्य समान चीजें थीं जो मैं भी करना चाहता था कि यह अच्छी तरह से किया था।
लॉरेंस जॉनसन

यह संभवतः पोस्ट किए गए सभी उत्तरों में से सबसे अधिक लचीला है, लेकिन मुझे लगता है कि बेसनेम और dirname कमांड के उत्तर देने वाले उत्तर कुछ ध्यान देने योग्य हैं। अगर आप किसी अन्य फैंसी पैटर्न मिलान की जरूरत नहीं है तो वे सिर्फ चाल हो सकते हैं।
mgadda

7
इसे $ {x% .bar} क्या कहते हैं? मैं इसके बारे में और जानना चाहूंगा।
तुलसी

14
@ बासिल: पैरामीटर विस्तार। एक कंसोल प्रकार पर "मैन बैश" और फिर टाइप करें "/ पैरामीटर विस्तार"
ज़ैन लिंक्स

1
मुझे लगता है कि 'मैन बैश' स्पष्टीकरण समझ में आता है यदि आप पहले से ही जानते हैं कि यह क्या करता है या यदि आपने खुद को कठिन तरीके से आजमाया है। यह लगभग संदर्भ के रूप में बुरा है। मैं इसके बजाय सिर्फ गूगल करूँगा।
ट्रिपलएबिग

225

बेसन कमांड को देखें:

NAME="$(basename /foo/fizzbuzz.bar .bar)"

11
संभवतः वर्तमान में प्रस्तुत सभी समाधानों में सबसे सरल ... हालाँकि मैं बैकटिक्स के बजाय $ (...) का उपयोग करूँगा।
माइकल जॉनसन

6
सबसे सरल लेकिन एक निर्भरता जोड़ता है (एक विशाल या अजीब नहीं, मैं मानता हूं)। इसके लिए प्रत्यय जानना भी जरूरी है।
बजे विंको वर्सलोविच

और अंत से कुछ भी निकालने के लिए इस्तेमाल किया जा सकता है, मूल रूप से यह अंत से सिर्फ एक स्ट्रिंग हटाने का काम करता है।
स्मार

2
समस्या समय की मार है। मैं बस बेसिन का उपयोग करते हुए 800 फ़ाइलों को संसाधित करने के लिए लगभग 5min ले बैश को देखने के बाद इस चर्चा के लिए सवाल खोजा। उपरोक्त रेगेक्स पद्धति का उपयोग करते हुए, समय लगभग 7sec तक कम हो गया था। यद्यपि यह उत्तर प्रोग्रामर के लिए प्रदर्शन करने में आसान है, लेकिन हिट का समय बहुत अधिक है। इसमें एक दो हज़ार फाइलों के साथ एक फ़ोल्डर की कल्पना करो! मेरे पास कुछ ऐसे फोल्डर हैं।
xizdaqrian

@xizdaqrian यह बिल्कुल गलत है। यह एक सरल कार्यक्रम है, जिसे वापस लौटने में आधा सेकंड नहीं लेना चाहिए। मैंने अभी समय पाया / घर / मुझे / देव -नाम "* .py" .py -exec basename {} \; और इसने 1 सेकंड में 1500 फाइलों के लिए एक्सटेंशन और डायरेक्टरी को छीन लिया।
लेज़्ज़लो ट्रेज़्काई

40

शुद्ध बैश, दो अलग-अलग ऑपरेशनों में किया गया:

  1. पथ-स्ट्रिंग से पथ निकालें:

    path=/foo/bar/bim/baz/file.gif
    
    file=${path##*/}  
    #$file is now 'file.gif'
  2. पथ-स्ट्रिंग से एक्सटेंशन निकालें:

    base=${file%.*}
    #${base} is now 'file'.

18

बेसनेम का उपयोग करके मैंने इसे प्राप्त करने के लिए निम्नलिखित का उपयोग किया:

for file in *; do
    ext=${file##*.}
    fname=`basename $file $ext`

    # Do things with $fname
done;

इसके लिए फ़ाइल एक्सटेंशन का कोई प्राथमिक ज्ञान नहीं है और तब भी काम करता है जब आपके पास एक फ़ाइल नाम है जिसमें यह फ़ाइल नाम है (इसके विस्तार के सामने); basenameहालांकि इसे कार्यक्रम की आवश्यकता होती है , लेकिन यह GNU कोरुटिल्स का हिस्सा है, इसलिए इसे किसी भी डिस्ट्रो के साथ जहाज करना चाहिए।


1
बहुत बढ़िया जवाब! एक्सटेंशन को बहुत साफ तरीके से हटाता है, लेकिन यह हटा नहीं देता है। फ़ाइल नाम के अंत में।
मेट्रिक्स

3
@metrix बस "जोड़ें।" $ ext से पहले, यानी: fname=`basename $file .$ext`
कार्लोस ट्रॉनकोसो

13

शुद्ध बैश रास्ता:

~$ x="/foo/bar/fizzbuzz.bar.quux.zoom"; 
~$ y=${x/\/*\//}; 
~$ echo ${y/.*/}; 
fizzbuzz

इस कार्यक्षमता को "पैरामीटर विस्तार" के तहत मैन बैश पर समझाया गया है। नॉन बैश तरीके लाजिमी हैं: awk, perl, sed और इतने पर।

संपादित करें: फ़ाइल प्रत्यय में डॉट्स के साथ काम करता है और प्रत्यय (विस्तार) को जानने की आवश्यकता नहीं है, लेकिन नाम में डॉट्स के साथ काम नहीं करता है।


11

बेसनेम और dirname फ़ंक्शंस जो आपके बाद हैं:

mystring=/foo/fizzbuzz.bar
echo basename: $(basename "${mystring}")
echo basename + remove .bar: $(basename "${mystring}" .bar)
echo dirname: $(dirname "${mystring}")

आउटपुट है:

basename: fizzbuzz.bar
basename + remove .bar: fizzbuzz
dirname: /foo

1
यह यहाँ के हवाले से ठीक करने के लिए मददगार होगा - शायद के माध्यम से इस चलाने shellcheck.net साथ mystring=$1के बजाय वर्तमान निरंतर मूल्य (जो कई बार चेतावनी को दबाने होगा, रिक्त स्थान / ग्लोब वर्ण / आदि को रोकने के लिए नहीं कुछ किया जा रहा है), और समस्या का पता लगाने में यह पाता?
चार्ल्स डफी

खैर, मैंने $ रहस्य में उद्धरण चिह्नों का समर्थन करने के लिए कुछ उपयुक्त बदलाव किए। गोश यह बहुत समय पहले मैंने लिखा था :)
जेरब

परिणामों को उद्धृत करने के लिए और सुधार किया जाएगा: echo "basename: $(basename "$mystring")"- इस तरह से यदि mystring='/foo/*'आप खत्म होने के बाद* वर्तमान निर्देशिका में फ़ाइलों की सूची के साथ प्रतिस्थापित नहीं करते हैं । basename
चार्ल्स डफी

6

का उपयोग करते हुए basenameमान लिया गया है आप जानते हैं कि क्या फाइल एक्सटेंशन है, यह नहीं है कि यह?

और मेरा मानना ​​है कि विभिन्न नियमित अभिव्यक्ति सुझाव एक फ़ाइलनाम के साथ सामना नहीं करते हैं जिसमें एक से अधिक "हैं।"

निम्नलिखित डबल डॉट्स के साथ सामना करने के लिए लगता है। ओह, और फ़ाइल नाम जिसमें "/" खुद है (सिर्फ किक के लिए)

पास्कल को परोपकार करने के लिए, "क्षमा करें कि यह स्क्रिप्ट इतनी लंबी है। मेरे पास इसे छोटा करने का समय नहीं था"


  #!/usr/bin/perl
  $fullname = $ARGV[0];
  ($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
  ($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
  print $basename . "\n";
 

1
यह अच्छा और मजबूत है
गौरव जैन

4

इस उत्तर में इस्तेमाल POSIX अनुरूप सिंटैक्स के अलावा ,

basename string [suffix]

जैसे की

basename /foo/fizzbuzz.bar .bar

GNUbasename एक और वाक्यविन्यास का समर्थन करता है:

basename -s .bar /foo/fizzbuzz.bar

उसी परिणाम के साथ। अंतर और फायदा यह है कि -sइसका मतलब है -a, जो कई तर्कों का समर्थन करता है:

$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar

यह भी -zविकल्प का उपयोग करके NUL बाइट्स के साथ आउटपुट को अलग करके फ़ाइलनाम-सुरक्षित बनाया जा सकता है , उदाहरण के लिए इन फ़ाइलों में रिक्त स्थान, newlines और ग्लोब वर्ण (द्वारा उद्धृत ls):

$ ls has*
'has'$'\n''newline.bar'  'has space.bar'  'has*.bar'

एक सरणी में पढ़ना:

$ readarray -d $'\0' arr < <(basename -zs .bar has*)
$ declare -p arr
declare -a arr=([0]=$'has\nnewline' [1]="has space" [2]="has*")

readarray -dबैश 4.4 या नए की आवश्यकता है। पुराने संस्करणों के लिए, हमें लूप करना होगा:

while IFS= read -r -d '' fname; do arr+=("$fname"); done < <(basename -zs .bar has*)

इसके अलावा, निर्दिष्ट प्रत्यय आउटपुट में हटा दिया जाता है यदि मौजूद है (और अन्यथा अनदेखा किया गया है)।
अक्ष १६१ a


3

यदि आप अन्य पोस्ट में सुझाए गए बेसनेम का उपयोग नहीं कर सकते हैं, तो आप हमेशा सेड का उपयोग कर सकते हैं। यहाँ एक (बदसूरत) उदाहरण है। यह सबसे बड़ा नहीं है, लेकिन यह वांछित स्ट्रिंग को निकालने और वांछित स्ट्रिंग के साथ इनपुट को बदलने के द्वारा काम करता है।

echo '/foo/fizzbuzz.bar' | sed 's|.*\/\([^\.]*\)\(\..*\)$|\1|g'

जो आपको आउटपुट मिलेगा

fizzbuzz


यद्यपि यह मूल प्रश्न का उत्तर है, यह कमांड तब उपयोगी है जब मेरे पास स्क्रीन पर प्रिंट करने के लिए आधार नामों को निकालने के लिए किसी फ़ाइल में पथ की पंक्तियाँ हैं।
सांचोल चोई

2

सुझाए गए पर्ल समाधान से सावधान रहें: यह पहली डॉट के बाद कुछ भी हटा देता है।

$ echo some.file.with.dots | perl -pe 's/\..*$//;s{^.*/}{}'
some

यदि आप इसे पर्ल के साथ करना चाहते हैं, तो यह काम करता है:

$ echo some.file.with.dots | perl -pe 's/(.*)\..*$/$1/;s{^.*/}{}'
some.file.with

लेकिन यदि आप बैश का उपयोग कर रहे हैं, तो y=${x%.*}(या basename "$x" .extयदि आप विस्तार जानते हैं) के समाधान बहुत सरल हैं।


1

बेसन ऐसा करता है, जो रास्ता निकालता है। यह दिए गए प्रत्यय को हटा देगा और यदि यह फ़ाइल के प्रत्यय से मेल खाता है, लेकिन आपको कमांड को देने के लिए प्रत्यय को जानना होगा। अन्यथा आप एमवी का उपयोग कर सकते हैं और यह पता लगा सकते हैं कि नया नाम किसी और तरीके से क्या होना चाहिए।


1

पूर्ण-पथ के बिना फ़ाइल नाम प्राप्त करने के लिए दूसरे-टॉप-रेटेड उत्तर के साथ शीर्ष-रेटेड उत्तर को संयोजित करना:

$ x="/foo/fizzbuzz.bar.quux"
$ y=(`basename ${x%%.*}`)
$ echo $y
fizzbuzz

आप यहाँ एक सरणी का उपयोग क्यों कर रहे हैं? इसके अलावा, बेसन का उपयोग क्यों करें?
कोडफोस्टर
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.