В отличие от многих других языков программирования, синтаксис оболочки Борна имеет определенные особенности, особенно в отношении пробелов в определенных местах. И вот что вас здесь запутало:
Важно помнить, что [
- это команда , а не функция синтаксиса (сноска 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"
.
Ответ 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
Вы можете получить точную продолжительность в секундах, а затем просуммировать их с помощью 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