Выполнение атомарных операций записи в файл в bash

13

После прохождения Баша документации , этот вопрос , и это один он все еще не ясно мне , как я могу выполнить атомную запись (добавление) операции в файл в Баше. У меня есть скрипт, который запускается в нескольких экземплярах и в какой-то момент должен записать данные в файл:

echo "$RESULT" >> `pwd`/$TEMP_DIR/$OUT_FILE

Как можно сделать все операции записи из всех одновременно работающих сценариев в этот файл атомарными (чтобы данные из одного экземпляра не перекрывали данные из другого)?

Sebi
источник
1
К вашему сведению, вам не нужно `pwd`; Вы можете просто использовать точку ( .). Также вы должны заключить это имя в кавычки, так как оно содержит переменные .
Wildcard
1
Вы также можете рассмотреть возможность использования FIFO .
Wildcard
@ Wildcard Спасибо. Я использовал pwdранее в сценарии, чтобы уведомить пользователя о текущем рабочем каталоге, а также записывать записи в файл журнала. просматривая FIFO сейчас.
Себи,
1
На самом деле , лучше познакомиться с FIFO прямо на этом сайте; ссылка, которую я предоставил несколько минут назад, - это спецификации POSIX, mkfifoа не вводный уровень.
Wildcard
Будь то FIFO или файл, вы все равно рискуете, что два экземпляра пишут одновременно и искажают результаты друг друга.
Клак

Ответы:

10

Кажется, вам нужно использовать flockкак в примере с человеком ( http://linux.die.net/man/1/flock )

(
flock -x 200

# Put here your commands that must do some writes atomically

) 200>/var/lock/mylockfile 

И поместите все свои команды, которые должны быть атомарными, в ().


источник
Операция записи будет атомарной, хотя (flock -s 200 # Поместите здесь ваши команды, которые должны делать некоторые записи атомарно) 200> / var / lock / mylockfile распределяется между несколькими одновременно выполняемыми сценариями? Или, наоборот, блокировка только относительно / var / lock / mylockfile?
Себи,
1
Ваш скрипт блокирует / var / lock / mylockfile, но только один экземпляр вашего скрипта может получить блокировку записи на него. Другие экземпляры будут ждать, пока не /var/lock/mylockfileстанет доступным для блокировки.
-s это общая блокировка - подходит для чтения, для блокировки записи вы должны использовать -x / -e. Это на странице руководства, на которую вы ссылались.
Эван Бенн
В какой момент происходит разблокировка в этом коде? Как только что-то будет записано в /var/lock/mylockfileпроцессе, который вызвал flock -x?
Крис Стрычински,
@ChrisStryczynski Вы можете запустить этот пример (flock -x 200; date; sleep 10; date;) 200>/var/lock/mylockfileв двух оболочках и увидеть, что разблокировки происходят только после выполнения всех команд в () (то есть в подоболочке)
4

flockэто один из способов блокировки операций. Утилита является частью набора инструментов util-linux и доступна только для Linux. Другие утилиты, доступные на более широком спектре платформ, основаны на setlockутилите Дэниела Бернштейна из его пакета daemontools:

  • setlock из daemontools
  • setlock от демона-бруса Брюса Гюнтера
  • s6-setlock от s6 Лорана Берко
  • chpst из рунита Геррита Папе
  • runlock из преступника Уэйна Маршалла
  • setlock из моего набора инструментов Nosh

Эти инструменты работают с несколько иной парадигмой, чем в ответе М. Куренкова (тот, который flockтакже может быть использован, но не в этом ответе). Один запускает setlockпрограмму, чтобы связать нагрузку с командой, которая должна быть заблокирована. setlockсам открывает и блокирует файл блокировки и оставляет файловый дескриптор для его открытия в процессе. Блокировка сохраняется до тех пор, пока этот процесс работает (если только последующая команда, связанная цепочкой для явного снятия блокировки, путем поиска и закрытия дескриптора открытого файла).

Для рассматриваемого случая необходимо заблокировать команду, которая создает строку вывода, зная, что это вызывает внешнюю команду echoвместо встроенной echoкоманды оболочки :

setlock mylockfile echo "$ RESULT" >> ./$TEMP_DIR/$OUT_FILE

В этом случае нет необходимости блокировать открытие выходного файла в режиме добавления. Если бы это было так, нужно было бы открыть этот файл в замке, что требует использования таких программ, как fdredir/ redirfd:

setlock mylockfile fdredir --append 1 "./$TEMP_DIR/$OUT_FILE" echo "$ RESULT"
которую можно превратить в функцию оболочки, если хотите:

outfile () {setlock mylockfile fdredir --append 1 "./$TEMP_DIR/$OUT_FILE" "$ @"; } 
[…]
Outfile echo "$ RESULT"
или придерживаясь синтаксиса оболочки и интерпретируя его второй оболочкой, работающей под блокировкой, требуя некоторого нетривиального цитирования, если переменные оболочки не экспортируются как переменные среды:

setlock mylockfile sh -c 'echo' "$ RESULT" '>> "./'$TEMP_DIR'/'$OUT_FILE'" '

Это, конечно, обобщает вещи, отличные от записи в выходные файлы:

setlock mylockfile sh -c '... заблокирован; прочее ...

JdeBP
источник