Вопрос или проблема
Я использую скрипт для транскодирования большого количества фильмов с помощью командной строки HandBrake. Я использую модифицированную версию скрипта здесь:
https://gist.github.com/ralphcrisostomo/56fc395b1646bd55aeeb2eb442043887
Проблема в том, что транскодирование зависает непредсказуемо на небольшой выборке фильмов. Это сопровождается очень специфической ошибкой в stderr.
Что я хотел бы сделать: 1) следить за stdout (но не за stderr) на экране, 2) контролировать stderr на предмет ошибки и 3) если ошибка обнаружена, завершить процесс HandBrake, записать проблемный файл и перейти к следующему.
В настоящее время команда транскодирования выглядит следующим образом:
echo "" | HandBrakeCLI -i "$ITEM" -o "$OUTPUT" --preset="$PRESET" $OPTS 2> /dev/null
что удовлетворяет 1).
Следуя совету здесь:
Как grep стандартный поток ошибок (stderr)?
Я могу успешно grep поток ошибок на предмет соответствующего слова, например:
echo "" | HandBrakeCLI -i "$ITEM" -o "$OUTPUT" --preset="$PRESET" 2> >(grep -i error)
или
echo "" | HandBrakeCLI -i "$ITEM" -o "$OUTPUT" --preset="$PRESET" 3>&2 2>&1 1>&3- | grep -i "error"
Однако все это делает только то, что печатает соответствующую строку из stderr на экран. Мне не ясно, как я могу использовать это для выполнения конкретных команд, например, убить HandBrakeCLI ; echo $ITEM >> err.log
Я пробовал использовать grep -q, чтобы проверить на успешное совпадение вот так:
echo "" | HandBrakeCLI -i "$ITEM" -o "$OUTPUT" --preset="$PRESET" $OPTS 3>&2 2>&1 1>&3- | grep -q "error" && echo "ошибка найдена" ; echo $ITEM >> failed.txt
Но скрипт просто завершается при обнаружении ошибки, не переходя к следующему элементу в цикле.
Любая помощь приветствуется!
обновлено для ответа на вопрос тердона:
Мне также было любопытно о первоначальном echo “” | в вызове HandBrake. Это было в оригинальном скрипте, упомянутом в начале запроса, но мне не ясно, зачем это нужно. Я запускаю это на MacOS. Полный скрипт:
#!/bin/bash
SOURCE=$1
ENCODER=$2
OPTS=$3
TOTAL=$(find "$SOURCE" \! -name ".*" -type f | wc -l)
CURR=0
DESTINATION="PROCESSED"
ENCODER=${ENCODER:="hw"}
# Создать директории
[[ -d $DESTINATION ]] || mkdir -p $DESTINATION
while IFS= read -d '' -r ITEM
do
RES=`ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of default=nw=1 "$ITEM" | grep 'height*' | cut -c 8-`
FILE=${ITEM##*/}
EXT=${ITEM##*.}
EXT=$(echo $EXT | tr "[:upper:]" "[:lower:]")
OUTPUT="$DESTINATION/${FILE%.*}.$EXT"
# Получить правильный пресет
if [[ "$RES" == "2160" ]] && [[ "$ENCODER" == "hw" ]]
then
PRESET="H.265 Apple VideoToolbox 2160p 4K"
elif [[ "$RES" == "2160" ]] && [[ "$ENCODER" == "sw" ]]
then
PRESET="Fast 2160p60 4K HEVC"
elif [[ "$RES" != "2160" ]] && [[ "$ENCODER" == "hw" ]]
then
PRESET="H.265 Apple VideoToolbox 1080p"
elif [[ "$RES" != "2160" ]] && [[ "$ENCODER" == "sw" ]]
then
PRESET="Fast 1080p30"
fi
echo "Обработка" $FILE "с использованием пресета" $PRESET
echo "" | HandBrakeCLI -i "$ITEM" -o "$OUTPUT" --preset="$PRESET" $OPTS 2> /dev/null
CURR=$((CURR + 1))
echo "готово" $CURR "из"$TOTAL
done< <(find "$SOURCE" \( -iname '*.mp4' -or -iname '*.mov' -or -iname '*.MP4' -or -iname '*.MOV' \) -print0)
Ответ или решение
Условное выполнение команд на основе вывода grep в shell-скрипте
Как IT-эксперт, я понимаю вашу задачу и необходимость в создании скрипта для обработки фильмов с использованием HandBrakeCLI. Учитывая ваш запрос, давайте рассмотрим, как можно организовать условное выполнение команд в зависимости от вывода grep
, чтобы ваша прога могла корректно обрабатывать ошибки, возникающие при кодировании.
Цели
- Мониторинг стандартного вывода (stdout): Отображение информации на экране.
- Мониторинг ошибок (stderr): Поиск специфической ошибки.
- Условное выполнение после ошибки: Завершение процесса, ведение логов и переход к следующему файлу.
Решение
Чтобы реализовать поставленные задачи, нам нужно использовать grep
для мониторинга выхода стандартного потока ошибок и организовать условную логику для обработки ошибок. Вот как вы можете сделать это в вашем скрипте:
#!/bin/bash
SOURCE=$1
ENCODER=$2
OPTS=$3
TOTAL=$(find "$SOURCE" \! -name ".*" -type f | wc -l)
CURR=0
DESTINATION="PROCESSED"
ENCODER=${ENCODER:="hw"}
# Создание директории
[[ -d $DESTINATION ]] || mkdir -p $DESTINATION
while IFS= read -d '' -r ITEM
do
RES=$(ffprobe -v error -select_streams v:0 -show_entries stream=width,height -of default=nw=1 "$ITEM" | grep 'height*' | cut -c 8-)
FILE=${ITEM##*/}
EXT=${ITEM##*.}
EXT=$(echo $EXT | tr "[:upper:]" "[:lower:]")
OUTPUT="$DESTINATION/${FILE%.*}.$EXT"
# Определение пресета
if [[ "$RES" == "2160" ]] && [[ "$ENCODER" == "hw" ]]; then
PRESET="H.265 Apple VideoToolbox 2160p 4K"
elif [[ "$RES" == "2160" ]] && [[ "$ENCODER" == "sw" ]]; then
PRESET="Fast 2160p60 4K HEVC"
elif [[ "$RES" != "2160" ]] && [[ "$ENCODER" == "hw" ]]; then
PRESET="H.265 Apple VideoToolbox 1080p"
elif [[ "$RES" != "2160" ]] && [[ "$ENCODER" == "sw" ]]; then
PRESET="Fast 1080p30"
fi
echo "Обработка" $FILE "с использованием пресета" $PRESET
# Запуск HandBrakeCLI и мониторинг stderr
{
echo "" | HandBrakeCLI -i "$ITEM" -o "$OUTPUT" --preset="$PRESET" $OPTS 2>&1 | grep -i "error" > /dev/null
} || {
echo "Ошибка при обработке $ITEM" >> err.log
echo "Текущий файл был пропущен из-за ошибки."
continue
}
CURR=$((CURR + 1))
echo "Завершено" $CURR "из" $TOTAL
done < <(find "$SOURCE" \( -iname '*.mp4' -or -iname '*.mov' -or -iname '*.MP4' -or -iname '*.MOV' \) -print0)
Объяснение решения
-
Обработка вывода: Мы перенаправляем stderr в stdout (
2>&1
) и затем используемgrep
для фильтрации ошибок. Обратите внимание на> /dev/null
, который скрывает выводgrep
. -
Условное выполнение: Использование конструкции
||
позволяет выполнить команду в случае возникновения ошибки. Если команда завершилась неуспешно (мы нашли "error"), скрипт производит логирование, продолжает выполнение и переходит к следующему входному файлу. -
Логирование: В случае возникновения ошибки мы записываем информацию в лог-файл
err.log
и выводим сообщение, чтобы проинформировать пользователя об этом.
Остальные аспекты
-
Echo "" | в вызове HandBrakeCLI в вашем исходном скрипте может быть необходимо, если HandBrakeCLI ожидает ввод, связанный с интерфейсом. Если HandBrakeCLI может работать без этого, вы можете убрать этот фрагмент.
-
Проверка условий: Убедитесь, что
ffprobe
корректно определяет разрешение видео; это поможет в правильном назначении пресетов.
Данный алгоритм обеспечит вам необходимую функциональность для стабильного выполнения вашей задачи. Если у вас возникнут дополнительные вопросы или потребуется помощь, не стесняйтесь обращаться!