Синхронизировать выполнение сценария оболочки
Модифицированная версия сценария оболочки преобразует аудиофайл из 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;
Тем не менее, это все еще оставляет условие гонки.
8 ответов
Команда "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
В качестве альтернативы, вы можете сделать так, чтобы lockfile продолжал повторяться до бесконечности, поэтому вам не нужно проверять код возврата, и вместо этого вы можете проверить выходной файл, чтобы определить, следует ли запускать 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.
Перед началом кодирования проверьте наличие файла.lock и при необходимости убедитесь, что дата файла блокировки не слишком старая (в случае, если процесс умирает). Если он не существует, создайте его до начала кодирования и удалите его после завершения кодирования.
Вы также можете скопировать файл, но это действительно работает только в c, где вы вызываете flock() и записываете в файл, затем закрываете и разблокируете. Для сценария оболочки вы, вероятно, вызываете другую утилиту для написания файла.
В bash можно установить опцию noclobber, чтобы избежать перезаписи файла.
набор помощи | egrep 'noclobber|-C'
Как насчет написания Makefile?
ALL_FLAC=$(wildcard *.flac)
ALL_MP3=$(patsubst %.flac, %.mp3, $(ALL_FLAC)
all: $(ALL_MP3)
%.mp3: %.flac
$(FLAC) ...
Тогда делай
$ make -j4 all
Используйте такой инструмент, как FLOM (Free LOck Manager) и просто сериализуйте вашу команду, как показано ниже:
flom -- flac ....