Синхронизировать выполнение скрипта командной строки
-
16-09-2019 - |
Вопрос
Модифицированная версия сценарий оболочки преобразует аудиофайл из формата FLAC в формат MP3.Компьютер оснащен четырехъядерным процессором.Скрипт запускается с помощью:
./flac2mp3.sh $(find flac -type f)
Это преобразует файлы FLAC в flac
каталог (без пробелов в именах файлов) для файлов MP3 в mp3
каталог (на том же уровне, что и flac
).Если целевой MP3-файл уже существует, скрипт пропускает этот файл.
Проблема в том, что иногда два экземпляра скрипта проверяют наличие одного и того же MP3-файла почти одновременно, что приводит к искажению MP3-файлов.
Как бы вы запустили скрипт несколько раз (т. е. один раз для каждого ядра), без необходимости указывать другой набор файлов в каждой командной строке и без перезаписи работы?
Обновление - Минимальное условие гонки
Скрипт использует следующий механизм блокировки:
# Convert FLAC to MP3 using tags from flac file.
#
if [ ! -e $FLAC.lock ]; then
touch $FLAC.lock
flac -dc "$FLAC" | lame${lame_opts} \
--tt "$TITLE" \
--tn "$TRACKNUMBER" \
--tg "$GENRE" \
--ty "$DATE" \
--ta "$ARTIST" \
--tl "$ALBUM" \
--add-id3v2 \
- "$MP3"
rm $FLAC.lock
fi;
Тем не менее, это все еще оставляет состояние гонки.
Решение
Команда «lockfile» обеспечивает то, что вы пытаетесь сделать для сценариев оболочки без состояния гонки.Команда была написана людьми из procmail специально для подобных целей и доступна в большинстве систем BSD/Linux (поскольку procmail доступен для большинства сред).
Ваш тест будет выглядеть примерно так:
lockfile -r 3 $FLAC.lock
if test $? -eq 0 ; then
flac -dc "$FLAC" | lame${lame_opts} \
--tt "$TITLE" \
--tn "$TRACKNUMBER" \
--tg "$GENRE" \
--ty "$DATE" \
--ta "$ARTIST" \
--tl "$ALBUM" \
--add-id3v2 \
- "$MP3"
fi
rm -f $FLAC.lock
В качестве альтернативы вы можете заставить файл блокировки повторять попытки бесконечно, чтобы вам не нужно было проверять код возврата, а вместо этого вы могли проверить выходной файл, чтобы определить, запускать ли flac.
Другие советы
Если у вас нет lockfile
и не могу его установить (ни в одной из его версий - существует несколько реализаций), существует надежный и переносимый атомный мьютекс. mkdir
.
Если каталог, который вы пытаетесь создать, уже существует, mkdir
потерпит неудачу, так что вы можете это проверить;Когда создание завершается успешно, у вас есть гарантия, что ни один другой взаимодействующий процесс не находится в критической секции одновременно с вашим кодом.
if mkdir "$FLAC.lockdir"; then
# you now have the exclusive lock
: critical section
: code goes here
rmdir "$FLAC.lockdir"
else
: nothing? to skip this file
# or maybe sleep 1 and loop back and try again
fi
Для полноты картины, возможно, также поискать flock
если вы используете набор платформ, где это надежно доступно, и вам нужна производительная альтернатива lockfile
.
Вы можете реализовать блокировку файлов FLAC, над которыми он работает.Что-то вроде:
if (not flac locked)
lock flac
do work
else
continue to next flac
Отправьте выходные данные во временный файл с уникальным именем, затем переименуйте файл в нужное имя.
flac -dc "$FLAC" | lame${lame_opts} \
--tt "$TITLE" \
--tn "$TRACKNUMBER" \
--tg "$GENRE" \
--ty "$DATE" \
--ta "$ARTIST" \
--tl "$ALBUM" \
--add-id3v2 \
- "$MP3.$$"
mv "$MP3.$$" "$MP3"
Если состояние гонки время от времени просачивается через вашу систему блокировки файлов, конечный результат все равно будет результатом одного процесса.
Чтобы заблокировать файловый процесс, вы можете создать файл с тем же именем и расширением .lock.
Перед началом кодирования проверьте наличие файла блокировки и, при необходимости, убедитесь, что дата файла блокировки не слишком старая (на случай, если процесс завершится).Если он не существует, создайте его до начала кодирования и удалите после завершения кодирования.
Вы также можете заблокировать файл, но это действительно работает только в c, где вы вызываете flock() и записываете в файл, а затем закрываете и разблокируете.Для сценария оболочки вы, вероятно, вызываете другую утилиту для записи файла.
Как насчет написания Makefile?
ALL_FLAC=$(wildcard *.flac)
ALL_MP3=$(patsubst %.flac, %.mp3, $(ALL_FLAC)
all: $(ALL_MP3)
%.mp3: %.flac
$(FLAC) ...
Тогда делай
$ make -j4 all
В bash можно установить опцию noclobber, чтобы избежать перезаписи файла.
набор справочных данных | egrep 'noclobber|-C'
Используйте такой инструмент, как FLOM (бесплатный менеджер блокировок) и просто сериализуйте свою команду, как показано ниже:
flom -- flac ....