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


170

Я пишу сценарій нічної збірки в баш.
Все прекрасно і денді, за винятком одного маленького корча:


#!/bin/bash

for file in "$PATH_TO_SOMEWHERE"; do
      if [ -d $file ]
      then
              # do something directory-ish
      else
              if [ "$file" == "*.txt" ]       #  this is the snag
              then
                     # do something txt-ish
              fi
      fi
done;

Моя проблема - визначити розширення файлу, а потім діяти відповідно. Я знаю, що проблема полягає в операторі if, тестуючи файл txt.

Як я можу визначити, чи має у файлі суфікс .txt?


Це порушиться, якщо у вас є файл із пробілом у його імені.
jfg956

Окрім відповіді Павла, ви можете використовувати $(dirname $PATH_TO_SOMEWHERE)та $(basename $PATH_TO_SOMEWHERE)розділити їх на папки та каталоги та робити щось каталогу-ish та file-ish
McPeppr

Відповіді:


249

Я думаю, ви хочете сказати "Чи рівні останні чотири символи файлу $ .txt?" Якщо так, ви можете скористатися наступним:

if [ ${file: -4} == ".txt" ]

Зауважте, що простір між file:і -4потрібний, оскільки модифікатор ': -' означає щось інше.


1
для цього ви можете перейменувати command.com в command.txt і на машині Windows.
рідний схід

9
Якщо ви хочете вказати нерівність, не забудьте включити додаткові дужки: якщо [[$ {файл: -4}! = ".Txt"]]
Ram Rajamony

4
@RamRajamony Чому потрібно використовувати [[при тестуванні нерівності?
PesaThe

Я хотів зазначити, що простір після товстої кишки важливий. ${var:-4}не те саме, що ${var: -4}; перший (без пробілу) буде розширюватися як "-4", якщо змінено var, другий (з пробілом) повертає останні 4 символи var.
pbatey

1
У bash, це призведе до помилки "[: ==: очікується одинарний оператор", якщо ви не поставите лапки навколо першої змінної. Тож if [ "${file: -4}" == ".txt" ]натомість.
Giles B

264

Зробіть

if [ "$file" == "*.txt" ]

подобається це:

if [[ $file == *.txt ]]

Тобто подвійні дужки і жодних лапок.

Права сторона ==- візерунок оболонки. Якщо вам потрібен регулярний вираз, використовуйте =~тоді.


15
Я про це не знав. Здається, особливий випадок, коли права частина == або! = Розгорнута як візерунок оболонки. Особисто я вважаю, що це ясніше, ніж моя відповідь.
Пол Стефенсон

20
Я новачок у bash, і мені знадобилося трохи часу, щоб розібратися, як це використовувати в багатоумовній ifзаяві. Я ділюсь цим тут на випадок, якщо хтось допомагає. if [[ ( $file == *.csv ) || ( $file == *.png ) ]]
joelostblom

6
@cheflo - це добре для загальних умов. У цьому конкретному випадку ви також можете використовувати if [[ $file =~ .*\.(csv|png) ]]. Це коротше, зрозуміліше, простіше додати додаткові розширення, і їх можна легко налаштувати (додавши "csv | png" у змінну).
jox

4
Ви можете поставити подвійні лапки навколо файлу. if [[ "$file" == *.txt ]]Якщо у файлі є пробіли у своєму імені, потрібно подвійне цитування.
shawnhcorey

Чи слід використовувати цю відповідь замість прийнятої?
Фредо

26

Ви просто не можете бути впевнені в системі Unix, що .txt файл справді є текстовим файлом. Ваша найкраща ставка - використовувати "файл". Можливо, спробуйте використовувати:

file -ib "$file"

Тоді ви можете використовувати список типів MIME, щоб співставити або проаналізувати першу частину MIME, де ви отримуєте такі матеріали, як "текст", "додаток" тощо.


2
Що file -i...стосується кодування міми, ви можете використовуватиfile --mime-type -b ...
Вільф

24

Ви можете використовувати команду "файл", якщо ви дійсно хочете дізнатися інформацію про файл, а не покладатися на розширення.

Якщо вам комфортно користуватися розширенням, ви можете використовувати grep, щоб побачити, чи відповідає він.


так, я знаю fileкоманду. Я насправді намагався співставити на основі випуску зазначеної команди ... але я жахливо провалююсь у цих if-операторах.

17

Ви також можете зробити:

   if [ "${FILE##*.}" = "txt" ]; then
       # operation for txt files here
   fi

11
case $FILE in *.txt ) ... ;; esacздавалося б більш міцним та ідіоматичним.
трійчанка

11

Як і "файл", використовуйте трохи простіший "mimetype -b", який працюватиме незалежно від розширення файлу.

if [ $(mimetype -b "$MyFile") == "text/plain" ]
then
  echo "this is a text file"
fi

Редагувати: вам може знадобитися встановити libfile-mimeinfo-perl у вашій системі, якщо mimetype недоступний


1
Вам слід уточнити, що сценарій 'mimetype' доступний не у всіх системах.
gilbertpilz

Готово, додано libfile-mimeinfo-perl
dargaud

5

Правильна відповідь про те, як взяти розширення, доступне для імені файлу в Linux,:

${filename##*\.} 

Приклад друку всіх розширень файлів у каталозі

for fname in $(find . -maxdepth 1 -type f) # only regular file in the current dir
    do  echo ${fname##*\.} #print extensions 
done

У вашій відповіді використовується подвійна косою рисою, але у вашому прикладі використовується лише одна похила. Ваш приклад правильний, ваша відповідь - ні.
gilbertpilz

3

Я написав скрипт bash, який переглядає тип файлу, потім копіює його в розташування, я використовую його для перегляду відео, яке я переглянув в Інтернеті з мого кешу firefox:

#!/bin/bash
# flvcache script

CACHE=~/.mozilla/firefox/xxxxxxxx.default/Cache
OUTPUTDIR=~/Videos/flvs
MINFILESIZE=2M

for f in `find $CACHE -size +$MINFILESIZE`
do
    a=$(file $f | cut -f2 -d ' ')
    o=$(basename $f)
    if [ "$a" = "Macromedia" ]
        then
            cp "$f" "$OUTPUTDIR/$o"
    fi
done

nautilus  "$OUTPUTDIR"&

Він використовує ідеї, подібні до представлених тут, сподіваюся, що це комусь корисно.


2

Я думаю, '$PATH_TO_SOMEWHERE'це щось на кшталт '<directory>/*'.

У цьому випадку я зміню код на:

find <directory> -maxdepth 1 -type d -exec ... \;
find <directory> -maxdepth 1 -type f -name "*.txt" -exec ... \;

Якщо ви хочете зробити щось складніше з іменами каталогу та текстових файлів, ви можете:

find <directory> -maxdepth 1 -type d | while read dir; do echo $dir; ...; done
find <directory> -maxdepth 1 -type f -name "*.txt" | while read txtfile; do echo $txtfile; ...; done

Якщо у назвах файлів є пробіли, ви можете:

find <directory> -maxdepth 1 -type d | xargs ...
find <directory> -maxdepth 1 -type f -name "*.txt" | xargs ...

Це чудові приклади того, як ви робите «петлі» в оболонці. Явні forта whileпетлі краще зарезервувати, коли тіло циклу має бути складнішим.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.