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

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

quite awful code sample

Я хотел бы записать букву редактору этой публикации обо что случилось с этим и как записать это лучше.

Сценарий пытается получить jpeg файлы в переменную, так, чтобы что-то (использование сжатия lepton) может быть сделан с ними.

for jpeg in `echo "$(file $(find ./ ) |
   grep JPEG | cut -f 1 -d ':')"`
  do
     /path/to/command "$jpeg"
...

По-видимому, в этом экземпляре мы не можем доверять файлам, которые назовут с a .jpg расширение, таким образом, мы не можем поймать их с чем-то как

for f in *.JPG *.jpg *.JPEG *.jpeg ; do ...

потому что писатель использовал file проверять их тип, но если именам файлов нельзя доверять, чтобы иметь разумное расширение, то я не вижу, как мы можем доверять им, чтобы не быть -rf * или (; \ $!| или имейте новые строки или безотносительно.

Как я могу нормально получить файлы в переменную типом с for или while, или, возможно, постарайтесь не делать так при помощи find с -exec, или некоторый другой метод?

Премия для понимания и демонстраций что случилось с кодом в изображении.

Я отметил этот вопрос с [ударом], так как это о сценарии удара, но если Вы испытываете желание отвечать способом сделать это, которое не использует удар, затем не стесняйтесь делать это.

6
задан 31 August 2017 в 10:44

3 ответа

Код сначала:

Давайте сделаем это со специальными шариками и a Bash for цикл:

#!/bin/bash
shopt -s globstar dotglob

for f in ./** ; do 
    if file -b -- "$f" | grep -q '^JPEG image data,' ; then 

        # do whatever you want with the JPEG file "$f" in here:
        md5sum -- "$f"

    fi
done

Объяснение:

В первую очередь, мы должны сделать шарики Bash более полезными путем включения globstar и dotglob опции оболочки. Вот их описание от man bash в SHELL ВСТРОЕННЫЙ раздел COMMANDS о shopt:

 dotglob 
    If set, bash includes filenames beginning with a `.' in the results of 
    pathname expansion.
 globstar
    If set, the pattern ** used in a pathname expansion context will match 
    all files and zero or more directories and subdirectories. If the pattern
    is followed by a /, only directories and subdirectories match.

Затем мы используем этот новый "рекурсивный шарик" ./** в a for цикл для итерации по всем файлам и папкам в текущем каталоге и всех его подкаталогах. Всегда используйте полные пути или явные относительные пути, запускающиеся с a ./ или ../ в Ваших шариках, не просто **, предотвратить проблемы со специальными именами файлов как ~.

Теперь мы тестируем каждый файл (и папка) имя с file команда для ее содержания. -b опция препятствует тому, чтобы он печатал имя файла снова перед строкой информации о содержании, которая делает фильтрацию более безопасной.

Теперь мы знаем, что информация о содержании всех допустимых файлов JPG/JPEG должна запуститься с JPEG image data,, который является тем, из чего мы тестируем вывод file поскольку с grep. Мы используем -q опция подавить любой вывод, поскольку мы только интересуемся grepкод выхода, который указывает, соответствовал ли шаблон или нет.

Если это соответствовало, код в if/then блок будет выполняться. Мы можем сделать что-либо, в чем мы хотим здесь. Текущее имя файла JPEG доступно в переменной оболочки $f. Мы просто должны удостовериться, что всегда поместили его в двойные кавычки для предотвращения случайной оценки имен файлов со специальными символами как пробелы, новые строки или символы. Также обычно лучше разделить его от других аргументов путем размещения его после --, который заставляет большинство команд интерпретировать его как имя файла, даже если это - что-то как -v или --help это было бы иначе интерпретировано как опция.


Вопрос о премии:

Время для аварийного завершения некоторого кода, для науки! Вот версия от Вашего вопроса/книги:

for jpeg in `echo "$(file $(find ./ ) 
    | grep JPEG | cut -f 1 -d ':')"`
do
     /path/to/command "$jpeg"
done

В первую очередь, позвольте мне упоминать, как сложный они записали это. У нас есть 4 уровня вложенных подоболочек, с помощью смешанных синтаксисов замены команды (`` и $()), которые просто необходимы из-за неправильного/субоптимального использования find.

Здесь find просто списки все файлы и печать их имена, один на строку. Затем полный вывод передается file исследовать каждого из них. Но ожидайте! Одно имя файла на строку? Что относительно имен файлов, содержащих новые строки? Право, они повредят его!

$ ls --escape ne*ne
new\nline
$ file $(find . -name 'ne*ne' )
./new: cannot open `./new' (No such file or directory)
line:  cannot open `line' (No such file or directory)

На самом деле даже простые пробелы повреждают его также, потому что их рассматривают как разделители также file. Вы не можете даже заключить в кавычки "$(find ./ )" здесь как средство, потому что это затем заключило бы целый многострочный вывод в кавычки как один единственный аргумент имени файла.

$ ls simple*
simple spaces.jpg
$ file $(find ./ -name 'simple*')
./simple:   cannot open `./simple' (No such file or directory)
spaces.jpg: cannot open `spaces.jpg' (No such file or directory)

Следующий шаг, file вывод сканируется с grep JPEG. Не делайте Вы думаете, что немного легко обмануть такой простой шаблон, тем более, что вывод плоскости file всегда содержит имя файла также? В основном все с "JPEG" в его имени файла инициирует соответствие, независимо от того, что он содержит.

$ echo "to be or not to be" > IAmNoJPEG.txt
$ file IAmNoJPEG.txt | grep JPEG
IAmNoJPEG.txt: ASCII text

Хорошо, таким образом, мы имеем file вывод всех файлов JPEG (или те, кто симулирует быть одним), теперь они обрабатывают все строки с cut извлечь исходное имя файла из первого столбца, разделенного двоеточием... Угадайте, что, давайте попробуем это на файле с двоеточием на его имя:

$ ls colon*
colons:evil.jpeg
$ file colon* | grep JPEG | cut -f 1 -d ':'
colons

В заключение, подход от Ваших книжных работ, но только если все файлы это проверяет, не содержит пробелов, новых строк, двоеточий и вероятно других специальных символов и не содержит строку "JPEG" нигде в их именах файлов. Это также довольно ужасно, но поскольку красота лежит в глазу наблюдателя, я не собираюсь околачиваться об этом.

5
ответ дан 23 November 2019 в 07:25

0. Сценарий хочет сделать что-то вроде этого.

Сценарий, показанный в Вашем вопросе, пытается перечислить файлы и проверку, если они - JPEGs, но не делает ни одного надежно. Это пытается передать все пути к file в единственном выполнении и извлечении и имена файлов и типы от вывода file, который разумен, так как это может быть быстрее, чем выполнение file снова и снова для каждого файла. Но сделать это правильно, необходимо быть осторожны относительно того, как пути передаются file, как file разграничивает его вывод, и как Вы используете тот вывод. Можно использовать это:

#!/bin/bash

find . -exec file --mime-type -r0F '' {} + | while read -rd ''; do
    read -r mimetype
    case "$mimetype" in image/jpeg)
        # Bash placed the filename in "$REPLY" -- put commands that use it here.
        # You can have as many commands as you want before the closing ";;" token.
        ;;
    esac
done

Это - один из нескольких корректных путей. (Это не должно устанавливать IFS=; посмотрите ниже.) find с + аргументы разнообразного пути передач file и только выполнения это так же много раз по мере необходимости для обработки их всех, обычно только однажды. Кредит переходит к AFSHIN для идеи передать --mime-type кому: file получить тип MIME, который содержит информацию, которую Вы на самом деле хотите и легки проанализировать.

Подробное объяснение следует. Я использовал определенную задачу сжатия JPEG как пример. Это - то, что сценарий, который Вы показали, для, и lepton имеет некоторые причуды, которые нужно рассмотреть в решении, как улучшить тот сценарий. Если Вы просто хотите видеть сценарий, который работает lepton на каждом файле JPEG можно пропустить для разделения 7. Соединение Всего этого.

Термин путь имеет несколько определений. В этом ответе я использую его для значения пути.

1. Установка lepton

Сценарий, который Вы показали, предназначен, чтобы пересечь иерархию каталогов, найти изображения JPEG и обработать их с компрессором JPEG без потерь lepton. Для основной мотивации Вашего вопроса не может действительно иметь значения команда, но различные команды имеют другой синтаксис. Некоторые команды принимают несколько входных имен файлов для единственного выполнения. Большинство принимает -- указать на конец опций. Я буду использовать lepton как мой пример. lepton команда не принимает несколько входных имен файлов и не распознает --.

Использовать lepton, установите его сначала. Это официально упаковывается для Ubuntu 17.04 и позже (sudo apt install lepton). Для более ранних релизов Ubuntu, или использовать более новую версию, чем упаковывается для Вашего выпуска, клон git репозиторий (git clone https://github.com/dropbox/lepton.git) и создайте источник, как проинструктировано в README. Или Вы смогли находить PPA.

Завися, как Вы устанавливаете его, lepton может быть в /usr/bin, /usr/local/bin, или в другом месте. Вероятно, Вы захотите это где-нибудь в $PATH; затем можно выполнить его как lepton. Сценарий Вы показали полные пути использования lepton и стандартные утилиты mv и rm, но не к другим стандартным утилитам file, find, grep и cut. (Это - Bash, таким образом, echo- бессмысленный в том сценарии так или иначе - встроенная оболочка. exit всегда встроенное.), Хотя это не один из серьезных дефектов сценария, нет никакой заметной причины такого несоответствия. Если Вы не пишете сценарий для признания не наличия $PATH набор разумно - в этом случае необходимо использовать полные пути для всех внешних команд - я предлагаю использовать относительные пути для стандартных команд и тех, которых Вы установили.

2. Выполнение lepton

Предостережения и общая информация

Я протестировал с лептоном v1.0-1.2.1-104-g209463a (от Мерзавца). lepton был выпущен назад в июле 2016, таким образом, я предположу, что текущий синтаксис будет продолжать работать. Но будущие версии могут добавить опции. При чтении этого годы с этого времени Вы могли бы проверить если lepton добавила поддержка задач, которые когда-то потребовали сценариев.

Будьте осторожны, какие параметры командной строки Вы передаете. Например, я пытался работать lepton с -verbose как первый аргумент и art.jpg как второе. Это интерпретировало -verbose как входное имя файла и выход с ошибкой, но не перед усечением art.jpg- который это интерпретировало как выходное имя файла - вниз для обнуления байтов. К счастью, у меня было резервное копирование!

Можно передать нуль, один, или два пути к lepton. Во всех случаях это исследует свой входной файл или поток, чтобы видеть, содержит ли это данные Лептона или JPEG. JPEG сжат до Лептона; Лептон распаковывается к JPEG. lepton удалит и добавит расширения файла, но не использует их для решения, что сделать.

Нулевые имена файлов — lepton - чтения от stdin и записей к stdout.

Таким образом lepton - < infile > outfile один путь состоит в том, чтобы читать из infile и запишите в outfile, даже если их имена запускаются с - (как опции делают). Но метод, я буду использовать пути передач, которые запускаются с ., таким образом, я не должен буду волноваться об этом.

Одно имя файла — lepton infile чтения infile и называет его собственный выходной файл.

Это - то, как сценарий Вы показали использование lepton.

Если содержание infile похож на JPEG, lepton производит файл Лептона; если его содержание похоже на файл Лептона, lepton производит JPEG. lepton решает, как это хочет назвать свой выходной файл путем разделения расширения от infile, если таковые имеются, и добавление любого a .jpg или .lep расширение в зависимости от того, какой файл это создает. Но это не использует расширение, которое это удаляет (если таковые имеются) для выведения типа файла, на который это воздействует.

Это рассматривает последнее . и что-либо после него как расширение. Если infile a.b.c, Вы добираетесь a.b.lep или a.b.jpg. Если имя файла запускается с a . без другого .s, lepton все еще отношения, что как расширение: от названного JPEG .abc Вы добираетесь .lep. Только . в имени файла - не именах каталогов - инициировал это, таким образом, из файла Лептона x/fo.o/abc Вы добираетесь x/fo.o/abc.jpg (который Вы хотите), нет x/fo.jpg (который был бы плох).

Если выходное имя файла получило этот путь имена существующий файл, _s добавляются в конец, после расширения, пока оно не делает, и используется имя с добавленными символами нижнего подчеркивания: abc.lep, abc.lep_, abc.lep__, и т.д.,xyz.jpg, xyz.jpg_, xyz.jpg__, и т.д.

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

Автоматически удаление и добавление расширений и добавление символов нижнего подчеркивания избегают проблемы, которой необходимо было бы иначе управлять сами - предотвращение потери данных, когда выходной файл уже существует. Но это также выставляет то, что могло бы быть глубоким недостатком дизайна в сценарии, который Вы показали. Если Ваши файлы называют разумно, то весь Ваш конец файлов JPEG в .jpg или .jpeg (возможно, использованный для своей выгоды), и никакие файлы не-JPEG так названы. Но затем Вы не должны исследовать файлы с file узнать, которые JPEGs!

Таким образом предпосылка сценария, который Вы показали, - то, что файлы нельзя было бы назвать обоснованно. Это всегда плохо для сценария для поведения неправильно или неожиданно на именах файлов, содержащих пробелы, *, и другие специальные символы. Таким образом, его поведение разделения на пробеле и расширения шариков (внешняя неупомянутая замена команды, предназначенная только для разделения отдельных имен файлов, делает это) особенно плохо. См. превосходный ответ Командующего Байта для деталей. Это - вероятно, худший дефект в сценарии, который Вы показали.

Но это также достойно рассмотрения, что происходит с именами файлов чей в последний раз . концептуально не начинает расширение файла. Предположим Pictures имеет четыре файла, весь JPEGs: 01. Milan wide-angle sunset, 01. Milan wide-angle sunset highres, 02. Kyle birthday party prep - blooper cakes, и 03. The subtle found art of unopened expired paint cans with peeling labels. Затем for f in ~/Pictures/0*; do lepton "$f"; done создает 01.lep, 01.lep_, 02.lep, и 03.lep- вероятно, не, что Вы хотите.

Если у Вас есть JPEGs, не названный .jpg или возможно .jpeg, лучший общий подход должен переименовать их тот путь и исследовать любые конфликты имен, которые возникают при выполнении так. Но это выходит за рамки этого ответа.

Те, которые переименовывают проблемы, происходят с JPEGs, не названным как JPEGs, не non-JPEGs названный как JPEGs. Все же даже затем, может быть лучшее решение. Если проблема ._ файлы от macOS и Вы не хотите удалять их, просто исключать файлы с продвижением ._ (или даже продвижение .). Однако, передавая всего один путь к lepton избегает потери данных (из-за _ добавление правил); если главная цель состоит в том, чтобы исключить non-JPEGs, основная идея является звуковой даже при том, что для реализации нужна фиксация.

Таким образом, я буду использовать один путь lepton infile синтаксис. Но любой, кто рассматривает автоматизацию lepton как это на странно именованных файлах должен помнить сгенерированный .lep файлы можно назвать способами, которые не показывают входные имена файлов.

Два имен файлов — lepton infile outfile делает точно, что Вы ожидаете.

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

Как с другими способами работать lepton, lepton определяет ли infile JPEG должен быть сжат или файл Лептона, который будет распакован путем исследования его содержания. Если infile JPEG, lepton пишет названный файл Лептона outfile; если infile файл Лептона, lepton пишет названный JPEG outfile. С этим синтаксисом с двумя путями, lepton не изменяет Ваше указанное выходное имя файла всегда. Это не добавляет или удаляет расширения или добавляет _s для разрешения конфликтов имен. Если outfile уже существует, это перезаписывается.

Можно хотеть это, но если не и Вы используете этот синтаксис затем, необходимо решить проблему сами, заставив сценарий скорректировать выходные имена файлов. Вы можете делать это способом, которое служит Вам лучше, чем leptonсобственная схема, когда выполнено со всего одним параметром пути. Но я не попытаюсь предположить Ваши определенные потребности и предпочтения; я буду просто использовать синтаксис с одним путем.

3. Передача разнообразных путей от find кому: file

Сценарий Вы показали попытки использовать file $(find ./ ) передать один путь на аргумент file путем выполнения find в замене команды. Это часто не будет работать, потому что $(find ./ ) разделения на пробеле, который могут содержать имена файлов. Это характерно для файлов - особенно отображает! - и папки, чтобы иметь пробелы на их имена. Сценарий Вы показали обработкам путь ./abc/foo bar.jpg как два пути, ./abc/foo и bar.jpg. В лучшем случае ни один не существует; если они делают, Вы неумышленно воздействуете на неправильную вещь. И первоначальный тракт не будет обработан вообще.

Хотя ширина этой проблемы может быть уменьшена путем установки IFS=$'\n' таким образом, разделение слова только выполняется между строками (\n представляет символ новой строки), это не хорошее решение. Помимо того, чтобы быть неловким, это может все еще перестать работать, поскольку имена файлов и имена каталогов могут содержать новые строки. Я отговариваю от именования файлов или каталогов с ними кроме к тестовым программам или сценариям для ошибок. Но такие имена могут быть созданы, включая случайно, где Вы не ожидаете их. Единственные символы, которые не может содержать имя файла, являются разделителем пути / и нулевой символ. Нулевой символ является таким образом единственным, который не может появиться в пути и единственном безопасном выборе разграничить списки произвольных путей. Вот почему find имеет a -print0 действие и xargs имеет a -0 опция.

Это может быть сделано правильно с find . -print0 | xargs -0 ... но Вам не нужна третья утилита для передачи путей от find кому: file. find -exec действие достаточно. Аргументы после -exec создайте команду для выполнения, до \; или +. find ... -exec ... \; выполняет команду однажды на файл, в то время как find ... -exec ... + передает команду столько путей, сколько она может на выполнение, которое обычно быстрее. Обычно все аргументы соответствуют и выполнения команды только однажды. В редких случаях командная строка была бы слишком длинной и find выполняет команду несколько раз. Так + форма только безопасна для выполнения команд, которые (a) взятие их параметры пути в конце и (b) работа то же в одном выполнении с несколькими именами файлов, как они делают в отдельных выполнениях.

lepton пример команды, которая не должна быть выполнена с помощью + форма -exec потому что это не принимает несколько исходных имен файлов. Первым был бы вход, вторым будет вывод, и другие были бы чрезмерными. Но много команд действительно делают то же самое, когда выполнено однажды с несколькими аргументами как тогда, когда выполнено несколько раз с одним аргументом, и file один из них.

Эта команда генерирует таблицу:

find . -exec file --mime-type -r0F '' {} +

find замены {} спор с путем, когда это вызывает file, и замены + со столькими дополнительными параметрами пути, сколько будет соответствовать.

Опции --mime-type -r0F '' переданный find объяснены ниже.

Некоторые люди кавычка {}, например, '{}'. Хорошо делать так, но ни Bash, ни другие оболочки стиля Границы не требуют его. Bash и некоторое другое расширение фигурной скобки поддержки оболочек, но пустая пара фигурных скобок не расширены. Я принимаю решение не заключить в кавычки {}, в свете неправильного представления то заключение в кавычки {} предотвращает find от работающего разделения слова. Даже если Ваша оболочка требуется {} чтобы быть заключенным в кавычки, это все еще не имело бы никакого отношения к разделению слова, потому что find никогда не делает это. (Если бы Вы хотели разделение слова, то необходимо было бы сказать find кому: -exec оболочка.) И find не может сказать, записали ли Вы {} или '{}'- повороты оболочки '{}' в {} (во время удаления кавычки) прежде, чем передать его find.

4. Испускание применимого ⟨Path, тип файла ⟩ таблица с file

Проблема

Причина я должен передать некоторые опции file- и не может просто использовать find . -exec file {} +- это таблица file генерирует по умолчанию неоднозначно:

01. Milan wide-angle sunset:                  JPEG image data, JFIF standard 1.01, resolution (DPI), density 1x1, segment length 16, baseline, precision 8, 1400x1400, frames 3
02. Kyle birthday party prep - blooper cakes: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 512x512, frames 3
first line
second line:                       JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 500x500, frames 3

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

Столбец описания также имеет путь больше информации, чем нам нужно. Командующий байта объясняет одну причину grepлуг для JPEG в каждой целой строке возвраты неправильно заканчивается: файл не-JPEG с JPEG на его имя дает положительную ложь. (Точка проверки типа - то, что Вы не можете полагаться на имя, таким образом, это - вполне пагубная ошибка в сценарии, Вы показали.), Но даже когда Вы знаете, что смотрите в столбце описания, он может все еще содержать JPEG даже если это не тип:

$ touch empty.JPEG  # not a JPEG
$ gzip -k empty.JPEG
$ file empty.JPEG*
empty.JPEG:    empty
empty.JPEG.gz: gzip compressed data, was "empty.JPEG", last modified: Mon Aug 28 16:37:56 2017, from Unix

Ответ Командующего байта решил это (a), передающим -b опция к file, то, чтобы заставлять это опустить пути, : разделитель и пробелы перед типом, затем (b) использование grep проверять, начинается ли описание JPEG ( ^ привязка в шаблоне ^JPEG image data, делает это). Это работает, если Вы отслеживаете пути, переданные file- не проблема для метода Командующего Байта, который работал file отдельно для каждого пути так или иначе.

Решение

Я должен использовать другое решение, потому что моя цель состоит в том, чтобы проанализировать и пути и типы от fileвывод так, чтобы file не должен быть выполнен отдельно для каждого файла. К счастью, file в Ubuntu имеет много опций. Я использую file --mime-type -r0F '' paths:

  • --mime-type печатает тип MIME, а не подробное описание. Это - все, в чем я нуждаюсь, и затем я могу просто выполнить точное совпадение против всего этого. Для JPEG, file --mime-type шоу image/jpeg в столбце описания. (См. также ответ AFSHIN.)
  • Согласно man file, -r заставляет непечатные символы не быть замененными восьмеричными Escape как \003. Я полагаю, что должен был бы иначе добавить шаг для преобразования таких последовательностей назад в фактические символы, которые, вероятно, не могут быть сделаны надежно - что, если такая последовательность появляется буквально в имени файла? (file не выходит \ как \\.) Я говорю, что "Верю", поскольку мне не удалось добраться file для распечатывания такой escape-последовательности, и я не уверен, что она действительно делает так в столбце имени файла. Так или иначе, -r безопасно здесь.
  • -0 ключевая опция здесь. Без него этот метод не мог работать надежно. Это делает file распечатайте нулевой символ - один символ, который никогда не позволяется в путях, потому что он обычно используется для маркировки концов строк в программах C - сразу после имени файла. Это отмечает повреждение, в каждой строке, между двумя столбцами таблицы.
  • -F '' делает file ничего не распечатайте ('' пустой аргумент) вместо :. Двоеточие ненадежно (это может появиться в именах файлов), и никакого преимущества здесь, так как нулевой символ уже печатается для указания на конец столбца пути и запуск столбца описания.

Сделать find выполненный file --mime-type -r0F '' paths Я использую -exec file --mime-type -r0F '' {} +. find -exec замены действия {} + с путями.

5. Потребление таблицы

Я составил таблицу этот путь:

find . -exec file --mime-type -r0F '' {} +

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

Чтение каждой строки

Та комбинация - текст чтения, который может содержать новую строку до нулевого символа, затем прочитал текст, который не может содержать новую строку, пока новая строка - не то, как любая из общих утилит Unix обычно используется. Подход, который я проявлю, должен передать вывод по каналу find к циклу. Каждое повторение цикла читает одну строку таблицы при помощи read оболочка, встроенная дважды, с различными вариантами.

Для чтения пути я использую:

read -rd ''
  • -r readтолько стандартная опция и Вы должны почти всегда использовать его. Без него обратная косая черта выходит как \n от входа переводятся в символы, которые они представляют. Мы не хотим это.
  • Обычно, read чтения, пока это не видит новой строки. Для игнорирования новых строк и остановки в нулевом символе вместо этого, я использую -d возможность, которую Bash предоставляет, для определения другого символа. Для нулевого символа передайте пустой аргумент ''.
  • Я уже использую расширение Bash ( -d опция), таким образом, я могу также пользоваться поведением Bash по умолчанию, когда никакое имя переменной не передается read. Это помещает все, что это считало - кроме оконечного знака - в специальной переменной $REPLY. Обычно read пробел полос ($IFS символы) с начала и конца входа, и это - общая идиома для записи IFS= read ... предотвратить это. При чтении неявно в $REPLY в Bash это не необходимо.

Для чтения описания я использую:

read -r mimetype
  • Никакие обратные косые черты не должны появляться в типе MIME, но это - хорошая практика для передачи -r кому: read если Вы не хотите \ Escape переводятся.
  • На этот раз я указываю имя переменной явно. Назовите его, что Вы любите. Я выбрал mimetype.
  • На этот раз, отсутствие IFS= препятствовать тому, чтобы ведущий и запаздывающий пробел был разделен, является значительным. Я хочу удаленный. Это отбрасывает пробелы с начала описания это find записи для создания таблицы более человекочитаемой, когда это показывают в терминале.

Создание цикла

Цикл должен продолжиться, пока существует другой путь, который будет считан. read команда возвращает true (в оболочке, программируя это, нуль, в отличие от почти всех других языков программирования), когда это успешно читает что-то и ложь (в программировании оболочки, любом ненулевом значении), когда это не делает. Так общее while read идиома полезна здесь. Я передаю по каналу (|) вывод find- который является выводом одного или (редко) больше file команды - к while цикл.

find . -exec file --mime-type -r0F '' {} + | while read -rd ''; do
    read -r mimetype
    # Commands using "$REPLY" and "$mimetype" go here.
done

В цикле я считал остальную часть строки для получения описания (read -r mimetype). Я не потрудился проверять, успешно выполнилось ли это. file должен только когда-либо производить полные строки, даже если это встречается с ошибками. (file отправляет сообщения об ошибках и предупреждающие сообщения к стандартной погрешности, таким образом, они, будет казаться, в конвейере не повредят таблицу.) Необходимо смочь полагаться на это.

Если Вы хотите проверить если read -r mimetype следовавший так или иначе, можно использовать if. Или можно включать его в while условие цикла:

find . -exec file --mime-type -r0F '' {} + |
while read -rd '' && read -r mimetype; do
    # Commands using "$REPLY" and "$mimetype" go here.
done

Вы видите, что я также разделяю верхнюю строку для удобочитаемости. (Нет \ требуется разделить в |.)

Тестирование цикла

Если Вы хотите протестировать цикл перед продолжением, можно поместить эту команду под (или вместо) # Commands... комментарий:

    printf '[%s] [%s]\n\n' "$REPLY" "$mimetype"

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

[.] [inode/directory]

[./stuv] [inode/x-empty]

[./ghi
jkl] [inode/x-empty]

[./fo.o/abc
def   ] [image/jpeg]

[./fo.o/wyz.lep] [application/octet-stream]

[./fo.o/wyz] [image/jpeg]

Это должно только видеть, работает ли цикл правильно. Размещение записей таблицы в [ ] как это не помог бы сценарию сделать то, что он должен сделать, поскольку пути могут содержать [, ], и последовательные новые строки.

6. Использование извлеченного типа тракта и типа файла

В каждом повторении цикла, "$REPLY" содержит путь и "$mimetype" содержит описание типа. Узнать если "$REPLY" называет файл JPEG, проверьте если "$mimetype" точно image/jpeg.

Можно сравнить строковое использование if и [/test (или [[) с =. Но я предпочитаю case:

find -exec file --mime-type -r0F '' {} + | while read -rd ''; do
    read -r mimetype
    case "$mimetype" in image/jpeg)
        # Put commands here that use "$REPLY".
        ;;
    esac
done

Если Вы просто хотели показать пути JPEG в том же формате как выше - чтобы помочь протестировать с путями, содержащими новые строки - все case...esac оператор мог быть:

    case "$mimetype" in image/jpeg) printf '[%s]\n\n' "$REPLY";; esac

Но цель состоит в том, чтобы работать lepton на каждом файле JPEG. Чтобы сделать это, используйте:

    case "$mimetype" in image/jpeg) lepton "$REPLY";; esac

7. Соединение всего этого

Добавление этого lepton команда и hashbang строка для выполнения его с Bash, вот являются полным сценарием:

#!/bin/bash

find . -exec file --mime-type -r0F '' {} + | while read -rd ''; do
    read -r mimetype
    case "$mimetype" in image/jpeg) lepton "$REPLY";; esac
done

lepton отчеты, что это делает, но это не показывает имена файлов. Этот альтернативный сценарий печатает сообщение с каждым путем перед выполнением lepton на нем:

#!/bin/bash

find . -exec file --mime-type -r0F '' {} + | while read -rd ''; do
    read -r mimetype
    case "$mimetype" in image/jpeg)
        printf '\nProcessing "%s":\n' "$REPLY" >&2
        lepton "$REPLY"
    esac
done

Я распечатал сообщения к стандартной погрешности (>&2), так как это то, где lepton отправляет его собственные сообщения. Тем путем, вывод, все остается вместе при передаче по каналу или перенаправлении. Запущение того скрипта производит вывод как это (но больше из него, если у Вас есть больше чем два JPEGs):

Processing "./art.jpg":
lepton v1.0-1.2.1-104-g209463a
6777856 bytes needed to decompress this file
56363 86007
65.53%
2635854 bytes needed to decompress this file
56363 86007
65.53%

Processing "./fo.o/abc
def   ":
lepton v1.0-1.2.1-104-g209463a
6643508 bytes needed to decompress this file
36332 46875
77.51%
2456117 bytes needed to decompress this file
36332 46875
77.51%

Повторение в каждой строке файла конфигурации - который также появляется, когда Вы работаете lepton не печатая имена файлов - то, потому что lepton проверки, которые его выходные файлы могут распаковать правильно.

Сценарий, который Вы показали, имел exit 0 в конце. Можно сделать это, если Вам нравится. Это заставляет сценарий всегда сообщать об успехе. Иначе сценарий возвращает статус выхода последней выполненной команды - который, вероятно, предпочтителен. Так или иначе это может сообщить об успехе даже если find, file, или lepton возникшие проблемы, если последнее lepton за командой следуют. Можно, конечно, развернуть сценарий с более сложным кодом обработки ошибок.

8. Возможно, Вы хотите пути, также

Если Вы хотите генерировать список путей, отдельных от leptonсобственный вывод, можно использовать в своих интересах leptonповедение записи в стандартную погрешность путем печати путей к стандартному выводу вместо этого. В этом случае Вы, вероятно, хотите распечатать просто пути и не сообщение "Обработки". Можно дополнительно хотеть завершить пути с нулевыми символами вместо новых строк, поскольку затем можно обработать список, не повреждаясь на путях, которые содержат новые строки.

#!/bin/bash

case "$1" in
    -0) format='%s\0';;
    *)  format='%s\n';;
esac

find . -exec file --mime-type -r0F '' {} + | while read -rd ''; do
    read -r mimetype
    case "$mimetype" in image/jpeg)
        printf "$format" "$REPLY"
        lepton "$REPLY"
    esac
done

Когда Вы запускаете тот скрипт, можно передать -0 флаг, чтобы заставить его испустить нулевые символы вместо новых строк. Тот сценарий не делает надлежащей обработки Параметра стиля Unix: это только проверяет первый аргумент, который Вы передаете; передача флага неоднократно в том же аргументе (-00) не работает; и никакие связанные с опцией сообщения об ошибках никогда не сгенерированы. Это ограничение для краткости, и потому что Вам, вероятно, не нужно ничто более сложное, поскольку сценарий не поддерживает аргументов неопции и -0 единственный возможный вариант.

В моей системе я назвал тот сценарий jpeg-lep3 и вставленный в него ~/source, затем работал ~/source/jpeg-lep3 -0 > out, который распечатал просто leptonвывод к моему терминалу. Если Вы делаете что-то как этот, можно протестировать это, нулевые символы были правильно записаны между использованием путей:

xargs -0 printf '[%s]\n\n' < out
6
ответ дан 23 November 2019 в 07:25

Вы имеете find и сверьтесь file команда для ее типа пантомимы также.

find . -type f -exec file --mime-type -b '{}' +

Или заставить его завершиться так же как следуйте:

find . -type f -exec sh -c '
    file --mime-type -b "$0" | grep -q "aPATTERN" && printf "$0\n"
' {} \;

Или identify опция от пакетов ImageMagic.

find -type f -print0 | xargs -0 identify
1
ответ дан 23 November 2019 в 07:25

Другие вопросы по тегам:

Похожие вопросы: