Извлечь имя файла и расширение в Bash - PullRequest
1849 голосов
/ 08 июня 2009

Я хочу получить имя файла (без расширения) и расширение отдельно.

Лучшее решение, которое я нашел на данный момент:

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

Это неправильно, потому что не работает, если имя файла содержит несколько . символов. Если, скажем, у меня есть a.b.js, он будет рассматривать a и b.js вместо a.b и js.

Это легко сделать на Python с помощью

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

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

Есть идеи получше?

Ответы [ 37 ]

4 голосов
/ 21 ноября 2011

Вот код с 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
3 голосов
/ 12 июля 2010

Просто используйте ${parameter%word}

В вашем случае:

${FILE%.*}

Если вы хотите протестировать его, все последующие работы и просто удалите расширение:

FILE=abc.xyz; echo ${FILE%.*};
FILE=123.abc.xyz; echo ${FILE%.*};
FILE=abc; echo ${FILE%.*};
3 голосов
/ 04 декабря 2015

Построение из Petesh ответа, если нужно только имя файла, путь и расширение могут быть разделены в одну строку,

filename=$(basename ${fullname%.*})
2 голосов
/ 27 августа 2013

Основано в основном на превосходном @ mklement0 и переполнено случайными, полезными bashisms - а также другими ответами на этот / другие вопросы / "этот проклятый интернет" ... Я завернул все это в небольшой, немного более понятной, многократно используемой функции для моей (или вашей) .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>
2 голосов
/ 10 июня 2014

Простой ответ:

Чтобы раскрыть переменные POSIX ответ , обратите внимание, что вы можете создавать более интересные шаблоны. Так что для случая, описанного здесь, вы можете просто сделать это:

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

Это прервет последнее вхождение .tar. .

В более общем смысле, если вы хотите удалить последнее вхождение. . <что-то еще> затем

${1.*.*}

должно работать нормально.

Ссылка на приведенный выше ответ кажется мертвой. Вот отличное объяснение множества манипуляций со строками, которые вы можете выполнять непосредственно в Bash, из TLDP .

2 голосов
/ 16 сентября 2017

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

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

1-я строка объяснила: он соответствует PATH.EXT или НИЧЕГО и заменяет его на EXT. Если НИЧЕГО было найдено, группа ext не перехватывается.

1 голос
/ 19 мая 2012

Используя файл примера /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
1 голос
/ 03 января 2014

Из ответов выше, самый короткий oneliner для имитации Python

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

при условии, что ваш файл действительно имеет расширение,

EXT="${PATH##*.}"; FILE=$(basename "$PATH" .$EXT)
1 голос
/ 11 декабря 2018

ИМХО, лучшее решение уже было дано (с использованием расширения параметров оболочки) и является лучшим на данный момент.

Я, однако, добавляю эту, которая просто использует команды dumbs, которая неэффективна и которую никто никогда не должен использовать серьезно:

FILENAME=$(echo $FILE | cut -d . -f 1-$(printf $FILE | tr . '\n' | wc -l))
EXTENSION=$(echo $FILE | tr . '\n' | tail -1)

Добавлено просто для удовольствия : -)

0 голосов
/ 30 июля 2014

Простой bash один вкладыш. Я использовал это, чтобы удалить расширение rst из всех файлов в pwd

for each in `ls -1 *.rst`
do
     a=$(echo $each | wc -c)
     echo $each | cut -c -$(( $a-5 )) >> blognames
done

Что это делает?

1) ls -1 *.rst перечислит все файлы на stdout в новой строке (попробуйте).

2) echo $each | wc -c считает количество символов в каждом имени файла.

3) echo $each | cut -c -$(( $a-5 )) выбирает до 4 последних символов, т. Е. .rst.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...