Як я можу видалити розширення імені файлу в скрипті оболонки?


144

Що не так із наведеним нижче кодом?

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

Так, я отримую буквальний рядок $filename | cut -f1 -d'.', але якщо я видаляю лапки, я нічого не отримую. Тим часом, набираючи текст

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

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


6
basename $filename .exeзробив би те саме. Це передбачає, що ви завжди знаєте, яке розширення потрібно видалити.
mpe

6
@mpe, ти маєш на увазі basename "$filename" .exe. Інакше назви файлів з пробілами були б поганими новинами.
Чарльз Даффі

Відповіді:


113

Ви повинні використовувати синтаксис підстановки команд,$(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
Зворотні посилання застаріли за допомогою POSIX, $()є кращим.
Йорданм

3
Розгортання та трубопровід для отримання кількох символів - це найгірше рішення, яке можна уявити.
Єнс

39
Проблема з цією відповіддю полягає в тому, що вона передбачає, що в рядок введення є ТОЛЬКА одна крапка ... @chepner нижче має набагато краще рішення ... name = $ {filename%. *}
Scott Stensland

4
Ця відповідь характерна для новачків і не повинна поширюватися. Використовуйте механізм вбудованої , як описано chepner у відповідь
neric

258

Ви також можете використовувати розширення параметрів:

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

6
тут пояснення команди: gnu.org/software/bash/manual/html_node/…
Carlos Robles

1
І ось я збирався використати echo -n "This.File.Has.Periods.In.It.txt" | awk -F. '{$NF=""; print $0}' | tr ' ' '.' | rev | cut -c 2- | rev. Дякую.
користувач208145

1
Чи працює це з файлами з кількома розширеннями image.png.gz?
Hawker65

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

1
Це, здається, працює краще, ніж прийнята відповідь. Він відшаровує останнє розширення лише у тому випадку, якщо файл має більше однієї крапки, тоді як вирізання видаляє все після першої точки.
Дейл Андерсон


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", вибираючи символи перед першим екземпляром крапки у імені файлу, не враховуючи шлях. Напевно, не хочеться таким чином знімати розширення.
Фроц

0

Дві проблеми з вашим кодом:

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

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

$> 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.