Bash проверка расширения файла

Обновлено: 06.07.2024

Я пишу ночной сценарий сборки в Bash.
Все хорошо и модно, за исключением одной маленькой загвоздки:

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

Как определить, имеет ли файл суффикс .txt?

Это сломается, если у вас есть файл с пробелом в имени. В дополнение к ответу Пола, вы можете использовать $(dirname $PATH_TO_SOMEWHERE) и $(basename $PATH_TO_SOMEWHERE) для разделения на папки и директории и делать что-то с каталогами и файлами

Я думаю, что вы хотите сказать: «Последние четыре символа $ file равны .txt ?» Если это так, вы можете использовать следующее:

Обратите внимание, что между file: и -4 требуется пробел , так как модификатор ': -' означает что-то другое.

Если вы хотите указать неравенство, не забудьте включить дополнительные скобки: if [[$ ! = ".Txt"]] @RamRajamony Почему необходимо использовать [[при проверке неравенства? Я хотел указать, что пространство после двоеточия важно. $ это не то же самое, что $ ; первый (без пробела) будет расширяться как '-4', если переменная не установлена, вторая (с пробелом) возвращает последние 4 символа переменной var. В bash это приведет к ошибке «[: ==: ожидается унарный оператор», если вы не поместите кавычки вокруг первой переменной. Так что if [ "$" == ".txt" ] вместо.

То есть в двойных скобках и без кавычек.

Правая сторона == - это шаблон оболочки. Если вам нужно регулярное выражение, используйте =

Я не знал об этом. Кажется, это особый случай, когда правая часть == или! = Раскрывается как шаблон оболочки. Лично я думаю, что это яснее, чем мой ответ. Я новичок в bash, и мне потребовалось немного времени, чтобы понять, как использовать это в условном if выражении. Я делюсь этим здесь на случай, если это кому-то поможет. if [[ ( $file == *.csv ) || ( $file == *.jpg ) ]] @cheflo это хорошо для нескольких условий в целом. В этом конкретном случае вы также можете использовать if [[ $file =

.*\.(csv|png) ]] . Он короче, понятнее, проще добавлять дополнительные расширения и его можно легко настроить (поместив «csv | png» в переменную).

Вы можете поместить двойные кавычки вокруг файла. if [[ "$file" == *.txt ]] Если в имени файла есть пробелы, двойные кавычки обязательны. Должен ли я использовать этот ответ вместо принятого?

Вы просто не можете быть уверены в системе Unix, что файл .txt действительно является текстовым файлом. Лучше всего использовать файл. Может быть, попробуйте использовать:

Затем вы можете использовать список типов MIME для сопоставления или анализа первой части MIME, где вы получаете такие вещи, как «текст», «приложение» и т. Д.

В том file -i. числе кодирование MIME, вы можете использовать file --mime-type -b .

Вы можете использовать команду «file», если вы действительно хотите узнать информацию о файле, а не полагаться на расширения.

Если вы чувствуете себя комфортно при использовании расширения, вы можете использовать grep, чтобы увидеть, соответствует ли оно.

да, я в курсе file команды. Я на самом деле пытался сопоставить, основываясь на выводе указанной команды . но я ужасно провалился в этих операторах if.

Вы также можете сделать:

case $FILE in *.txt ) . ;; esac казалось бы более надежным и идиоматическим.

Подобно «file», используйте чуть более простой «mimetype -b», который будет работать независимо от расширения файла.

Редактировать: вам может понадобиться установить libfile-mimeinfo-perl в вашей системе, если mimetype недоступен

Вы должны четко дать понять, что скрипт mimetype доступен не во всех системах.

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

Пример печати всех расширений файлов в каталоге

В вашем ответе используется двойной обратный слеш, но в вашем примере используется только один обратный слеш. Ваш пример правильный, ваш ответ - нет.

Я написал bash-скрипт, который просматривает тип файла, затем копирует его в папку, и я использую его для просмотра видео, которые я смотрел онлайн, из моего кеша Firefox:

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

Я написал скрипт bash в текстовом редакторе. Какое расширение я могу сохранить мой скрипт, чтобы он мог работать как скрипт bash? Я создал скрипт, который теоретически должен запускать ssh-сервер. Мне интересно, как заставить скрипт выполняться, когда я нажимаю на него. Я использую OS X 10.9.5.

Не соглашаясь с другими ответами, существует общее соглашение использовать расширение .sh для сценариев оболочки - но это не полезное соглашение. Лучше вообще не использовать расширение. Преимущество возможности сказать, что foo.sh является сценарием оболочки, поскольку его имя минимально, и вы платите за него с потерей гибкости.

Чтобы сделать исполняемый скрипт bash, он должен иметь строку shebang вверху:

и используйте команду chmod +x чтобы система распознала его как исполняемый файл. Затем его необходимо установить в один из каталогов, перечисленных в вашем $PATH . Если скрипт называется foo , вы можете запустить его из командной строки, набрав foo . Или, если он находится в текущем каталоге (обычно для временных скриптов), вы можете набрать ./foo .

Ни оболочка, ни операционная система не обращают внимания на часть расширения имени файла. Это просто часть названия. И не давая ему специального расширения, вы гарантируете, что любому (пользователю или другому сценарию), который его использует, не нужно заботиться о том, как он был реализован, будь то сценарий оболочки (sh, bash, csh или что-то еще) сценарий Perl, Python или Awk или двоичный исполняемый файл. Система специально разработана таким образом, что интерпретируемый сценарий или исполняемый двоичный файл могут быть вызваны, не зная и не заботясь о том, как она реализована.

UNIX-подобные системы начинались с чисто текстового интерфейса командной строки. GUI, такие как KDE и Gnome, были добавлены позже. В настольной системе с графическим интерфейсом обычно можно запустить программу (опять же, будь то скрипт или исполняемый двоичный файл), например, дважды щелкнув по значку, который к ней относится. Как правило, это отбрасывает любой вывод, который может напечатать программа, и не позволяет передавать аргументы командной строки; это гораздо менее гибко, чем запускать его из командной строки. Но для некоторых программ (в основном для клиентов с графическим интерфейсом) это может быть удобнее.

Сценарии оболочки лучше всего изучать из командной строки, а не из графического интерфейса.

(Некоторые инструменты обращают внимание на расширения файлов. Например, компиляторы обычно используют расширение для определения языка, на котором написан код: .c для C, .cpp для c++ и т.д. Это соглашение не применяется к исполняемым файлам. файлы.)

Вам не нужно никакого расширения (или вы можете выбрать произвольное расширение, но .sh - полезное соглашение).

в терминале. См. Chmod (1) для команды и chmod (2) для системного вызова.

Если вы не вводите полный путь к файлу, вы должны поместить этот файл в какой-то каталог, указанный в вашем PATH (см. Environment (7) и execvp (3)), который вы можете навсегда установить в вашем

/.bashrc если ваша оболочка входа bash )

Выполнение вашего сценария двойным щелчком мыши (что? Вы не сказали!) Является проблемой среды рабочего стола и может зависеть от рабочего стола (может отличаться в Kde, Mate, Gnome. или IceWM или RatPoison). Возможно, чтение спецификации EWMH поможет вам получить более качественную картину.

Возможно, выполнение исполняемого скрипта с помощью chmod может сделать его кликабельным на вашем рабочем столе (очевидно, Quartz в MacOSX).Но тогда вы, вероятно, должны сделать это дать некоторую визуальную обратную связь.

И на некоторых компьютерах нет рабочего стола, в том числе вашего собственного, когда вы получаете к нему удаленный доступ с помощью ssh.

Я не верю, что это хорошая идея - запустить скрипт оболочки, нажав. Вы, вероятно, хотите иметь возможность аргументировать свой сценарий оболочки (и как бы вы это сделали, нажав?), И вам следует позаботиться о его выводе. Если вы можете написать сценарий оболочки, вы можете использовать интерактивную оболочку в терминале. Это лучший и самый естественный способ использовать скрипт. Хорошие интерактивные оболочки (например, zsh или fish или, возможно, недавний bash ) имеют восхитительные и настраиваемые средства автозаполнения, и вам не придется много печатать (научитесь использовать клавишу tab на клавиатуре). Кроме того, сценарии и программы часто являются частями составных команд (конвейеры и т.д.).

PS. Я использую Unix с 1986 года, а Linux с 1993 года. Я никогда не запускал свои собственные программы или скрипты, нажимая. Почему я должен?

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

Лучшее решение, которое я нашел, это:

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

Это может быть легко сделано в Python с

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

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

Сначала получите имя файла без пути:

Кроме того, вы можете сосредоточиться на последнем "/" пути вместо "." который должен работать, даже если у вас есть непредсказуемые расширения файлов:

Вы можете проверить документацию:

  • В Интернете в разделе " 3.5.3 Расширение параметров оболочки "
  • На странице руководства bash в разделе "Расширение параметров"

Подробнее см. расширение параметров оболочки в руководстве Bash.

Обычно вы уже знаете расширение, поэтому вы можете использовать:

Вы можете использовать магию переменных POSIX:

Там предостережение в том, что если ваше имя файла имеет форму ./somefile.tar.gz , то echo $ будет с жадностью удалять самое длинное совпадение с . , и у вас будет пустая строка.

(Вы можете обойти это с помощью временной переменной:

Этот сайт объясняет больше.

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

И вот несколько тестовых примеров:

Вы можете использовать basename .

Вам нужно предоставить базовое имя с расширением, которое должно быть удалено, однако если вы всегда выполняете tar с помощью -z , то вы знаете, что расширение будет .tar.gz .

Это должно делать то, что вы хотите:

работает отлично, поэтому вы можете просто использовать:

Команды, кстати, работают следующим образом.

Команда NAME заменяет символ "." , за которым следует любое количество символов не "." до конца строки, ничего (т.е. удаляет все из окончательного "." в конец строки, включительно). Это в основном не-жадная подстановка с использованием регулярных выражений.

Команда для EXTENSION заменяет любое количество символов, за которыми следует символ "." в начале строки, без ничего (т.е. удаляет все с начала строки до конечной точки включительно), Это жадная подстановка, которая является действием по умолчанию.

Используя Bash, вы также можете $ получить имя файла без расширения и $ , чтобы получить расширение самостоятельно. То есть

Вы можете использовать команду cut для удаления последних двух расширений (часть ".tar.gz" ):

Как отметил Клейтон Хьюз в комментарии, это не будет работать для фактического примера в вопросе. Поэтому в качестве альтернативы я предлагаю использовать sed с расширенными регулярными выражениями, например:

Он работает, удаляя последние два (альфа-числовые) расширения без каких-либо условий.

[Обновлено после комментария Андерса Линдаля]

Не нужно беспокоиться о awk или sed или даже perl для этой простой задачи. Существует чистое Bash, os.path.splitext() -компонентное решение, которое использует только разложения параметров.

Разделите путь пути в пару (root, ext) таким образом, чтобы root + ext == path , а ext пуст или начинается с периода и содержит не более одного периода. Ведущие периоды в basename игнорируются; splitext('.cshrc') возвращает ('.cshrc', '') .

Почитание ведущих периодов

Игнорирование ведущих периодов

Ниже приведены тестовые примеры для Игнорирования реализации ведущих периодов, которые должны соответствовать реализации ссылок Python на каждом входе.

Результаты испытаний

Все тесты прошли.

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

Все варианты использования используют исходный полный путь для ввода, не зависящий от промежуточных результатов.

Наименьшее и простое решение (в одну строку) это:

Принятый ответ хорошо работает в типичных случаях, но терпит неудачу в крайних случаях, а именно:

  • Для имен файлов без расширения (называемых суффиксом в оставшейся части этого ответа) extension=$ Возвращает входное имя файла, а не пустую строку.
  • extension=$ не включает начальное . вопреки соглашению.
    • Слепой предвкушение . не будет работать для имен файлов без суффикса.

    Таким образом, сложность надежного решения, охватывающего все граничные случаи, требует функции - см. Ее определение ниже; он может вернуть все компоненты пути.

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

    Лучшее решение, которое я нашел до сих пор:

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

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

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

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

    Сначала получите имя файла без пути:

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

    Вы можете проверить документацию:

    Для получения дополнительных сведений см. расширение параметров оболочки в руководство по Bash.

    Обычно вы уже знаете расширение, поэтому можете использовать:

    Вы можете использовать магию расширения параметров POSIX:

    Здесь есть предостережение: если ваше имя файла имеет форму ./somefile.tar.gz , то echo $ жадно удалит самое длинное совпадение с . , и вы получите пустую строку.

    (Вы можете обойти это с помощью временной переменной:

    Этот сайт объясняет больше.

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

    А вот несколько тестов:

    Отлично работает, поэтому вы можете просто использовать:

    Команды, кстати, работают следующим образом.

    Команда для NAME заменяет символ "." , за которым следует любое количество символов, отличных от - "." , до конца строки и ничего (т. Е. Удаляет все из последнего "." до конца строки включительно). По сути, это не жадная подстановка с использованием уловки регулярных выражений.

    Команда для EXTENSION заменяет любое количество символов, за которым следует символ "." в начале строки, ничем (т. Е. Удаляет все от начала строки до последней точки, включительно ). Это жадная подстановка, которая является действием по умолчанию.

    Вы можете использовать basename .

    Вам необходимо указать базовое имя с расширением, которое необходимо удалить, однако, если вы всегда выполняете tar с -z , то вы знаете, что расширение будет .tar.gz .

    Это должно делать то, что вы хотите:

    Используя Bash, есть также $ , чтобы получить имя файла без расширения, и $ , чтобы получить только расширение. Это,

    Не нужно возиться с awk , sed или даже perl для этой простой задачи. Существует чистое-Bash, os.path.splitext() -совместимое решение, которое использует только расширения параметров.

    Разделите путь в имени пути на пару (root, ext) так, чтобы root + ext == path и ext были пустыми или начинались с точки и содержали не более одной точки. Начальные точки в базовом имени игнорируются; splitext('.cshrc') возвращает ('.cshrc', '') .

    Почитание ведущих периодов

    Игнорирование ведущих периодов

    Вот тестовые примеры для реализации Игнорирование начальных периодов , которые должны соответствовать эталонной реализации Python на каждом входе.

    Результаты теста

    Все тесты прошли.

    принятый ответ хорошо работает в типичных случаях , но не работает в крайних случаях , а именно:

    • Для имен файлов без расширения (называемых суффиксом в оставшейся части этого ответа) extension=$ возвращает имя входного файла, а не пустую строку.
    • extension=$ не включает начальный . , что противоречит соглашению.
      • Слепое добавление . к именам файлов без суффикса не сработает.

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

      Обратите внимание, что аргументы после пути ввода выбираются произвольно, позиционные переменные имена .
      Чтобы пропустить переменные, не представляющие интереса, которые предшествуют уже имеющимся, укажите _ (для использования выбрасываемой переменной $_ ) или '' ; например, чтобы извлечь только корень и расширение имени файла, используйте splitPath '/etc/bash.bashrc' _ _ fnameroot extension .

      Тестовый код, выполняющий функцию:

      Ожидаемый результат - обратите внимание на крайние случаи:

      • имя файла без суффикса
      • имя файла, начинающееся с . ( не считается началом суффикса)
      • входной путь, заканчивающийся на / (конечный / игнорируется)
      • входной путь, который является только именем файла ( . возвращается как родительский путь)
      • имя файла, содержащее более . - префиксного токена (суффиксом считается только последний):

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

      Во всех вариантах использования в качестве входных данных используется исходный полный путь, вне зависимости от промежуточных результатов.

      Самое маленькое и простое решение (в одну строку):

      Вы можете принудительно вырезать все поля и последующие, добавляя - к номеру поля.

      Итак, если ФАЙЛ равен eth0.pcap.gz , РАСШИРЕНИЕ будет pcap.gz

      Используя ту же логику, вы также можете получить имя файла, используя '-' со следующим образом:

      Это работает даже для имен файлов, у которых нет расширения.

      Распознавание волшебных файлов

      В Linux и других unixen существует волшебная команда с именем file , которая определяет тип файла, анализируя некоторые первые байты файла. Это очень старый инструмент, изначально использовавшийся для серверов печати (если не был создан для . я не уверен в этом).

      Расширения стандартов можно найти в /etc/mime.types (на моем Debian рабочего стола GNU / Linux. См. man file и man mime.types . Возможно, вам придется установить утилиту file и пакеты mime-support ):

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

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

      (Это вдохновлено правильным ответом @Petesh):

      Хорошо, если я правильно понимаю, проблема здесь в том, как получить имя и полное расширение файла с несколькими расширениями, например, stuff.tar.gz .

      Это работает для меня:

      Это даст вам stuff как имя файла и .tar.gz как расширение. Он работает с любым количеством расширений, включая 0. Надеюсь, это поможет тем, у кого такая же проблема =)

      Это учитывает несколько точек и пробелов в имени файла, однако, если расширения нет, он возвращает само имя файла. Хотя легко проверить; просто проверьте, совпадают ли имя файла и расширение.

      Естественно, этот метод не работает для файлов .tar.gz. Однако это можно сделать в два этапа. Если расширение - gz, проверьте еще раз, есть ли еще расширение tar.

      Как извлечь имя файла и расширение в fish :

      Предостережения: разбивается на последнюю точку, что хорошо подходит для имен файлов с точками в них, но не подходит для расширений с точками в них. См. Пример ниже.

      Использование:

      Вероятно, есть способы сделать это лучше. Не стесняйтесь редактировать мой ответ, чтобы улучшить его.

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

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

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

      Вот код с AWK. Это можно сделать проще. Но я плохо разбираюсь в AWK.

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

      Основываясь в основном на отличных материалах @ mklement0 и полных случайных полезных башизмов , а также на других ответах на этот / другие вопросы / "чертов интернет" . Я все это завернул в немного более понятная, многоразовая функция для моего (или вашего) .bash_profile , которая заботится о том, что (я считаю) должно быть более надежной версией dirname / basename / что у вас ..

      Это отключит последнее вхождение .tar. .

      В более общем плане, если вы хотите удалить последнее вхождение. . , тогда

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

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

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

      Это единственное, что у меня сработало:

      Это также можно использовать при интерполяции строк, но, к сожалению, вам нужно заранее установить base .

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

      Используя пример файла /Users/Jonathan/Scripts/bash/MyScript.sh , этот код:

      Приведет к тому, что $ будет MyScript , а $ будет .sh :

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

      Вы можете использовать команду cut , чтобы удалить последние два расширения (часть ".tar.gz" ):

      Как отметил Клейтон Хьюз в комментарии, это не сработает для фактического примера в вопросе. В качестве альтернативы я предлагаю использовать sed с расширенными регулярными выражениями, например:

      Он работает, безоговорочно удаляя два последних (буквенно-цифровых) расширения.

      Читайте также: