फ़ाइल नाम में एक्सटेंशन को हथियाना


33

मुझे बैश से फ़ाइल एक्सटेंशन कैसे मिलेगा? यहाँ मैंने क्या कोशिश की है:

filename=`basename $filepath`
fileext=${filename##*.}

ऐसा करने से मुझे bz2पथ से विस्तार मिल सकता है /dir/subdir/file.bz2, लेकिन मुझे पथ से समस्या है /dir/subdir/file-1.0.tar.bz2

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

अपने प्रश्न को स्पष्ट करने के लिए, मैं किसी भी दिए गए संग्रह को निकालने के लिए केवल एक कमांड द्वारा बैश स्क्रिप्ट बना रहा था extract path_to_file। कैसे फ़ाइल निकालने के लिए अपने संपीड़न देखकर या प्रकार संग्रहण करके स्क्रिप्ट से निर्धारित होता है, कि .tar.gz हो सकता है, .gz, .bz2 आदि मुझे लगता है कि इस स्ट्रिंग परिवर्तन को शामिल करना चाहिए, उदाहरण के लिए अगर मैं विस्तार मिलता है .gzमैं तो जाँच करनी चाहिए कि क्या .tarइससे पहले तार है .gz- यदि हां, तो विस्तार होना चाहिए .tar.gz


2
फ़ाइल = "/ dir / subdir / फ़ाइल 1.0.tar.bz2"; इको $ {फ़ाइल ## *।} प्रिंट '.bz2' यहाँ। वह आउटपुट क्या है जिसकी आप अपेक्षा कर रहे हैं?
axel_c

1
मुझे ज़रूरत है.tar.bz2
12

जवाबों:


19

यदि फ़ाइल का नाम है file-1.0.tar.bz2, तो एक्सटेंशन है bz2। एक्सटेंशन निकालने के लिए आप जिस विधि का उपयोग कर रहे हैं ( fileext=${filename##*.}) पूरी तरह से मान्य है।

आप कैसे तय करते हैं कि आप चाहते हैं कि विस्तार हो या tar.bz2नहीं ? आपको पहले इस प्रश्न का उत्तर देना होगा। फिर आप यह पता लगा सकते हैं कि शेल विनिर्देश आपके विनिर्देश से क्या मेल खाता है।bz20.tar.bz2

  • एक संभावित विनिर्देश यह है कि एक्सटेंशन को एक पत्र के साथ शुरू होना चाहिए। यह अनुमानवादी कुछ सामान्य एक्सटेंशन की तरह विफल हो जाता है 7z, जिसे एक विशेष मामले के रूप में सबसे अच्छा माना जा सकता है। यहाँ एक बैश / ksh / zsh कार्यान्वयन है:

    basename=$filename; fileext=
    while [[ $basename = ?*.* &&
             ( ${basename##*.} = [A-Za-z]* || ${basename##*.} = 7z ) ]]
    do
      fileext=${basename##*.}.$fileext
      basename=${basename%.*}
    done
    fileext=${fileext%.}

    POSIX पोर्टेबिलिटी के लिए, आपको caseपैटर्न मिलान के लिए एक स्टेटमेंट का उपयोग करना होगा ।

    while case $basename in
            ?*.*) case ${basename##*.} in [A-Za-z]*|7z) true;; *) false;; esac;;
            *) false;;
          esac
    do 
  • एक अन्य संभावित विनिर्देश यह है कि कुछ एक्सटेंशन एन्कोडिंग को दर्शाते हैं और संकेत देते हैं कि आगे स्ट्रिपिंग की आवश्यकता है। यहाँ एक bash / ksh / zsh कार्यान्वयन है ( shopt -s extglobbash के setopt ksh_globतहत और zsh के तहत आवश्यक):

    basename=$filename
    fileext=
    while [[ $basename = ?*.@(bz2|gz|lzma) ]]; do
      fileext=${basename##*.}.$fileext
      basename=${basename%.*}
    done
    if [[ $basename = ?*.* ]]; then
      fileext=${basename##*.}.$fileext
      basename=${basename%.*}
    fi
    fileext=${fileext%.}

    ध्यान दें कि यह 0एक विस्तार मानता है file-1.0.gz

Are और संबंधित निर्माण POSIX में हैं , इसलिए वे किसी भी गैर-प्राचीन बॉर्न-शैली के शेल जैसे कि राख, बैश, केश या ज़श में काम करते हैं। ${VARIABLE##SUFFIX}


इसे हल किया जाना चाहिए, अगर पिछले .टोकन से पहले स्ट्रिंग संग्रह प्रकार है, उदाहरण के लिए tar, अगर इसका संग्रह प्रकार नहीं जैसे 0पुनरावृत्ति समाप्त होना चाहिए।
यूरे

2
@ और: यह इस विशेष मामले में काम करता है, लेकिन यह एक सामान्य समाधान नहीं है। मैकीज के उदाहरण.patch.lzma पर विचार करें । एक बेहतर अनुमानी स्ट्रिंग विचार करने के लिए किया जाएगा के बाद पिछले .: अगर यह एक संपीड़न प्रत्यय है ( .7z, .bz2, .gz, ...), अलग करना जारी है।
गिल्स एसओ- बुराई को रोकना '

@NoamM इंडेंटेशन में क्या गलत था? आपके संपादन के बाद यह निश्चित रूप से टूट गया है: दोगुना-नेस्टेड कोड एकल-नेस्टेड के समान है।
गिल्स एसओ- बुराई को रोकना '28

22

आप विस्तार को केवल दो बार निकालने के बजाय फ़ाइल नाम पर मिलान करके मामलों को सरल बना सकते हैं:

case "$filename" in
    *.tar.bz2) bunzip_then_untar ;;
    *.bz2)     bunzip_only ;;
    *.tar.gz)  untar_with -z ;;
    *.tgz)     untar_with -z ;;
    *.gz)      gunzip_only ;;
    *.zip)     unzip ;;
    *.7z)      do something ;;
    *)         do nothing ;;
esac

यह समाधान खूबसूरती से सरल है।
AsymLabs

6
$ echo "thisfile.txt"|awk -F . '{print $NF}'

इस पर टिप्पणियाँ यहाँ: http://liquidat.wordpress.com/2007/09/29/short-tip-get-file-extension-in-shell-script/


1
.tar.gzविस्तार के लिए काम नहीं
12

4
खैर .tar .gz वास्तव में एक gzip फ़ाइल के अंदर एक टार है इसलिए यह इस अर्थ में काम करता है कि यह gzip फ़ाइल से gz एक्सटेंशन निकालता है।
क्रिस

2

यहाँ पर मेरा शॉट है: डॉट्स टू न्यूलाइन्स, पाइप टू थ्रू tail, लास्ट लाइन प्राप्त करें:

$> TEXT=123.234.345.456.456.567.678
$> echo $TEXT | tr . \\n | tail -n1
678

0
echo ${filename#$(echo $filename | sed 's/\.[^[:digit:]].*$//g;')}

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

% echo $filename
2.6.35-zen2.patch.lzma
% echo ${filename#$(echo $filename | sed 's/\.[^[:digit:]].*$//g;')}
.patch.lzma

सभी मामलों के लिए काम नहीं करता है। 'Foo.7z' के साथ कोशिश करें
axel_c

आपको उद्धरण की आवश्यकता है, और बेहतर उपयोग printfके मामले में फ़ाइल नाम में बैकस्लैश है या इसके साथ शुरू होता है -:"${filename#$(printf %s "$filename" | sed 's/\.[^[:digit:]].*$//g;')}"
गाइल्स का SO- बुराई होना बंद हो '

@axel_c: सही है, और मैंने एक उदाहरण के रूप में Maciej के समान विनिर्देश लागू किया है। आप क्या अनुमान लगाते हैं कि "पत्र से शुरू होता है" से बेहतर है?
गाइल्स का SO- दुष्ट होना बंद '

1
@ गिल्स: मुझे लगता है कि कोई समाधान नहीं है जब तक कि आप ज्ञात एक्सटेंशन की पूर्व-सूचीबद्ध सूची का उपयोग नहीं करते हैं, क्योंकि एक्सटेंशन कुछ भी हो सकता है।
axel_c

0

एक दिन मैंने उन कठिन कार्यों को बनाया है:

# args: string how_many
function get_last_letters(){ echo ${1:${#1}-$2:$2}; }
function cut_last_letters(){ echo ${1:0:${#1}-$2}; }

मुझे यह सीधा दृष्टिकोण मिला है, कई मामलों में बहुत उपयोगी है, न केवल जब यह एक्सटेंशन के बारे में जाता है।

एक्सटेंशन की जाँच के लिए - यह सरल और विश्वसनीय है

~$ get_last_letters file.bz2 4
.bz2
~$ get_last_letters file.0.tar.bz2 4
.bz2

कट-ऑफ एक्सटेंशन के लिए:

~$ cut_last_letters file.0.tar.bz2 4
file.0.tar

एक्सटेंशन बदलने के लिए:

~$ echo $(cut_last_letters file.0.tar.bz2 4).gz
file.0.tar.gz

या, अगर आपको "आसान कार्य पसंद हैं:

~$ function cut_last_letters_and_add(){ echo ${1:0:${#1}-$2}"$3"; }
~$ cut_last_letters_and_add file.0.tar.bz2 4 .gz
file.0.tar.gz

पुनश्च यदि आपको वे कार्य पसंद आए या उन्हें उपयोगी पाया गया, तो कृपया इस पोस्ट को देखें :) (और उम्मीद है कि एक टिप्पणी डालें)।


0

जैकमैन केस-आधारित उत्तर बहुत अच्छा और पोर्टेबल है, लेकिन अगर आप केवल एक चर में फ़ाइल नाम और विस्तार चाहते हैं, तो मुझे यह समाधान है:

INPUTFILE="$1"
INPUTFILEEXT=$( echo -n "$INPUTFILE" | rev | cut -d'.' -f1 | rev )
INPUTFILEEXT=$( echo -n $INPUTFILEEXT | tr '[A-Z]' '[a-z]' ) # force lowercase extension
INPUTFILENAME="`echo -n \"$INPUTFILE\" | rev | cut -d'.' -f2- | rev`"

# fix for files with multiple extensions like "gbamidi-v1.0.tar.gz"
INPUTFILEEXT2=$( echo -n "$INPUTFILENAME" | rev | cut -d'.' -f1 | rev )
if [ "$INPUTFILEEXT2" = "tar" ]; then
    # concatenate the extension
    INPUTFILEEXT="$INPUTFILEEXT2.$INPUTFILEEXT"
    # update the filename
    INPUTFILENAME="`echo -n \"$INPUTFILENAME\" | rev | cut -d'.' -f2- | rev`"
fi

यह केवल डबल एक्सटेंशन के साथ काम करता है और पहले वाला "टार" होना चाहिए।

लेकिन आप एक स्ट्रिंग लंबाई परीक्षण के साथ "टार" टेस्ट लाइन को बदल सकते हैं और कई बार फिक्स को दोहरा सकते हैं।


-1

मैंने इसका उपयोग करके इसे हल किया:

filename=`basename $filepath`
fileext=${filename##*.}
fileext2=${filename%.*}
fileext3=${fileext2##*.}
if [ "$fileext3" == "tar" ]; then
    fileext="tar."$fileext
fi

लेकिन यह केवल ज्ञात संग्रह प्रकार के लिए काम करता है, केवल इस मामले में tar

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