Как я могу удалить расширение имени файла в сценарии оболочки?


147

Что не так со следующим кодом?

name='$filename | cut -f1 -d'.''

Как есть, я получаю буквенную строку $filename | cut -f1 -d'.', но если я удаляю кавычки, я ничего не получаю. Между тем, набрав

"test.exe" | cut -f1 -d'.'

в оболочке дает мне вывод, который я хочу test. Я уже знаю $filename, было назначено правильное значение. Я хочу назначить переменной имя файла без расширения.


6
basename $filename .exeсделал бы то же самое. Это при условии, что вы всегда знаете, какое расширение вы хотите удалить.
mpe

7
@ mpe, ты имеешь в виду basename "$filename" .exe. В противном случае имена файлов с пробелами будут плохими новостями.
Чарльз Даффи

Ответы:


114

Вы должны использовать синтаксис подстановки команд,$(command) когда хотите выполнить команду в сценарии / команде.

Так что ваша линия будет

name=$(echo "$filename" | cut -f 1 -d '.')

Объяснение кода:

  1. echoполучить значение переменной $filenameи отправить его на стандартный вывод
  2. Затем мы получаем вывод и передаем его cutкоманде
  3. cutБудет использовать. в качестве разделителя (также известного как разделитель) для разрезания строки на сегменты и -fвыбора того, какой сегмент мы хотим иметь в выводе
  4. Тогда $()подстановка команды получит вывод и вернет его значение
  5. Возвращенное значение будет присвоено переменной с именем name

Обратите внимание, что это дает часть переменной до первого периода .:

$ filename=hello.world
$ echo "$filename" | cut -f 1 -d '.'
hello
$ filename=hello.hello.hello
$ echo "$filename" | cut -f 1 -d '.'
hello
$ filename=hello
$ echo "$filename" | cut -f 1 -d '.'
hello

Спасибо. Я также заметил, что мне нужно использовать команду echo. name =echo $filename | cut -f1 -d'.'
mimicocotopus

17
Backticks не рекомендуется POSIX, $()является предпочтительным.
Иордания

3
Форкинг и прокачка нескольких персонажей - самое худшее из возможных решений.
Йенс

39
Проблема с этим ответом состоит в том, что предполагается, что входная строка имеет ТОЛЬКО одну точку ... @chepner ниже имеет гораздо лучшее решение ... name = $ {filename%. *}
Скотт Стенсланд

4
Этот ответ характерен для начинающих и не должен распространяться. Используйте механизм встроенной , как описано chepner в ответ
neric

261

Вы также можете использовать расширение параметров:

$ filename=foo.txt
$ echo "${filename%.*}"
foo

6
вот объяснение команды: gnu.org/software/bash/manual/html_node/…
Карлос Роблес

1
И здесь я собирался использовать echo -n "This.File.Has.Periods.In.It.txt" | awk -F. '{$NF=""; print $0}' | tr ' ' '.' | rev | cut -c 2- | rev. Спасибо.
user208145

1
Это работает с файлами с несколькими расширениями, как image.png.gz?
Hawker65

11
%.*удалит только последнее расширение; если вы хотите удалить все расширения, используйте %%.*.
chepner

1
Кажется, это работает лучше, чем принятый ответ. Он удаляет последнее расширение только в том случае, если файл имеет более одной точки, в то время как cut удаляет все после первой точки.
Дейл Андерсон


20

Если ваше имя файла содержит точку (отличную от расширения), используйте это:

echo $filename | rev | cut -f 2- -d '.' | rev

1
Я забыл средний обороты, но как только увидел, это было здорово!
высший пооба

Еще лучше, если -sзадана опция cut, так что она возвращает пустую строку всякий раз, когда имя файла не содержит точки.
Hibou57

1
На мой взгляд, это должен быть принятый ответ, поскольку он работает с путями с точками в них, со скрытыми файлами, начинающимися с точки, или даже с файлами с несколькими расширениями.
Тим Криф

20
file1=/tmp/main.one.two.sh
t=$(basename "$file1")                        # output is main.one.two.sh
name=$(echo "$file1" | sed -e 's/\.[^.]*$//') # output is /tmp/main.one.two
name=$(echo "$t" | sed -e 's/\.[^.]*$//')     # output is main.one.two

используйте то, что вы хотите. Здесь я предполагаю, что последняя .(точка), за которой следует текст, является расширением.


6
#!/bin/bash
file=/tmp/foo.bar.gz
echo $file ${file%.*}

выходы:

/tmp/foo.bar.gz /tmp/foo.bar

Обратите внимание, что удаляется только последнее расширение.


3

Моя рекомендация заключается в использовании basename.
Это по умолчанию в Ubuntu, визуально простой код и имеет дело с большинством случаев.

Вот некоторые подслучаи для работы с пробелами и многоточечным / под-расширением:

pathfile="../space fld/space -file.tar.gz"
echo ${pathfile//+(*\/|.*)}

Обычно он сначала избавляется от расширения ., но на нашем ..пути терпит неудачу

echo **"$(basename "${pathfile%.*}")"**  
space -file.tar     # I believe we needed exatly that

Вот важное примечание:

Я использовал двойные кавычки внутри двойных кавычек, чтобы иметь дело с пробелами. Одиночная кавычка не пройдет из-за отправки $. Bash необычен и читает «вторые» первые «цитаты» из-за расширения.

Тем не менее, вам все еще нужно думать о .hidden_files

hidden="~/.bashrc"
echo "$(basename "${hidden%.*}")"  # will produce "~" !!!  

не ожидаемый "" результат. Чтобы это произошло, используйте $HOMEили /home/user_path/
потому что bash снова "необычен" и не расширяет "~" (поиск bash BashPitfalls)

hidden2="$HOME/.bashrc" ;  echo '$(basename "${pathfile%.*}")'

1
#!/bin/bash
filename=program.c
name=$(basename "$filename" .c)
echo "$name"

выходы:

program

Чем это отличается от ответа, данного Стивеном Пенни 3 года назад?
gniourf_gniourf

1

Как указал Hawker65 в комментарии к ответу chepner, решение с наибольшим количеством голосов не заботится ни о нескольких расширениях (например, filename.tar.gz), ни о точках в остальной части пути (например, this.path / with). .dots / in.path.name). Возможное решение:

a=this.path/with.dots/in.path.name/filename.tar.gz
echo $(dirname $a)/$(basename $a | cut -d. -f1)

Он удаляет tar.gz, выбирая символы перед первым экземпляром точки в имени файла, не считая пути. Вероятно, никто не хочет лишать расширения таким способом.
Frotz

0

Две проблемы с вашим кодом:

  1. Вы использовали '(тик) вместо `(тик назад), чтобы окружить команды, которые генерируют строку, которую вы хотите сохранить в переменной.
  2. Вы не "выводили" переменную "$ filename" в канал в команду "cut".

Я бы изменил ваш код на "name =` echo $ filename | cut -f 1 -d '.' `", как показано ниже (опять же, обратите внимание на обратные галочки, окружающие определение переменной name):

$> filename=foo.txt
$> echo $filename
foo.txt
$> name=`echo $filename | cut -f1 -d'.'`
$> echo $name
foo
$> 
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.