Захват расширения в имени файла

Как получить расширение файла из bash? Вот что я пробовал:

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

Делая это, я могу получить расширение bz2 из пути /dir/subdir/file.bz2 , но у меня есть проблема с path /dir/subdir/file-1.0.tar.bz2 .

Я бы предпочел решение, используя только bash без внешних программ, если это возможно.

Чтобы сделать мой вопрос ясным, я создавал скрипт bash для извлечения любого заданного архива только одной командой extract path_to_file . Как извлечь файл определяется сценарием, вид его типа сжатия или архивации, который может быть .tar.gz, .gz, .bz2 и т. Д. Я думаю, что это должно включать манипуляции с строкой, например, если я получаю расширение .gz то я должен проверить, имеет ли он строку .tar перед .gz – если это так, расширение должно быть .tar.gz .

Если имя файла – file-1.0.tar.bz2 , расширение – bz2 . Метод, который вы используете для извлечения расширения ( fileext=${filename##*.} ), Отлично действует¹.

Как вы решаете, хотите ли вы, чтобы расширение было tar.bz2 а не 0.tar.bz2 или 0.tar.bz2 ? Сначала вам нужно ответить на этот вопрос. Затем вы можете выяснить, какая команда оболочки соответствует вашей спецификации.

  • Одна из возможных спецификаций заключается в том, что расширения должны начинаться с буквы. Эта эвристика терпит неудачу для нескольких общих расширений, таких как 7z , которые лучше всего рассматривать как частный случай. Вот реализация bash / 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 extglob под bash и 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 .

¹ ${VARIABLE##SUFFIX} и связанные с ним конструкции находятся в POSIX , поэтому они работают в любой антикварной оболочке Bourne, такой как ash, bash, ksh или zsh.

Вы можете упростить ситуацию, просто выполнив сопоставление шаблонов в имени файла, а не извлекая расширение дважды:

 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 
 $ echo "thisfile.txt"|awk -F . '{print $NF}' 

Комментарии по этому вопросу: http://liquidat.wordpress.com/2007/09/29/short-tip-get-file-extension-in-shell-script/

 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 

Однажды я создал эти сложные функции:

 # 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 

PS Если вам понравились эти функции или они были использованы, обратитесь к этому сообщению 🙂 (и, надеюсь, добавьте комментарий).

Вот мой снимок: переведите точки на новые строки, проведите через tail , получите последнюю строку:

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

ответ на вопрос о Jackman довольно хорош и переносим, ​​но если вы просто хотите, чтобы имя файла и расширение в переменной я нашел это решение:

 INPUTFILE="$1" INPUTFILEEXT=$( echo -n "$INPUTFILE" | rev | cut -d'.' -f1 | rev ) INPUTFILEEXT=$( echo -n $INPUTFILEEXT | tr '[AZ]' '[az]' ) # 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 

Он работает только с двойными расширениями, а первый должен быть «tar».

Но вы можете изменить тестовую строку «tar» с помощью теста длины строки и повторить исправление несколько раз.

я решил это, используя это:

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

но это работает только для известного типа архивирования, в этом случае только tar