Перемістіть файл, але лише якщо він закритий


10

Я хочу перемістити великий файл, створений зовнішнім процесом, як тільки він закриється.

Чи правильна ця тестова команда?

if lsof "/file/name"
then
        # file is open, don't touch it!
else
        if [ 1 -eq $? ]
        then
                # file is closed
                mv /file/name /other/file/name
        else
                # lsof failed for some other reason
        fi
fi

EDIT: файл представляє набір даних, і мені доведеться почекати, поки він завершиться, щоб перемістити його, щоб на нього могла діяти інша програма. Ось чому мені потрібно знати, чи виконується зовнішній процес з файлом.


3
Бічна примітка: щойно файл відкривається, процеси використовують дескриптори файлів і дані inode для маніпулювання ним. Зміна шляху (тобто переміщення файлу) не спричинить занадто багато проблем.
Джон У. Сміт

2
Чи є у вас контроль над зовнішнім процесом? Чи можливо, щоб зовнішній процес створив тимчасовий файл і перейменував його після того, як він закінчить писати до нього?
Jenny D

@JennyD Я провів деяке розслідування, і це виявляється правдою. Мені це зовсім не потрібно, lsofмені просто потрібно перевірити, чи не розширення файлу .tmp. Це робить його банальним. Однак я радий, що я поставив своє запитання, оскільки я трохи дізнався про lsofі inotifyта інше.
Петро Ковач

@PeterKovac Я дізнався більше про них, прочитавши відповіді, тому я дуже радий, що ви це запитали.
Дженні Д

@JohnWHSmith - Це звичайно вірно, якщо перемістити файл в одній і тій же файловій системі, якщо він перемістить файл до нової файлової системи до того, як автор закінчить писати до нього, він втратить деякі дані.
Джонні

Відповіді:


11

З lsofчоловічої сторінки

Lsof повертає один (1), якщо виявлено будь-яку помилку, у тому числі невдало знайти імена команд, імена файлів, інтернет-адреси чи файли, імена входу, файли NFS, PID, PGID або UID, про які було запропоновано перерахувати. Якщо параметр -V заданий, lsof вкаже пошукові елементи, які не вдалося внести до списку.

Тож це дозволило б припустити, що ваш lsof failed for some other reasonпункт ніколи не буде виконаний.

Ви намагалися просто перемістити файл, поки ваш зовнішній процес все ще відкритий? Якщо каталог призначення знаходиться в одній і тій же файловій системі, то з цим не повинно виникнути жодних проблем, якщо тільки вам не потрібно буде отримати доступ до нього під початковим шляхом з третього процесу, оскільки базовий inode залишиться тим самим. Інакше я думаю mv, що все-таки провалиться.

Якщо вам дійсно потрібно дочекатися, коли ваш зовнішній процес закінчиться файлом, краще використовувати команду, яка блокує замість багаторазового опитування. У Linux ви можете використовувати inotifywaitдля цього. Наприклад:

 inotifywait -e close_write /path/to/file

Якщо потрібно використовувати lsof(можливо, для портативності), ви можете спробувати щось на кшталт:

until err_str=$(lsof /path/to/file 2>&1 >/dev/null); do
  if [ -n "$err_str" ]; then
    # lsof printed an error string, file may or may not be open
    echo "lsof: $err_str" >&2

    # tricky to decide what to do here, you may want to retry a number of times,
    # but for this example just break
    break
  fi

  # lsof returned 1 but didn't print an error string, assume the file is open
  sleep 1
done

if [ -z "$err_str" ]; then
  # file has been closed, move it
  mv /path/to/file /destination/path
fi

Оновлення

Як зазначає @JohnWHSmith нижче, найбезпечніший дизайн завжди використовує lsofцикл, як зазначено вище, можливо, що більш ніж один процес матиме файл, відкритий для запису (приклад може бути погано записаним демоном індексації, який відкриває файли з прочитаним / пишіть прапор, коли його дійсно слід читати лише). inotifywaitвсе ще можна використовувати замість сну, хоча просто замініть спальну лінію inotifywait -e close /path/to/file.


Спасибі, я не знав про це inotify. На жаль, він не встановлений у моїй коробці, але я впевнений, що десь знайду пакунок. Перегляньте мою редакцію з причини, чому мені потрібно закрити файл: це набір даних, і він повинен бути завершеним, перш ніж його далі обробляти.
Петро Ковач

1
Ще одна сторона зауваження: в той час inotifywaitяк сценарій заважатиме "опитувати" двічі часто, ОП все ще потрібно перевіряти lsofв циклі: якщо файл відкривається двічі, закриття одного разу може викликати inotifyподію, навіть якщо файл не готовий бути маніпулюється (наприклад, в останньому фрагменті коду ваш sleepдзвінок можна буде замінити inotifywait).
Джон У. Сміт

@John a close_writeмає бути нормальним, оскільки лише один процес може одночасно відкривати файл для запису. Він припускає, що інший не відкриє його відразу після його закриття, але тоді те саме питання існує і при lsofопитуванні.
Graeme

1
@Graeme Хоча це може бути істинним за дизайном у випадку ОП, ядро ​​дозволяє відкрити файл для запису двічі (у такому випадку CLOSE_WRITEвін запускається двічі).
Джон У. Сміт

@John, оновлено.
Graeme

4

Як альтернативний підхід, це ідеальний випадок для труби - другий процес обробляє вихід з першого процесу, як тільки він буде доступний, а не чекає завершення повного процесу:

process1 input_file.dat | process2 > output_file.dat

Переваги:

  • Набагато швидше загалом:
    • Не потрібно писати та читати з диска (цього можна уникнути, якщо ви користуєтеся ramdisk).
    • Слід використовувати машинні ресурси більш повно.
  • Немає проміжного файлу для видалення після закінчення.
  • Не потрібно комплексної блокування, як в ОП.

Якщо у вас немає можливості безпосередньо створити трубу, але у вас є GNU coreutils, ви можете скористатися цим:

tail -F -n +0 input_file.dat | process2 > output_file.dat

Це почне читати вхідний файл з самого початку, незалежно від того, наскільки перший процес проходить через запис файлу (навіть якщо він ще не запущений або вже закінчений).


Так, це було б "очевидним" рішенням. На жаль, процес генерації даних поза моїм контролем (керується іншим користувачем).
Петро Ковач

@PeterKovac Це не має значення: cat input_file.dat | process2 output_file.dat
MariusMatutiae

@MariusMatutiae , але catі process2може завершитися до process1завершення. Вони не заблокують.
cpugeniusmv
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.