Синхронизировать выполнение скрипта командной строки

StackOverflow https://stackoverflow.com/questions/1474052

Вопрос

Модифицированная версия сценарий оболочки преобразует аудиофайл из формата 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 ....
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top