Видалення створених тимчасових файлів у несподіваному виході з bash


89

Я створюю тимчасові файли зі сценарію bash. Я видаляю їх наприкінці обробки, але оскільки сценарій працює досить довго, якщо я його вб'ю або просто CTRL-C під час запуску, тимчасові файли не видаляються.
Чи можна зафіксувати ці події та очистити файли до закінчення виконання?

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

TMP1=`mktemp -p /tmp`
TMP2=`mktemp -p /tmp`
...

і

TMP1=/tmp/`basename $0`1.$$
TMP2=/tmp/`basename $0`2.$$
...

А може, є якісь кращі рішення?


Відповіді:


97

Ви можете встановити " пастку " для запуску при виході або на control-c для очищення.

trap "{ rm -f $LOCKFILE; }" EXIT

Крім того, один з моїх улюблених unix-isms - це відкрити файл, а потім видалити його, поки ви все ще відкритий. Файл залишається у файловій системі, і ви можете читати та писати його, але як тільки програма закривається, файл зникає. Не впевнений, як ви це зробите в bash.

BTW: Один аргумент, який я наведу на користь mktemp замість використання власного рішення: якщо користувач передбачає, що ваша програма збирається створити величезні тимчасові файли, він, можливо, захоче встановити TMPDIRдесь більше, наприклад / var / tmp. mktemp визнає, що ваш рулонний розчин (другий варіант) цього не робить. TMPDIR=/var/tmp gvim -d foo barНаприклад, я часто використовую .


8
З Bash, exec 5<>$TMPFILEпов'язує дескриптор файлу 5 до $ TMPFILE читання і запису, і ви можете використовувати <&5, >&5і /proc/$$/fd/5(Linux) в подальшому. Єдина проблема полягає в тому, що Башу бракує seekфункції ...
ефемієнт

Ви прийняли вашу відповідь, оскільки надане вами посилання пояснює найкраще, що мені потрібно. Дякую
скин

4
Пара приміток щодо trap: немає можливості затримати SIGKILL(за задумом, оскільки це негайно припиняє виконання). Отже, якщо це може статися, майте запасний план (наприклад, tmpreaper). По-друге, пастки не накопичувальні - якщо вам потрібно виконати більше однієї дії, всі вони повинні бути в trapкоманді. Один з способів впоратися з декількома діями очищення полягає у визначенні функції (і ви можете перевизначити його як ваша програма триває, якщо це необхідно) і посилання , що: trap cleanup_function EXIT.
Тобі Спейт

1
Мені довелося скористатися, trap "rm -f $LOCKFILE" EXITінакше я отримав би несподівану помилку кінця файлу.
Яакко

3
Shellcheck дав попередження використовувати одинарні лапки, що вираз буде розширено "зараз" подвійними лапками, а не пізніше, коли буде викликано пастку.
LaFayette

110

Зазвичай я створюю каталог, в який можна розмістити всі мої тимчасові файли, а потім відразу після цього створюю обробник EXIT для очищення цього каталогу при виході сценарію.

MYTMPDIR=$(mktemp -d)
trap "rm -rf $MYTMPDIR" EXIT

Якщо ви покладете всі свої тимчасові файли $MYTMPDIR, тоді всі вони будуть видалені, коли ваш сценарій вийде у більшості обставин. Вбивство процесу за допомогою SIGKILL (kill -9) вбиває процес одразу, тому ваш обробник EXIT у такому випадку не працюватиме.


27
+1 Визначально використовуйте пастку на EXIT, а не безглуздий TERM / INT / HUP / будь-що інше, що ви можете придумати. Хоча, НЕ забудьте процитувати ваші розкладання параметрів , і я також рекомендую вам єдину цитату вашої пастки: пастка «ет -rf" $ TMPDIR "» EXIT
lhunath

7
Одинарні лапки, тому що тоді ваша пастка все одно буде працювати, якщо пізніше у вашому сценарії ви вирішите очистити та змінити TMPDIR через обставини.
lhunath

1
@AaronDigulla Чому $ () проти зворотних посилань важливий?
Ogre Psalm33


3
Код @AlexanderTorstling завжди повинен бути в лапках, щоб запобігти введенню, що призведе до довільного виконання коду. Якщо ви розширите дані в баш-код STRING, то ці дані тепер можуть робити все, що робить код, що призводить до невинних помилок з пробілами, а також до деструктивних помилок, таких як очищення вашого homedir з химерних причин або введення дірок у безпеці. Зверніть увагу, що trap приймає рядок bash-коду, який згодом буде оцінений як є. Тож пізніше, коли пастка спрацює, одинарні лапки зникнуть, а будуть лише синтаксичні подвійні лапки.
Lhunath

25

Ви хочете скористатися пасткою для обробки виходу з сценарію або сигналів, таких як CTRL-C. Детальніше див. У Вікі Грега .

Для ваших Tempfiles використання basename $0- це гарна ідея, а також надання шаблону, який надає місце для достатньої кількості тимчасових файлів:

tempfile() {
    tempprefix=$(basename "$0")
    mktemp /tmp/${tempprefix}.XXXXXX
}

TMP1=$(tempfile)
TMP2=$(tempfile)

trap 'rm -f $TMP1 $TMP2' EXIT

1
Не захоплюйте TERM / INT. Пастка на EXIT. Спроба передбачити умову виходу на основі отриманих сигналів нерозумна і, безумовно, не є зачарованою.
lhunath

3
Мінор: Використовуйте $ () замість одинарних зворотних посилань. І поставте подвійні лапки близько $ 0, оскільки вони можуть містити пробіли.
Аарон Дігулла

Ну, зворотні посилання добре працюють у цьому коментарі, але це справедливий момент, добре мати звичку використовувати $(). Також додані подвійні лапки.
Брайан Кемпбелл

1
Ви можете замінити всю свою підпрограму лише TMP1 = $ (tempfile -s "XXXXXX")
Руслан Кабалін,

4
@RuslanKabalin Не всі системи мають tempfileкоманду, тоді як усі розумні сучасні системи, про які я знаю, мають mktempкоманду.
Брайан Кемпбелл,

9

Тільки майте на увазі, що обрана відповідь є bashism, що означає рішення як

trap "{ rm -f $LOCKFILE }" EXIT

буде працювати тільки в bash (він не буде ловити Ctrl + c, якщо оболонка dashабо класичнаsh ), але якщо ви хочете сумісність, вам все одно потрібно перерахувати всі сигнали, які ви хочете затримати.

Також майте на увазі, що коли скрипт виходить із пастки для сигналу "0" (він же EXIT), завжди виконується, що призводить до подвійного виконання trap команди.

Це причина не складати всі сигнали в один рядок, якщо є сигнал EXIT.

Щоб краще зрозуміти його, подивіться на наступний сценарій, який буде працювати в різних системах без змін:

#!/bin/sh

on_exit() {
  echo 'Cleaning up...(remove tmp files, etc)'
}

on_preExit() {
  echo
  echo 'Exiting...' # Runs just before actual exit,
                    # shell will execute EXIT(0) after finishing this function
                    # that we hook also in on_exit function
  exit 2
}


trap on_exit EXIT                           # EXIT = 0
trap on_preExit HUP INT QUIT TERM STOP PWR  # 1 2 3 15 30


sleep 3 # some actual code...

exit 

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


4

Альтернативою використання передбачуваного імені файлу за допомогою $$ є прогалина в безпеці, і ви ніколи, ніколи не повинні думати про його використання. Навіть якщо це просто простий особистий скрипт на вашому однокористувацькому ПК. Це дуже погана звичка, якої ви не повинні отримувати. BugTraq сповнений випадків "небезпечного тимчасового файлу". Дивіться тут , тут і тут для отримання додаткової інформації щодо аспекту безпеки тимчасових файлів.

Спочатку я думав процитувати небезпечні завдання TMP1 та TMP2, але, задумавшись, це, мабуть, було б не найкращою ідеєю .


Я дав би, якби міг: +1 за пораду з питань безпеки та ще +1 за те, що не цитував погану ідею та посилання
TMG

1

Я віддаю перевагу використанню, tempfileяке створює файл у / tmp безпечним способом, і вам не доведеться турбуватися про його іменування:

tmp=$(tempfile -s "your_sufix")
trap "rm -f '$tmp'" exit

tempfile, на жаль, дуже не портативний, хоча і безпечніший, тому часто краще уникати його або принаймні наслідувати.
lericson

1

Я не можу повірити, що так багато людей припускають, що ім'я файлу не міститиме пробілу. Світ зазнає краху, якщо $ TMPDIR коли-небудь буде призначено "тимчасовому каталогу".

zTemp=$(mktemp --tmpdir "$(basename "$0")-XXX.ps")
trap "rm -f ${zTemp@Q}" EXIT

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


+1 Хоча одинарні лапки trap 'rm -f "${zTemp}"' EXITправильно обробляють пробіли та інші спеціальні символи, рішення цієї відповіді не відкладає оцінку zTemp. Тому немає необхідності турбуватися про значення того, що zTempбуде змінено пізніше у сценарії. Крім того, zTempможе бути оголошений локальним для функції; це не повинно бути глобальною змінною сценарію.
Робін А. Мід,

Подвійні лапки навколо RHS завдання не потрібні.
Робін А. Мід,

Слід зазначити, що ${parameter@operator}розширення були додані в Bash 4.4 (випущено вересень 2016 р.).
Робін А. Мід,

-4

Вам не потрібно турбуватися про видалення цих файлів tmp, створених за допомогою mktemp. Вони будуть видалені пізніше.

Використовуйте mktemp, якщо можете, оскільки він генерує більше унікальних файлів, ніж префікс '$$'. І це виглядає як більш крос-платформенний спосіб створення тимчасових файлів, а потім явно помістити їх у / tmp.


4
Видалено ким чи чим?
innaM

Видалено операцією | сама файлова система через певний проміжок часу
Микола Голуб'єв

4
Магія? Cronjob? Або перезавантажена машина Solaris?
innaM

Ймовірно, один із них. Якщо тимчасовий файл не було видалено деяким перериванням (це буде не надто часто), колись tmp-файли буде видалено - ось чому вони назвали temp.
Микола Голуб’єв

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