बैश में फ़ाइल नाम और एक्सटेंशन निकालें


2104

मैं फ़ाइल नाम (बिना एक्सटेंशन) और एक्सटेंशन अलग से प्राप्त करना चाहता हूं।

अब तक मैंने पाया सबसे अच्छा समाधान है:

NAME=`echo "$FILE" | cut -d'.' -f1`
EXTENSION=`echo "$FILE" | cut -d'.' -f2`

यह गलत है क्योंकि यह काम नहीं करता है यदि फ़ाइल नाम में कई .वर्ण हैं। अगर, हम कहते हैं, मेरे पास है a.b.js, यह विचार करेगा aऔर b.js, के बजाय a.bऔर js

इसे पायथन में आसानी से किया जा सकता है

file, ext = os.path.splitext(path)

लेकिन मैं अगर संभव हो तो इसके लिए पायथन दुभाषिया को आग नहीं लगाना चाहता।

कोई बेहतर विचार?


यह सवाल इस बैश तकनीक और कई अन्य संबंधित लोगों को समझाता है।
jjclarkson

28
नीचे दिए गए शानदार उत्तरों को लागू करते समय, अपने वेरिएबल में पेस्ट न करें जैसे कि मैं यहां दिखाता हूं गलत: extension="{$filename##*.}" जैसे मैंने थोड़ी देर के लिए किया था! $कर्ल के बाहर ले जाएँ : सही: extension="${filename##*.}"
क्रिस के

4
यह स्पष्ट रूप से एक गैर-तुच्छ समस्या है और मेरे लिए यह बताना कठिन है कि क्या नीचे दिए गए उत्तर पूरी तरह से सही हैं। यह आश्चर्यजनक है कि यह (बीए) श में ऑपरेशन में निर्मित नहीं है (उत्तर पैटर्न मिलान का उपयोग करके फ़ंक्शन को लागू करने के लिए लगता है)। मैंने पायथन के os.path.splitextरूप में इसके बजाय ऊपर का उपयोग करने का फैसला किया ...
पीटर गिब्सन

1
जैसा कि विस्तार को एक फ़ाइल की प्रकृति का प्रतिनिधित्व करना है, एक जादू कमांड है जो फ़ाइल को उसकी प्रकृति और दिव्य मानक एक्सटेंशन की पेशकश करने के लिए चेक करता है । मेरा जवाब
F. Hauri

2
सवाल पहली जगह में समस्याग्रस्त है क्योंकि .. सामान्य रूप से ओएस और यूनिक्स फ़ाइल-सिस्टम के दृष्टिकोण से, फ़ाइल एक्सटेंशन जैसी कोई चीज नहीं है। इसका उपयोग करना "।" अलग-अलग हिस्सों में एक मानव सम्मेलन है , जो केवल तब तक काम करता है जब तक कि मानव इसका पालन करने के लिए सहमत न हो जाए। उदाहरण के लिए, 'टार' प्रोग्राम के साथ, आउटपुट फाइल को "टार" नाम देने का निर्णय लिया जा सकता था। ".tar" प्रत्यय के बजाय उपसर्ग - "somedir.tar" के बजाय "tar.somedir" देना। इस वजह से कोई "सामान्य, हमेशा काम करता है" समाधान नहीं है - आपको कोड लिखना होगा जो आपकी विशिष्ट आवश्यकताओं और अपेक्षित फ़ाइल नाम से मेल खाता है।
सीएम

जवाबों:


3498

सबसे पहले, पथ के बिना फ़ाइल नाम प्राप्त करें:

filename=$(basename -- "$fullfile")
extension="${filename##*.}"
filename="${filename%.*}"

वैकल्पिक रूप से, आप 'के बजाय पथ के अंतिम' / 'पर ध्यान केंद्रित कर सकते हैं।' यदि आपके पास अप्रत्याशित फ़ाइल एक्सटेंशन हैं तो भी काम करना चाहिए:

filename="${fullfile##*/}"

आप दस्तावेज़ की जाँच करना चाहते हैं:


85
पूर्ण सुविधा सेट के लिए gnu.org/software/bash/manual/html_node/… देखें ।
D शावले

24
"$ फुलफाइल" में कुछ उद्धरण जोड़ें, या आप फ़ाइल नाम को तोड़ने का जोखिम लेंगे।
लुननाथ

47
बिल्ली, आप फ़ाइल नाम = "$ {fullfile ## * /}" भी लिख सकते हैं और एक अतिरिक्त कॉल करने से बच सकते हैंbasename
17

45
यह "समाधान" काम नहीं करता है यदि फ़ाइल में एक्सटेंशन नहीं है - इसके बजाय, संपूर्ण फ़ाइल नाम आउटपुट है, जो कि एक्सटेंशन के बिना फाइलें सर्वव्यापी हैं, यह देखते हुए काफी बुरा है।
nccc

43
एक्सटेंशन के बिना फ़ाइल नामों से निपटने के लिए फिक्स extension=$([[ "$filename" = *.* ]] && echo ".${filename##*.}" || echo ''):। ध्यान दें कि यदि एक विस्तार है वर्तमान में, यह प्रारंभिक सहित लौटा दी जाएगी ., जैसे .txt
mklement0

682
~% FILE="example.tar.gz"

~% echo "${FILE%%.*}"
example

~% echo "${FILE%.*}"
example.tar

~% echo "${FILE#*.}"
tar.gz

~% echo "${FILE##*.}"
gz

अधिक विवरण के लिए, बैश मैनुअल में शेल पैरामीटर विस्तार देखें ।


22
आप (शायद अनजाने में) इस बात का उत्कृष्ट सवाल उठाते हैं कि क्या करना है अगर फ़ाइल नाम के "एक्सटेंशन" भाग में 2 डॉट्स हैं, जैसे कि .tar.gz ... मैंने उस मुद्दे पर कभी विचार नहीं किया है, और मुझे संदेह है। सामने वाले सभी संभावित वैध फ़ाइल एक्सटेंशनों को जाने बिना हल नहीं किया जा सकता है।
rmeador

8
क्यों हल नहीं? मेरे उदाहरण में, यह माना जाना चाहिए कि फ़ाइल में दो एक्सटेंशन हैं, न कि दो डॉट्स वाला एक्सटेंशन। आप दोनों एक्सटेंशन को अलग-अलग संभालते हैं।
जूलियानो

22
यह एक शाब्दिक आधार पर अस्वीकार्य है, आपको फ़ाइल प्रकार की जांच करनी होगी। विचार करें कि क्या आपके पास एक गेम था dinosaurs.in.tarऔर आपने इसे dinosaurs.in.tar.gz:) पर
भेज दिया था

11
यदि आप पूर्ण पथ से गुजर रहे हैं तो यह अधिक जटिल हो जाता है। मेरा एक 'था।' पथ के बीच में एक निर्देशिका, लेकिन फ़ाइल नाम में कोई भी नहीं। उदाहरण "/ a / bc / d / e / फ़ाइल नाम" हवा हो जाएगा ".c / d / e / फ़ाइल नाम"
वॉल्ट सेलर्स

6
स्पष्ट रूप से कोई x.tar.gzविस्तार नहीं है gzऔर फ़ाइल नाम x.tarयही है। दोहरी एक्सटेंशन जैसी कोई चीज नहीं है। मैं बहुत यकीन है कि बढ़ावा दे रहा हूँ :: फाइल सिस्टम इसे इस तरह से संभालता है। (विभाजित पथ, change_extension ...) और इसका व्यवहार अजगर पर आधारित है अगर मैं गलत नहीं हूं।
v.oddou

430

आमतौर पर आप पहले से ही विस्तार जानते हैं, इसलिए आप उपयोग करना चाहते हैं:

basename filename .extension

उदाहरण के लिए:

basename /path/to/dir/filename.txt .txt

और हम प्राप्त करते हैं

filename

60
यह दूसरा तर्क basenameकाफी आंखें खोलने वाला है, जैसे दयालु सर / मैडम :)
akaIDIOT

10
और इस तकनीक का उपयोग करके विस्तार कैसे निकालना है? ;) अरे रुको! हम वास्तव में इसे नहीं जानते हैं।
टॉमस गैंडर

3
आप ज़िपित निर्देशिका है के साथ दोनों सिरों कहो .zipया .ZIP। क्या कोई ऐसा तरीका है जिससे आप कुछ कर सकते हैं basename $file {.zip,.ZIP}?
डेनिस

8
जबकि यह केवल ओपीएस प्रश्न का हिस्सा है, यह उस प्रश्न का उत्तर देता है जो मैंने Google में टाइप किया था। :-) बहुत चालाक!
स्थापित करें

1
आसान और POSIX आज्ञाकारी
gpanda

146

आप POSIX पैरामीटर विस्तार के जादू का उपयोग कर सकते हैं:

bash-3.2$ FILENAME=somefile.tar.gz
bash-3.2$ echo "${FILENAME%%.*}"
somefile
bash-3.2$ echo "${FILENAME%.*}"
somefile.tar

इसमें एक चेतावनी है कि अगर आपका फ़ाइल नाम प्रपत्र का है ./somefile.tar.gzतो echo ${FILENAME%%.*}लालच से सबसे लंबे मैच को हटा देगा .और आपके पास खाली स्ट्रिंग होगा।

(आप एक अस्थायी चर के साथ चारों ओर काम कर सकते हैं:

FULL_FILENAME=$FILENAME
FILENAME=${FULL_FILENAME##*/}
echo ${FILENAME%%.*}

)


यह साइट अधिक बताती है।

${variable%pattern}
  Trim the shortest match from the end
${variable##pattern}
  Trim the longest match from the beginning
${variable%%pattern}
  Trim the longest match from the end
${variable#pattern}
  Trim the shortest match from the beginning

5
जोआचिम के उत्तर की तुलना में बहुत आसान है, लेकिन मुझे हमेशा POSIX चर प्रतिस्थापन देखना होगा। इसके अलावा, यह मैक्स OSX पर चलता है, जहां cutनहीं है --complementऔर sedनहीं है -r
jwadsack

72

यदि फ़ाइल में कोई एक्सटेंशन नहीं है, या कोई फ़ाइल नाम नहीं है, तो यह काम नहीं करता है। यहाँ मैं उपयोग कर रहा हूँ; यह केवल बिल्डिंस का उपयोग करता है और अधिक (लेकिन सभी नहीं) पैथोलॉजिकल फाइलनाम को संभालता है।

#!/bin/bash
for fullpath in "$@"
do
    filename="${fullpath##*/}"                      # Strip longest match of */ from start
    dir="${fullpath:0:${#fullpath} - ${#filename}}" # Substring from 0 thru pos of filename
    base="${filename%.[^.]*}"                       # Strip shortest match of . plus at least one non-dot char from end
    ext="${filename:${#base} + 1}"                  # Substring from len of base thru end
    if [[ -z "$base" && -n "$ext" ]]; then          # If we have an extension and no base, it's really the base
        base=".$ext"
        ext=""
    fi

    echo -e "$fullpath:\n\tdir  = \"$dir\"\n\tbase = \"$base\"\n\text  = \"$ext\""
done

और यहाँ कुछ टेस्टकेस हैं:

$ basename-and-extension.sh / / home / me / home / me / file /home/me/file.tar /home/me/file.tar.gz /home/me/.hidden / home / me / .hidden.tar / home / me / ..।
/:
    dir = "/"
    आधार = ""
    ext = ""
/घर मुझे/:
    dir = "/ home / me /"
    आधार = ""
    ext = ""
/ घर / मुझे / फ़ाइल:
    dir = "/ home / me /"
    आधार = "फ़ाइल"
    ext = ""
/home/me/file.tar:
    dir = "/ home / me /"
    आधार = "फ़ाइल"
    ext = "टार"
/home/me/file.tar.gz:
    dir = "/ home / me /"
    आधार = "file.tar"
    ext = "gz"
/home/me/.hidden:
    dir = "/ home / me /"
    आधार = ".हेड"
    ext = ""
/home/me/.hidden.tar:
    dir = "/ home / me /"
    आधार = ".हेड"
    ext = "टार"
/घर मुझे/..:
    dir = "/ home / me /"
    आधार = ".."
    ext = ""
।:
    दिर = ""
    आधार = "।"
    ext = ""

2
इसके बजाय dir="${fullpath:0:${#fullpath} - ${#filename}}"मैंने अक्सर देखा है dir="${fullpath%$filename}"। लिखना सरल है। निश्चित नहीं है कि कोई वास्तविक गति अंतर या गोच है।
डब्यूजिम

2
यह #! / बिन / बैश का उपयोग करता है जो लगभग हमेशा गलत होता है। यदि संभव हो तो # #! / Bin / sh को रोकें और यदि नहीं तो # / usr / bin / env बैश करें।
अच्छा व्यक्ति

@ अच्छा व्यक्ति: मुझे नहीं पता कि यह लगभग हमेशा गलत कैसे होता है: which bash-> /bin/bash; शायद यह आपका डिस्ट्रो है?
227 बजे vol7ron

2
@ vol7ron - कई डिस्ट्रो बैश पर / usr / लोकल / बिन / बैश में है। OSX पर कई लोग अपडेटेड बैश को / ऑप्ट / लोकल / बिन / बैश में इंस्टॉल करते हैं। जैसे कि / बिन / बैश गलत है और किसी को इसे खोजने के लिए env का उपयोग करना चाहिए। इससे भी बेहतर है कि बिन / श / और POSIX निर्माण का उपयोग करें। सोलारिस को छोड़कर यह एक POSIX शेल है।
गुड पर्सन

2
@ गुडपर्सन लेकिन अगर आप बैश के साथ अधिक सहज हैं, तो श का उपयोग क्यों करें? क्या यह कहना पसंद नहीं है, जब आप श का उपयोग कर सकते हैं तो पर्ल का उपयोग क्यों करें?
Vol7ron

46

आप उपयोग कर सकते हैं basename

उदाहरण:

$ basename foo-bar.tar.gz .tar.gz
foo-bar

आप विस्तार है कि हटाया जा जाएगा साथ basename प्रदान करने के लिए, फिर भी यदि आप हमेशा क्रियान्वित कर रहे हैं की क्या ज़रूरत है tarके साथ -zतो आप को पता विस्तार किया जाएगा .tar.gz

यह वही करना चाहिए जो आप चाहते हैं:

tar -zxvf $1
cd $(basename $1 .tar.gz)

2
मुझे लगता cd $(basename $1 .tar.gz)है .gz फ़ाइलों के लिए काम करता है। लेकिन सवाल में उन्होंने उल्लेख कियाArchive files have several extensions: tar.gz, tat.xz, tar.bz2
एसएस हेगड़े

टामी पो ने 2 साल पहले वही बातें पोस्ट की थीं।
phil294

हाय Blauhirn, wauw यह एक पुराना सवाल है। मुझे लगता है कि तारीखों के लिए कुछ हुआ है। मुझे स्पष्ट रूप से याद है कि सवाल पूछने के कुछ ही समय बाद जवाब दिया गया था, और जहां केवल कुछ अन्य जवाब थे। क्या ऐसा हो सकता है कि प्रश्न को दूसरे के साथ मिला दिया गया था, क्या एसओ ऐसा करता है?
बजरैक फ्रायड-हैन्सन

हां, मुझे ठीक से याद है। मैं मूल रूप से इस सवाल का जवाब देता हूं stackoverflow.com/questions/14703318/… उसी दिन यह पूछा गया था, 2 साल बाद इसे इस में मिला दिया गया था। जब मेरे जवाब को इस तरह से स्थानांतरित किया गया, तो मुझे एक नकली उत्तर के लिए दोषी ठहराया जा सकता है।
बजरैक फ्रायंड-हैन्सन

37
pax> echo a.b.js | sed 's/\.[^.]*$//'
a.b
pax> echo a.b.js | sed 's/^.*\.//'
js

ठीक काम करता है, तो आप बस उपयोग कर सकते हैं:

pax> FILE=a.b.js
pax> NAME=$(echo "$FILE" | sed 's/\.[^.]*$//')
pax> EXTENSION=$(echo "$FILE" | sed 's/^.*\.//')
pax> echo $NAME
a.b
pax> echo $EXTENSION
js

कमांड, वैसे, निम्नानुसार काम करते हैं।

वर्ण के लिए आदेश पंक्ति के अंत तक किसी भी संख्या में गैर-वर्णों के बाद NAMEएक "."चरित्र को प्रतिस्थापित करता है ".", कुछ भी नहीं (यानी, यह सब कुछ अंतिम "."से पंक्ति के अंत तक, समावेशी) को हटा देता है । यह मूल रूप से रेगेक्स ट्रिकरी का उपयोग कर एक गैर-लालची प्रतिस्थापन है।

EXTENSIONकिसी भी वर्ण के लिए कमांड "."लाइन की शुरुआत में एक चरित्र द्वारा पीछा किया जाता है , कुछ भी नहीं (यानी, यह लाइन की शुरुआत से अंतिम डॉट, समावेशी तक) को हटा देता है। यह एक लालची प्रतिस्थापन है जो डिफ़ॉल्ट क्रिया है।


एक्सटेंशन के बिना फ़ाइलों के लिए यह विराम, क्योंकि यह नाम और एक्सटेंशन के लिए समान प्रिंट करेगा। इसलिए मैं sed 's,\.[^\.]*$,,'नाम के लिए उपयोग करता हूं , और sed 's,.*\.,., ;t ;g'विस्तार के लिए (एटिपिकल testऔर getकमांड का उपयोग करता हूं , साथ ही विशिष्ट substituteकमांड के साथ )।
hppyy

32

मेलेन एक ब्लॉग पोस्ट पर एक टिप्पणी में लिखते हैं:

बैश का उपयोग करना, ${file%.*}विस्तार के बिना फ़ाइल नाम प्राप्त करना और ${file##*.}अकेले एक्सटेंशन प्राप्त करना भी है। अर्थात्,

file="thisfile.txt"
echo "filename: ${file%.*}"
echo "extension: ${file##*.}"

आउटपुट:

filename: thisfile
extension: txt


29

इस सरल कार्य के लिए awkया sedउससे परेशान होने की आवश्यकता नहीं है perl। एक शुद्ध-बैश है, os.path.splitext()असंगत समाधान जो केवल पैरामीटर विस्तार का उपयोग करता है।

संदर्भ कार्यान्वयन

का प्रलेखन os.path.splitext(path):

Pathname पथ को एक जोड़ी में विभाजित करें (root, ext)जैसे कि root + ext == path, और ext खाली है या एक अवधि के साथ शुरू होता है और इसमें अधिकांश एक अवधि होती है। बेसन पर प्रमुख अवधियों को अनदेखा किया जाता है; splitext('.cshrc')लौटता है ('.cshrc', '')

पायथन कोड:

root, ext = os.path.splitext(path)

बैश कार्यान्वयन

अग्रणी अवधि का सम्मान

root="${path%.*}"
ext="${path#"$root"}"

अग्रणी अवधियों की उपेक्षा

root="${path#.}";root="${path%"$root"}${root%.*}"
ext="${path#"$root"}"

टेस्ट

यहां इग्नोरिंग लीडिंग पीरियड्स इम्प्लीमेंटेशन के लिए टेस्ट केस हैं , जो कि हर इनपुट पर पायथन रेफरेंस इंप्लीमेंटेशन से मेल खाना चाहिए।

|---------------|-----------|-------|
|path           |root       |ext    |
|---------------|-----------|-------|
|' .txt'        |' '        |'.txt' |
|' .txt.txt'    |' .txt'    |'.txt' |
|' txt'         |' txt'     |''     |
|'*.txt.txt'    |'*.txt'    |'.txt' |
|'.cshrc'       |'.cshrc'   |''     |
|'.txt'         |'.txt'     |''     |
|'?.txt.txt'    |'?.txt'    |'.txt' |
|'\n.txt.txt'   |'\n.txt'   |'.txt' |
|'\t.txt.txt'   |'\t.txt'   |'.txt' |
|'a b.txt.txt'  |'a b.txt'  |'.txt' |
|'a*b.txt.txt'  |'a*b.txt'  |'.txt' |
|'a?b.txt.txt'  |'a?b.txt'  |'.txt' |
|'a\nb.txt.txt' |'a\nb.txt' |'.txt' |
|'a\tb.txt.txt' |'a\tb.txt' |'.txt' |
|'txt'          |'txt'      |''     |
|'txt.pdf'      |'txt'      |'.pdf' |
|'txt.tar.gz'   |'txt.tar'  |'.gz'  |
|'txt.txt'      |'txt'      |'.txt' |
|---------------|-----------|-------|

परीक्षण के परिणाम

सभी टेस्ट पास कर लिए।


2
नहीं, आधार फ़ाइल का नाम text.tar.gzहोना चाहिए textऔर विस्तार होना चाहिए.tar.gz
frederick99

2
@ frederick99 जैसा कि मैंने कहा कि यहाँ समाधान os.path.splitextपायथन के कार्यान्वयन से मेल खाता है । चाहे वह कार्यान्वयन संभवतः विवादास्पद आदानों के लिए समझदार हो, एक अन्य विषय है।
सायकर

पैटर्न ( "$root") के भीतर उद्धरण कैसे काम करते हैं? अगर उन्हें छोड़ दिया गया तो क्या हो सकता है? (मैं इस मामले पर कोई भी दस्तावेज नहीं पा सका।) यह भी कि इस के साथ *या ?उन में फाइलनेम कैसे संभालता है?
यमदूत

ठीक है, मुझे परीक्षण से पता चलता है कि उद्धरण पैटर्न एक शाब्दिक यानी बनाने के लिए, *और ?विशेष नहीं हैं। तो मेरे प्रश्न के दो भाग एक दूसरे का उत्तर देते हैं। क्या मैं सही हूं कि यह दस्तावेज नहीं है? या यह इस तथ्य से समझा जाना चाहिए कि उद्धरण सामान्य रूप से ग्लोब विस्तार को अक्षम करते हैं?
यमट

शानदार जवाब! मैं बस रूट कंप्यूटिंग के लिए थोड़ा सरल संस्करण सुझाऊंगा: root="${path#?}";root="${path::1}${root%.*}"- फिर एक्सटेंशन निकालने के लिए उसी को आगे बढ़ाएं।
मैलान

26

आप cutअंतिम दो एक्सटेंशन ( ".tar.gz"भाग) को हटाने के लिए कमांड का उपयोग कर सकते हैं :

$ echo "foo.tar.gz" | cut -d'.' --complement -f2-
foo

जैसा कि क्लेटन ह्यूजेस ने एक टिप्पणी में कहा है, यह प्रश्न में वास्तविक उदाहरण के लिए काम नहीं करेगा। इसलिए एक विकल्प के रूप में मैं sedइस तरह के विस्तारित नियमित भावों के साथ प्रयोग करने का प्रस्ताव करता हूं :

$ echo "mpc-1.0.1.tar.gz" | sed -r 's/\.[[:alnum:]]+\.[[:alnum:]]+$//'
mpc-1.0.1

यह बिना शर्त पिछले दो (अल्फा-न्यूमेरिक) एक्सटेंशन को हटाकर काम करता है।

[एंडर्स लिंडाहल की टिप्पणी के बाद फिर से अपडेट किया गया]


4
यह केवल उस मामले में काम करता है जहां फ़ाइल नाम / पथ में कोई अन्य डॉट्स नहीं हैं: echo "mpc-1.0.1.tar.zz" | कट -d '।' --comforce -f2- "mpc-1" का उत्पादन करता है (इसके द्वारा परिसीमन के बाद पहले 2 क्षेत्र।)
क्लेटन ह्यूज

@ClaytonHughes आप सही हैं, और मुझे इसका बेहतर परीक्षण करना चाहिए था। एक और उपाय जोड़ा गया।
कुछ प्रोग्रामर ने

सीड एक्सप्रेशन का उपयोग $यह जांचने के लिए करना चाहिए कि मैच किए गए एक्सटेंशन फ़ाइल नाम के अंत में है। अन्यथा, एक फ़ाइल नाम i.like.tar.gz.files.tar.bz2अप्रत्याशित परिणाम उत्पन्न कर सकता है।
एंडर्स लिंडाहल

@AndersLindahl यह अभी भी होगा, अगर एक्सटेंशन का sedक्रम चेन ऑर्डर के विपरीत है । यहां तक ​​कि $अंत में एक फ़ाइल नाम जैसे कि mpc-1.0.1.tar.bz2.tar.gzदोनों को हटा देगा .tar.gzऔर फिर .tar.bz2
कुछ प्रोग्रामर ने

$ गूंज "foo.tar.gz" | कट -d '।' -f2- बिना --complement को स्ट्रिंग $ echo के अंत में दूसरा विभाजन आइटम मिलेगा "foo.tar.zz" | कट -d '।' -f2- tar.gz
जीन ब्लैक

23

यहां कुछ वैकल्पिक सुझाव (ज्यादातर में awk) हैं, जिनमें कुछ उन्नत उपयोग के मामले भी शामिल हैं, जैसे सॉफ्टवेयर पैकेज के लिए संस्करण संख्या निकालना।

f='/path/to/complex/file.1.0.1.tar.gz'

# Filename : 'file.1.0.x.tar.gz'
    echo "$f" | awk -F'/' '{print $NF}'

# Extension (last): 'gz'
    echo "$f" | awk -F'[.]' '{print $NF}'

# Extension (all) : '1.0.1.tar.gz'
    echo "$f" | awk '{sub(/[^.]*[.]/, "", $0)} 1'

# Extension (last-2): 'tar.gz'
    echo "$f" | awk -F'[.]' '{print $(NF-1)"."$NF}'

# Basename : 'file'
    echo "$f" | awk '{gsub(/.*[/]|[.].*/, "", $0)} 1'

# Basename-extended : 'file.1.0.1.tar'
    echo "$f" | awk '{gsub(/.*[/]|[.]{1}[^.]+$/, "", $0)} 1'

# Path : '/path/to/complex/'
    echo "$f" | awk '{match($0, /.*[/]/, a); print a[0]}'
    # or 
    echo "$f" | grep -Eo '.*[/]'

# Folder (containing the file) : 'complex'
    echo "$f" | awk -F'/' '{$1=""; print $(NF-1)}'

# Version : '1.0.1'
    # Defined as 'number.number' or 'number.number.number'
    echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?'

    # Version - major : '1'
    echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?' | cut -d. -f1

    # Version - minor : '0'
    echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?' | cut -d. -f2

    # Version - patch : '1'
    echo "$f" | grep -Eo '[0-9]+[.]+[0-9]+[.]?[0-9]?' | cut -d. -f3

# All Components : "path to complex file 1 0 1 tar gz"
    echo "$f" | awk -F'[/.]' '{$1=""; print $0}'

# Is absolute : True (exit-code : 0)
    # Return true if it is an absolute path (starting with '/' or '~/'
    echo "$f" | grep -q '^[/]\|^~/'

सभी उपयोग के मामले मध्यवर्ती परिणाम के आधार पर, इनपुट के रूप में मूल पूर्ण पथ का उपयोग कर रहे हैं।


20

स्वीकार किए जाते हैं जवाब में अच्छी तरह से काम करता है ठेठ मामलों , लेकिन में विफल रहता है धार मामलों , अर्थात्:

  • बिना विस्तार के फ़ाइल नाम के लिए ( इस उत्तर के शेष भाग में प्रत्यय ), extension=${filename##*.}खाली स्ट्रिंग के बजाय इनपुट फ़ाइल नाम देता है।
  • extension=${filename##*.}.सम्मेलन में प्रारंभिक , विपरीत शामिल नहीं है ।
    • ब्लाइंडली प्रीपेंडिंग .बिना फिक्स के फिल्म्स के लिए काम नहीं करेगी।
  • filename="${filename%.*}"खाली स्ट्रिंग होगी, यदि इनपुट फ़ाइल नाम के साथ शुरू होता है .और इसमें आगे के .अक्षर नहीं होते हैं (जैसे, .bash_profile) - सम्मेलन के विपरीत।

---------

इस प्रकार, एक मजबूत समाधान की जटिलता जो एक फ़ंक्शन के लिए सभी किनारे मामलों को कवर करती है - नीचे इसकी परिभाषा देखें; यह एक पथ के सभी घटकों को वापस कर सकता है

उदाहरण कॉल:

splitPath '/etc/bash.bashrc' dir fname fnameroot suffix
# -> $dir == '/etc'
# -> $fname == 'bash.bashrc'
# -> $fnameroot == 'bash'
# -> $suffix == '.bashrc'

ध्यान दें कि इनपुट पथ के बाद तर्क स्वतंत्र रूप से चुने गए, स्थितीय चर नाम हैं
उन चरों की रुचि को नहीं छोड़ना, जो पहले हैं, निर्दिष्ट करें _(थ्रो-दूर चर का उपयोग करने के लिए $_) या ''; उदाहरण के लिए, केवल फ़ाइल नाम रूट और एक्सटेंशन निकालने के लिए, उपयोग करें splitPath '/etc/bash.bashrc' _ _ fnameroot extension


# SYNOPSIS
#   splitPath path varDirname [varBasename [varBasenameRoot [varSuffix]]] 
# DESCRIPTION
#   Splits the specified input path into its components and returns them by assigning
#   them to variables with the specified *names*.
#   Specify '' or throw-away variable _ to skip earlier variables, if necessary.
#   The filename suffix, if any, always starts with '.' - only the *last*
#   '.'-prefixed token is reported as the suffix.
#   As with `dirname`, varDirname will report '.' (current dir) for input paths
#   that are mere filenames, and '/' for the root dir.
#   As with `dirname` and `basename`, a trailing '/' in the input path is ignored.
#   A '.' as the very first char. of a filename is NOT considered the beginning
#   of a filename suffix.
# EXAMPLE
#   splitPath '/home/jdoe/readme.txt' parentpath fname fnameroot suffix
#   echo "$parentpath" # -> '/home/jdoe'
#   echo "$fname" # -> 'readme.txt'
#   echo "$fnameroot" # -> 'readme'
#   echo "$suffix" # -> '.txt'
#   ---
#   splitPath '/home/jdoe/readme.txt' _ _ fnameroot
#   echo "$fnameroot" # -> 'readme'  
splitPath() {
  local _sp_dirname= _sp_basename= _sp_basename_root= _sp_suffix=
    # simple argument validation
  (( $# >= 2 )) || { echo "$FUNCNAME: ERROR: Specify an input path and at least 1 output variable name." >&2; exit 2; }
    # extract dirname (parent path) and basename (filename)
  _sp_dirname=$(dirname "$1")
  _sp_basename=$(basename "$1")
    # determine suffix, if any
  _sp_suffix=$([[ $_sp_basename = *.* ]] && printf %s ".${_sp_basename##*.}" || printf '')
    # determine basename root (filemane w/o suffix)
  if [[ "$_sp_basename" == "$_sp_suffix" ]]; then # does filename start with '.'?
      _sp_basename_root=$_sp_basename
      _sp_suffix=''
  else # strip suffix from filename
    _sp_basename_root=${_sp_basename%$_sp_suffix}
  fi
  # assign to output vars.
  [[ -n $2 ]] && printf -v "$2" "$_sp_dirname"
  [[ -n $3 ]] && printf -v "$3" "$_sp_basename"
  [[ -n $4 ]] && printf -v "$4" "$_sp_basename_root"
  [[ -n $5 ]] && printf -v "$5" "$_sp_suffix"
  return 0
}

test_paths=(
  '/etc/bash.bashrc'
  '/usr/bin/grep'
  '/Users/jdoe/.bash_profile'
  '/Library/Application Support/'
  'readme.new.txt'
)

for p in "${test_paths[@]}"; do
  echo ----- "$p"
  parentpath= fname= fnameroot= suffix=
  splitPath "$p" parentpath fname fnameroot suffix
  for n in parentpath fname fnameroot suffix; do
    echo "$n=${!n}"
  done
done

फ़ंक्शन का परीक्षण करने वाला परीक्षण कोड:

test_paths=(
  '/etc/bash.bashrc'
  '/usr/bin/grep'
  '/Users/jdoe/.bash_profile'
  '/Library/Application Support/'
  'readme.new.txt'
)

for p in "${test_paths[@]}"; do
  echo ----- "$p"
  parentpath= fname= fnameroot= suffix=
  splitPath "$p" parentpath fname fnameroot suffix
  for n in parentpath fname fnameroot suffix; do
    echo "$n=${!n}"
  done
done

अपेक्षित आउटपुट - किनारे के मामलों पर ध्यान दें:

  • एक फ़ाइल नाम जिसमें कोई प्रत्यय नहीं है
  • एक फ़ाइल नाम जिसकी शुरुआत .( प्रत्यय की शुरुआत नहीं मानी जाती)
  • में समाप्त होने वाला एक इनपुट पथ /(अनुगामी /उपेक्षा है)
  • इनपुट पथ जो केवल फ़ाइल नाम है ( .मूल पथ के रूप में लौटाया गया है)
  • एक फ़ाइल नाम जिसमें अधिक-से-अधिक .टोकन (केवल अंतिम को प्रत्यय माना जाता है):
----- /etc/bash.bashrc
parentpath=/etc
fname=bash.bashrc
fnameroot=bash
suffix=.bashrc
----- /usr/bin/grep
parentpath=/usr/bin
fname=grep
fnameroot=grep
suffix=
----- /Users/jdoe/.bash_profile
parentpath=/Users/jdoe
fname=.bash_profile
fnameroot=.bash_profile
suffix=
----- /Library/Application Support/
parentpath=/Library
fname=Application Support
fnameroot=Application Support
suffix=
----- readme.new.txt
parentpath=.
fname=readme.new.txt
fnameroot=readme.new
suffix=.txt

19

सबसे छोटा और सरल उपाय (सिंगल लाइन में) है:

$ file=/blaabla/bla/blah/foo.txt
echo $(basename ${file%.*}) # foo

यह एक बेकार का उपयोग हैecho । सामान्य तौर पर, echo $(command)केवल commandतब तक बेहतर लिखा जाता है जब तक कि आपको विशेष रूप से commandपरिणाम प्रदर्शित करने से पहले आउटपुट पर व्हॉट्सएप टोकन और वाइल्डकार्ड विस्तार करने के लिए शेल की आवश्यकता न हो । क्विज़: echo $(echo '*')(और यदि आप वास्तव में क्या चाहते हैं, इसका आउटपुट है , तो आप वास्तव में वास्तव में बस चाहते हैंecho * ) ।
ट्रिपल एक्स

@triplee मैंने उपयोग नहीं किया echo कमांड का । मैंने इसका उपयोग केवल fooदूसरी पंक्ति के परिणाम के रूप में तीसरी पंक्ति में प्रदर्शित होने वाले परिणाम को प्रदर्शित करने के लिए किया था।
रॉन

लेकिन बस basename "${file%.*}"वही करेगा; आप इसके आउटपुट को कैप्चर करने के लिए कमांड प्रतिस्थापन का उपयोग कर रहे हैं, केवल echoउसी आउटपुट पर। (उद्धरण के बिना, परिणाम नाममात्र अलग है; लेकिन यह शायद ही प्रासंगिक है, बहुत कम एक विशेषता, यहाँ।)
ट्रिपल

इसके अलावा basename "$file" .txtपैरामीटर प्रतिस्थापन की जटिलता से बचा जाता है।
ट्रिपल

1
@ हमारे समय को बर्बाद करने का आरोप लगाने से पहले उनकी पहली टिप्पणी पढ़ें।
frederick99

14

मुझे लगता है कि अगर आपको फ़ाइल के नाम की आवश्यकता है, तो आप यह कोशिश कर सकते हैं:

FULLPATH=/usr/share/X11/xorg.conf.d/50-synaptics.conf

# Remove all the prefix until the "/" character
FILENAME=${FULLPATH##*/}

# Remove all the prefix until the "." character
FILEEXTENSION=${FILENAME##*.}

# Remove a suffix, in our case, the filename. This will return the name of the directory that contains this file.
BASEDIRECTORY=${FULLPATH%$FILENAME}

echo "path = $FULLPATH"
echo "file name = $FILENAME"
echo "file extension = $FILEEXTENSION"
echo "base directory = $BASEDIRECTORY"

और वह सब = द।


बस आधार चाहिए था :) धन्यवाद!
कार्लोस रिकार्डो

12

आप सभी फ़ील्ड और बाद वाले -को फ़ील्ड नंबर में जोड़ने के लिए कट करने के लिए बाध्य कर सकते हैं ।

NAME=`basename "$FILE"`
EXTENSION=`echo "$NAME" | cut -d'.' -f2-`

तो अगर FILE है eth0.pcap.gz, तो EXTENSION होगाpcap.gz

इसी तर्क का उपयोग करते हुए, आप फ़ाइल नाम का उपयोग करके '-' को इस प्रकार काट सकते हैं:

NAME=`basename "$FILE" | cut -d'.' -f-1`

यह उन फ़ाइलनामों के लिए भी काम करता है जिनमें कोई एक्सटेंशन नहीं है।


8

जादू फ़ाइल मान्यता

इस ढेर अतिप्रवाह प्रश्न पर बहुत सारे अच्छे उत्तरों के अलावा मैं जोड़ना चाहूंगा:

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

file myfile.txt
myfile.txt: UTF-8 Unicode text

file -b --mime-type myfile.txt
text/plain

मानक विस्तार में पाया जा सकता है /etc/mime.types(मेरे डेबियन जीएनयू / लिनक्स डेस्कटॉप पर। देखें man fileऔर man mime.typesशायद आपको fileउपयोगिता और mime-supportपैकेज स्थापित करना होगा ):

grep $( file -b --mime-type myfile.txt ) </etc/mime.types
text/plain      asc txt text pot brf srt

आप एक बना सकते हैं सही विस्तार का निर्धारण करने के लिए कार्य करता है। एक छोटा (पूर्ण नहीं) नमूना है:

file2ext() {
    local _mimetype=$(file -Lb --mime-type "$1") _line _basemimetype
    case ${_mimetype##*[/.-]} in
        gzip | bzip2 | xz | z )
            _mimetype=${_mimetype##*[/.-]}
            _mimetype=${_mimetype//ip}
            _basemimetype=$(file -zLb --mime-type "$1")
            ;;
        stream )
            _mimetype=($(file -Lb "$1"))
            [ "${_mimetype[1]}" = "compressed" ] &&
                _basemimetype=$(file -b --mime-type - < <(
                        ${_mimetype,,} -d <"$1")) ||
                _basemimetype=${_mimetype,,}
            _mimetype=${_mimetype,,}
            ;;
        executable )  _mimetype='' _basemimetype='' ;;
        dosexec )     _mimetype='' _basemimetype='exe' ;;
        shellscript ) _mimetype='' _basemimetype='sh' ;;
        * )
            _basemimetype=$_mimetype
            _mimetype=''
            ;;
    esac
    while read -a _line ;do
        if [ "$_line" == "$_basemimetype" ] ;then
            [ "$_line[1]" ] &&
                _basemimetype=${_line[1]} ||
                _basemimetype=${_basemimetype##*[/.-]}
            break
        fi
        done </etc/mime.types
    case ${_basemimetype##*[/.-]} in
        executable ) _basemimetype='' ;;
        shellscript ) _basemimetype='sh' ;;
        dosexec ) _basemimetype='exe' ;;
        * ) ;;
    esac
    [ "$_mimetype" ] && [ "$_basemimetype" != "$_mimetype" ] &&
      printf ${2+-v} $2 "%s.%s" ${_basemimetype##*[/.-]} ${_mimetype##*[/.-]} ||
      printf ${2+-v} $2 "%s" ${_basemimetype##*[/.-]}
}

यह फ़ंक्शन एक बैश वैरिएबल सेट कर सकता है जिसे बाद में उपयोग किया जा सकता है:

(यह @Petesh सही उत्तर से प्रेरित है):

filename=$(basename "$fullfile")
filename="${filename%.*}"
file2ext "$fullfile" extension

echo "$fullfile -> $filename . $extension"

8

ठीक है, अगर मैं सही ढंग से समझता हूं, तो यहां समस्या यह है कि किसी फ़ाइल का नाम और पूर्ण एक्सटेंशन कैसे प्राप्त करें, जिसमें कई एक्सटेंशन हैं, जैसे stuff.tar.gz

यह मेरे लिए काम करता है:

fullfile="stuff.tar.gz"
fileExt=${fullfile#*.}
fileName=${fullfile%*.$fileExt}

यह आपको stuffफ़ाइल नाम और .tar.gzएक्सटेंशन के रूप में देगा। यह किसी भी संख्या में एक्सटेंशन के लिए काम करता है, जिसमें 0. आशा है कि यह किसी को भी समान समस्या होने पर मदद करता है =)


सही परिणाम (के अनुसार os.path.splitext, जो ओपी चाहता है) ('stuff.tar', '.gz')
साइकर

6

मैं निम्नलिखित स्क्रिप्ट का उपयोग करता हूं

$ echo "foo.tar.gz"|rev|cut -d"." -f3-|rev
foo

यह बिल्कुल भी कुशल नहीं है। कई बार कांटे लगाने के लिए जो कि काफी अनावश्यक है क्योंकि इस ऑपरेशन को शुद्ध बैश में बिना किसी बाहरी आज्ञा और फोर्किंग के किया जा सकता है।
codeforester

5
$ F = "text file.test.txt"  
$ echo ${F/*./}  
txt  

यह एक फ़ाइल नाम में कई डॉट्स और रिक्त स्थान के लिए पूरा करता है, हालांकि अगर कोई विस्तार नहीं है तो यह फ़ाइल नाम को ही लौटाता है। हालांकि के लिए जाँच करने के लिए आसान; सिर्फ फ़ाइल नाम और एक्सटेंशन के लिए एक ही परीक्षण किया जा रहा है।

स्वाभाविक रूप से यह विधि .tar.gz फ़ाइलों के लिए काम नहीं करती है। हालाँकि इसे दो चरणों की प्रक्रिया में संभाला जा सकता था। यदि एक्सटेंशन gz है तो फिर से देखें कि क्या कोई टार एक्सटेंशन भी है या नहीं।


5

मछली में फ़ाइल नाम और विस्तार कैसे निकालें :

function split-filename-extension --description "Prints the filename and extension"
  for file in $argv
    if test -f $file
      set --local extension (echo $file | awk -F. '{print $NF}')
      set --local filename (basename $file .$extension)
      echo "$filename $extension"
    else
      echo "$file is not a valid file"
    end
  end
end

कैविट्स: अंतिम बिंदु पर विभाजन, जो उन में डॉट्स के साथ फ़ाइलनाम के लिए अच्छी तरह से काम करता है, लेकिन उन में डॉट्स के साथ एक्सटेंशन के लिए अच्छी तरह से नहीं। नीचे उदाहरण देखें।

उपयोग:

$ split-filename-extension foo-0.4.2.zip bar.tar.gz
foo-0.4.2 zip  # Looks good!
bar.tar gz  # Careful, you probably want .tar.gz as the extension.

ऐसा करने के लिए शायद बेहतर तरीके हैं। इसे सुधारने के लिए मेरे उत्तर को संपादित करने के लिए स्वतंत्र महसूस करें।


अगर आपके पास सीमित एक्सटेंशन हैं जिनसे आप निपटेंगे और आप उन सभी को जानते हैं, तो यह प्रयास करें:

switch $file
  case *.tar
    echo (basename $file .tar) tar
  case *.tar.bz2
    echo (basename $file .tar.bz2) tar.bz2
  case *.tar.gz
    echo (basename $file .tar.gz) tar.gz
  # and so on
end

इसके पास पहले उदाहरण के रूप में कैवेट नहीं है, लेकिन आपको हर मामले को संभालना होगा ताकि आप कितने एक्सटेंशनों की अपेक्षा कर सकते हैं, यह अधिक थकाऊ हो सकता है।


4

यहाँ AWK के साथ कोड है । इसे और अधिक सरलता से किया जा सकता है। लेकिन मैं AWK में अच्छा नहीं हूं।

filename$ ls
abc.a.txt  a.b.c.txt  pp-kk.txt
filename$ find . -type f | awk -F/ '{print $2}' | rev | awk -F"." '{$1="";print}' | rev | awk 'gsub(" ",".") ,sub(".$", "")'
abc.a
a.b.c
pp-kk
filename$ find . -type f | awk -F/ '{print $2}' | awk -F"." '{print $NF}'
txt
txt
txt

आपको अंतिम उदाहरण में पहले awk स्टेटमेंट की आवश्यकता नहीं है, है ना?
BHSPitMonkey

आप एक और काम करके Awk को पाइप करने से बच सकते हैं split()awk -F / '{ n=split($2, a, "."); print a[n] }' uses / `शीर्ष-स्तरीय सीमांकक के रूप में, लेकिन फिर दूसरे फ़ील्ड को विभाजित करता है .और नए सरणी से अंतिम तत्व प्रिंट करता है।
ट्रिपल

4

बस उपयोग करें ${parameter%word}

आपके मामले में:

${FILE%.*}

यदि आप इसका परीक्षण करना चाहते हैं, तो निम्नलिखित सभी कार्य, और एक्सटेंशन को हटा दें:

FILE=abc.xyz; echo ${FILE%.*};
FILE=123.abc.xyz; echo ${FILE%.*};
FILE=abc; echo ${FILE%.*};

2
क्यों होता है पतन? यह अभी भी उपयोगी है, हालांकि =संकेतों के आसपास रिक्त स्थान नहीं होना चाहिए ।
सिल्वरवॉल्फ - मोनिका

1
यह ठीक काम करता है। धन्यवाद! (अब यह समान संकेतों के आसपास रिक्त स्थान नहीं है, अगर यही कारण था कि इसे डाउनवोट किया गया था)
एलेक्स। S.

3

पेटेश जवाब से निर्माण , अगर केवल फ़ाइल नाम की जरूरत है, तो पथ और विस्तार दोनों को एक ही पंक्ति में छीन लिया जा सकता है,

filename=$(basename ${fullname%.*})

मेरे लिए काम नहीं किया: "अधिक जानकारी के लिए बेसनैम: लापता ऑपरेंड 'बेसनेम - चेले' की कोशिश करें।"
हेलमी

अजीब बात है, क्या आप निश्चित हैं कि आप बैश का उपयोग कर रहे हैं? मेरे मामले में, दोनों संस्करणों के साथ 3.2.25 (पुराने CentOS) और 4.3.30 (डेबियन जेसी) यह निर्दोष रूप से काम करता है।
cvr

हो सकता है कि फ़ाइल नाम में कोई स्थान हो? उपयोग करने का प्रयासfilename="$(basename "${fullname%.*}")"
एड्रियन

के लिए दूसरा तर्क basenameवैकल्पिक है, लेकिन एक्सटेंशन को स्ट्रिप ऑफ करने के लिए निर्दिष्ट करता है। प्रतिस्थापन अभी भी उपयोगी हो सकता है, लेकिन शायद basenameवास्तव में ऐसा नहीं है, क्योंकि आप वास्तव में शेल प्रतिस्थापन के साथ इन सभी प्रतिस्थापनों का प्रदर्शन कर सकते हैं।
ट्रिपलए

3

मोटे तौर पर @ mklement0 के उत्कृष्ट, और बेतरतीब, उपयोगी बशीज़ से भरे - साथ ही साथ इस / अन्य सवालों के जवाब / "कि darn internet" के आधार पर ... मैंने इसे सभी को थोड़ा, थोड़ा और अधिक समझ में लपेटा। मेरे (या आपके) के लिए पुन: प्रयोज्य कार्य.bash_profile जो इस बात का ध्यान रखता है कि (मुझे क्या लगता है) dirname/ basename/ के पास अधिक मजबूत संस्करण होना चाहिए जो मेरे पास है ..

function path { SAVEIFS=$IFS; IFS=""   # stash IFS for safe-keeping, etc.
    [[ $# != 2 ]] && echo "usage: path <path> <dir|name|fullname|ext>" && return    # demand 2 arguments
    [[ $1 =~ ^(.*/)?(.+)?$ ]] && {     # regex parse the path
        dir=${BASH_REMATCH[1]}
        file=${BASH_REMATCH[2]}
        ext=$([[ $file = *.* ]] && printf %s ${file##*.} || printf '')
        # edge cases for extensionless files and files like ".nesh_profile.coffee"
        [[ $file == $ext ]] && fnr=$file && ext='' || fnr=${file:0:$((${#file}-${#ext}))}
        case "$2" in
             dir) echo      "${dir%/*}"; ;;
            name) echo      "${fnr%.*}"; ;;
        fullname) echo "${fnr%.*}.$ext"; ;;
             ext) echo           "$ext"; ;;
        esac
    }
    IFS=$SAVEIFS
}     

उपयोग के उदाहरण ...

SOMEPATH=/path/to.some/.random\ file.gzip
path $SOMEPATH dir        # /path/to.some
path $SOMEPATH name       # .random file
path $SOMEPATH ext        # gzip
path $SOMEPATH fullname   # .random file.gzip                     
path gobbledygook         # usage: -bash <path> <dir|name|fullname|ext>

1
अच्छी तरह से किया; कुछ सुझाव: - आप $IFSसभी पर भरोसा नहीं करते हैं (और यदि आप थे, तो आप localसेटिंग के प्रभाव को स्थानीय बनाने के लिए उपयोग कर सकते हैं )। - localचर का उपयोग करने के लिए बेहतर है । - आपका त्रुटि संदेश आउटपुट होना चाहिए stderr, न कि stdout(उपयोग 1>&2), और आपको एक गैर-शून्य निकास कोड वापस करना चाहिए। - बेहतर करने के लिए नाम बदलने के fullnameलिए basename(पूर्व dir घटकों के साथ एक पथ का सुझाव देता है)। - nameबिना शर्त एक .(अवधि) को जोड़ देता है , भले ही मूल कोई नहीं था। आप बस basenameउपयोगिता का उपयोग कर सकते हैं , लेकिन ध्यान दें कि यह एक समाप्ति को अनदेखा करता है /
mklement0

2

एक सरल जवाब:

POSIX चर जवाब पर विस्तार करने के लिए , ध्यान दें कि आप अधिक दिलचस्प पैटर्न कर सकते हैं। इस मामले के लिए यहाँ विस्तृत, आप बस यह कर सकते हैं:

tar -zxvf $1
cd ${1%.tar.*}

यह .tar की अंतिम घटना को काट देगा। <कुछ>

आम तौर पर, यदि आप अंतिम घटना को हटाना चाहते थे। <कुछ><कुछ और> तब

${1.*.*}

ठीक काम करना चाहिए।

उपरोक्त उत्तर का लिंक मृत प्रतीत होता है। यहाँ स्ट्रिंग हेरफेर का एक गुच्छा है जो आप सीधे टीएलडीपी से बैश में कर सकते हैं


क्या मैच को असंवेदनशील बनाने का कोई तरीका है?
टोनिक्स

2

अगर आप भी खाली एक्सटेंशन की अनुमति देना चाहते हैं , तो यह सबसे छोटा है, जिसके साथ मैं आ सकता हूं:

echo 'hello.txt' | sed -r 's/.+\.(.+)|.*/\1/' # EXTENSION
echo 'hello.txt' | sed -r 's/(.+)\..+|(.*)/\1\2/' # FILENAME

पहली पंक्ति में समझाया गया: यह PATH.EXT या किसी भी चीज़ से मेल खाता है और इसे EXT से बदल देता है। यदि कुछ भी मिलान किया गया था, तो अतिरिक्त समूह कैप्चर नहीं किया गया है।


2

यह केवल एक ही है जो मेरे लिए काम करता है:

path='folder/other_folder/file.js'

base=${path##*/}
echo ${base%.*}

>> file

यह स्ट्रिंग प्रक्षेप में भी इस्तेमाल किया जा सकता है, लेकिन दुर्भाग्य से आपको पहले से सेट baseकरना होगा।


1

यहाँ वह एल्गोरिथ्म है जिसका उपयोग मैंने एक फ़ाइल के नाम और विस्तार को खोजने के लिए किया था जब मैंने नामों को आवरण के संबंध में विरोधाभासी बनाने के लिए एक बैश स्क्रिप्ट लिखी थी।

#! /bin/bash 

#
# Finds 
# -- name and extension pairs
# -- null extension when there isn't an extension.
# -- Finds name of a hidden file without an extension
# 

declare -a fileNames=(
  '.Montreal' 
  '.Rome.txt' 
  'Loundon.txt' 
  'Paris' 
  'San Diego.txt'
  'San Francisco' 
  )

echo "Script ${0} finding name and extension pairs."
echo 

for theFileName in "${fileNames[@]}"
do
     echo "theFileName=${theFileName}"  

     # Get the proposed name by chopping off the extension
     name="${theFileName%.*}"

     # get extension.  Set to null when there isn't an extension
     # Thanks to mklement0 in a comment above.
     extension=$([[ "$theFileName" == *.* ]] && echo ".${theFileName##*.}" || echo '')

     # a hidden file without extenson?
     if [ "${theFileName}" = "${extension}" ] ; then
         # hidden file without extension.  Fixup.
         name=${theFileName}
         extension=""
     fi

     echo "  name=${name}"
     echo "  extension=${extension}"
done 

परीक्षण चला।

$ config/Name\&Extension.bash 
Script config/Name&Extension.bash finding name and extension pairs.

theFileName=.Montreal
  name=.Montreal
  extension=
theFileName=.Rome.txt
  name=.Rome
  extension=.txt
theFileName=Loundon.txt
  name=Loundon
  extension=.txt
theFileName=Paris
  name=Paris
  extension=
theFileName=San Diego.txt
  name=San Diego
  extension=.txt
theFileName=San Francisco
  name=San Francisco
  extension=
$ 

FYI करें: संपूर्ण लिप्यंतरण कार्यक्रम और अधिक परीक्षण मामले यहां देखे जा सकते हैं: https://www.dropbox.com/s/4c6m0f2e28a1vxf/avoid-clashes-code.zip?dl=0


सभी समाधानों से यह केवल एक ही है जो फ़ाइल के साथ कोई विस्तार नहीं होने पर एक खाली स्ट्रिंग लौटाता है:extension=$([[ "$theFileName" == *.* ]] && echo ".${theFileName##*.}" || echo '')
f0nzie

1

उदाहरण फ़ाइल का उपयोग करके /Users/Jonathan/Scripts/bash/MyScript.sh, यह कोड:

MY_EXT=".${0##*.}"
ME=$(/usr/bin/basename "${0}" "${MY_EXT}")

${ME}होने MyScriptऔर ${MY_EXT}होने में परिणाम होगा .sh:


स्क्रिप्ट:

#!/bin/bash
set -e

MY_EXT=".${0##*.}"
ME=$(/usr/bin/basename "${0}" "${MY_EXT}")

echo "${ME} - ${MY_EXT}"

कुछ परीक्षण:

$ ./MyScript.sh 
MyScript - .sh

$ bash MyScript.sh
MyScript - .sh

$ /Users/Jonathan/Scripts/bash/MyScript.sh
MyScript - .sh

$ bash /Users/Jonathan/Scripts/bash/MyScript.sh
MyScript - .sh

2
यह सुनिश्चित नहीं है कि यह इतने सारे डाउनवोट क्यों है - यह वास्तव में स्वीकृत उत्तर की तुलना में अधिक कुशल है। (उत्तरार्द्ध के रूप में, यह बिना विस्तार के इनपुट फ़ाइलनामों के साथ भी टूट जाता है )। एक स्पष्ट पथ का उपयोग करना basename, शायद, ओवरकिल है।
mklement0

1

उपरोक्त उत्तरों से, पायथन की नकल करने के लिए सबसे छोटा ऑनलाइनर

file, ext = os.path.splitext(path)

मान लें कि आपकी फ़ाइल में वास्तव में एक्सटेंशन है, है

EXT="${PATH##*.}"; FILE=$(basename "$PATH" .$EXT)

मैं इस पर उतर गया हूँ। मैं जवाब को हटाने पर विचार कर रहा हूं, लोग किसी तरह इसे नापसंद करते हैं।
आमपाइक

बेसन एक्सटेंशन को नहीं हटाता है, सिर्फ पथ।
डेविड कुलेन

जब से मैंने SUFFIX विकल्प के बारे में भूल गए आदमी पृष्ठ को देखा, तब से यह बहुत लंबा है।
डेविड कुलेन

आपको यह जानना होगा कि आप किस विस्तार को छीनना चाहते हैं, इससे पहले कि आप जानते हैं कि इसमें क्या डाला EXTजाए, यह सभी तरह से नीचे है। (इसके अलावा, आपको अपने निजी चर नामों के लिए सभी अपरकेस से बचना चाहिए; वे सिस्टम वेरिएबल्स के लिए आरक्षित हैं।)
tripleee
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.