Как найти накопленную продолжительность на нескольких mp3 с командной строкой?

Основная проблема

В отличие от многих других языков программирования, синтаксис оболочки Борна имеет определенные особенности, особенно в отношении пробелов в определенных местах. И вот что вас здесь запутало:

Важно помнить, что [ - это команда , а не функция синтаксиса (сноска 1), и что ] - последний аргумент этой команды.

В оболочке Борна (и аналогичной / производной) пробел (сноска 2) разделяет команды и их аргументы, а ключевые слова и / if / до фактически просто вызовите любую команду, идущую после этих ключевых слов, и действуйте на основе возвращаемого значения.

Другой необычный характер оболочки Bourne, который выглядит как проблема в вашем коде, - это присвоение переменных, которое также зависит от пробелов: между именем переменной должно быть нет пробелов, = знак, и присваиваемая вещь.

Кроме того, вывод команд не попадает автоматически в переменные, как в большинстве языков: для этого вам нужно использовать «подстановку команд». Новый, вложенный и более широко рекомендуемый синтаксис для этого - $ (выполняемая команда) (сноска 3).

Примеры в вашем коде

Итак, применив приведенное выше к вашему коду:

while [ $key = "q"]; do

.. проблема в "q"] . Оболочка видит это как один из аргументов команды test / [: q] (кавычки здесь также ничего не делают: оболочка не имеет типов переменных : все является строкой, за исключением некоторых контекстов, поэтому q уже является строкой - кавычки предназначены для экранирования специальных символов). Я бы исправил это так:

while [ "$key" = q ]; do

.. Я также процитировал $ key , чтобы убедиться, что он всегда интерпретируется правильно (сноска 4). Та же проблема (и еще одна) возникает здесь:

if[$dateTex !eq $datePdf]

.. помните, что оболочка полагается на пробелы, чтобы указать, где заканчивается одно и начинается другое (опять же, сноска 2): если вы скажете if [$ some_var , сначала он заменит $ some_var на some_text , получив if [some_text , а затем попытается выполнить команду с именем if [ some_text .Итак, сначала вам нужно , если отделено пробелом от [.

Это , поэтому ваш код печатает ошибку, которую он делает: bash ожидает , затем только после того, как видит if , но никогда не видит if , он видит if [$ dateTex , который анализируется как совершенно другая команда / токен.

По тому же принципу вы хотите отделить [ от $ dateTex и $ datePdf от ] пробелом.

Наконец, ! Eq не является допустимым аргументом / тестом, который распознает команда [: -eq интерпретирует аргументы вокруг него как целые числа (которые являются по-прежнему строки в том, что касается оболочки, входя в команду [, независимо от того, цитируете вы их или нет), и сравнивает их на равенство. У вас была правильная идея с ! как оператор отрицания, но, как и многие другие вещи в оболочке, он должен быть отдельным аргументом для [ перед другими. Итак, вы хотите:

if [ ! "$dateTex" -eq "$datePdf" ]

..или вы можете использовать оператор отрицания оболочки вместо команды [ (опять же, обратите внимание на интервал - это должен быть отдельный токен / поле, когда синтаксис оболочки разбивается на пробелы):

if ! [ "$dateTex" -eq "$datePdf" ]

Кроме того, согласно другому ответу: = просто проверяет совпадение двух строк аргументов (в отличие от интерпретации строк аргументов как целых чисел перед их сравнением, как -eq ), и это также поддерживает специальный случай отрицания оператора ! = для проверки того, что две строки отличаются друг от друга. Если в вашем сценарии использования достаточно сравнения строк, вы можете использовать одно из следующих:

if [ "$dateTex" != "$datePdf" ]  # != operator
if [ ! "$dateTex" = "$datePdf" ] # test ! operator with test = operator
if ! [ "$dateTex" = "$datePdf" ] # shell ! operator with test = operator

Теперь перейдем к этим строкам с назначениями переменных:

dateTex = grep $1.tex| cut -b 43 - 54
datePdf = grep $1.pdf| cut -b 43 - 54

Во-первых,оболочка проанализирует dateTex = grep $ 1.tex , чтобы попытаться запустить команду dateTex с аргументами = , grep и один или несколько аргументов в зависимости от того, до чего $ 1 раскрывается (опять же, сноска 4). Итак, во-вторых, вы должны указать $ 1 . А для команды cut параметр -b принимает список как один аргумент, поэтому вы хотите объединить 43-54 в один (без пробелов). В любом случае, похоже, вы действительно хотите использовать подстановку команд, поэтому вам нужно что-то вроде этого:

dateTex=$(grep "$1".tex | cut -b 43-54)

.. и проделайте то же самое для другой строки. Наконец, grep filename кажется неправильным: первый аргумент grep (помимо параметров) должен быть шаблоном для поиска / сопоставления. Таким образом, как вы написали в вашем примере кода, он будет читать и искать по стандартному вводу любой шаблон «$ 1» .tex расширяется до. Скорее всего, вы захотите найти в файле "$ 1" .tex шаблон, поэтому вместо этого вам нужен шаблон grep "$ 1" .tex .

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

Сноски

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

[2] Технически это то, что находится в переменной IFS (Внутренний разделитель полей), но обычно это пробелы, табуляции и символы новой строки (при этом новые строки являются немного особенными по другим причинам).

[3] Старый, но не вложенный синтаксис - `command to run` - вы должны использовать его, если хотите, чтобы ваш скрипт запускался в очень старых или несколько нестандартных оболочках, таких как Solaris 10 / bin / sh . Лично я считаю, что этот синтаксис идеален, и не думаю, что вложение (единственное реальное преимущество нового синтаксиса) когда-либо необходимо в сценариях, хотя я вижу, как он упрощает интерактивное использование командной строки.

[4] Если переменная не заключена в кавычки, пробел (или что-то еще в $ IFS , в сноске 1) в переменной оценивается после замены переменной, и «разделение полей» выполняется на всем линия снова. Поэтому, если $ key не задан, пуст или просто только пробелов, он в основном "исчезнет" из команды, если он не заключен в кавычки. Добавьте, если есть смесь, например $ key содержит abc def , он станет двумя аргументами abc и def .Тот же принцип применяется ко всей строке с заменой, поэтому, если $ key содержит abc def , а командная строка - foo $ keybar , оболочка разделяет строку в fooabc defbar (запустите команду fooabc с одним аргументом defbar ). Ничего из этого не происходит, если переменная указана в кавычках: "$ key" .

1
07.11.2018, 17:09
2 ответа

Ответ Ipors Sicer работал отлично. Чтобы добавить к его ответу, я удалил все после точки и использовал этот ответ с небольшой модификацией, чтобы получить это:

secs=$(for file in *.mp3;
    do ffprobe -v error -select_streams a:0 -show_entries stream=duration -of default=noprint_wrappers=1:nokey=1 "$file";
done | paste -sd+|bc -l|cut -f1 -d '.'); 
printf '%d:%d\n' $(($secs/60)) $(($secs%60))

И я получил этот вывод:

87:2
0
27.01.2020, 23:31

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

for file in *.mp3;do ffprobe -v error -select_streams a:0 -show_entries stream=duration -of default=noprint_wrappers=1:nokey=1 "$file";done|paste -sd+|bc -l

Преобразуйте этот номер в формат ЧЧ :ММ :СС самостоятельно. например.https://stackoverflow.com/a/12199816/6481121

4
27.01.2020, 23:31

Теги

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