Не удается создать временный файл для блока here document

Обновлено: 04.07.2024

Когда PHP-программисту необходимо создать временный файл, он в мануале находит функцию tmpfile() и после изучения примеров начинает думать, как её лучше применить. Так было и со мной, когда мне потребовалось выгрузить данные сразу во временный файл, а не работать с ними через переменную. Но с файлом, созданным таким образом, в дальнейшем неудобно работать в силу того, что tmpfile() возвращает дескриптор, а не ссылку на локальный файл. Давайте немного углубимся в анатомию временного файла и рассмотрим подводные камни, с которыми мне пришлось столкнуться.

Временные файлы в PHP нужны, например, для загрузки большого количества данных из сети или выгрузки данных из БД. Сохранять мега- или гигабайты дынных в переменной не самая лучшая идея, поскольку потребление памяти интерпретатором и сервером ограничено. Лучшим вариантом является сохранение данных на диске и удаление их по результату обработки. Функция tmpfile() именно так и работает, но в PHP существуют и другие нативные способы, с помощью которых можно организовать работу с временными данными:

Временные данные, созданные через fopen() или tmpfile() , будут уничтожены самим PHP после завершения скрипта или когда вы принудительно закроете ресурс с помощью функции fclose() . Если будет брошено исключение, PHP всё равно уничтожит временные данные, но если вылетит фатальная ошибка или вы просто вызовете exit() , нет гарантий, что временные данные будут удалены.

Получаем URI временного файла

У вас может появиться необходимость «придержать» временные данные и поработать с ними на уровне физических файлов. Этого можно достичь, если открыть поток php://temp/maxmemory:0 или вызвать tmpfile() , а затем, до закрытия ресурса, воспользоваться stream_get_meta_data() , чтобы извлечь метаданные и узнать абсолютный путь к файлу для дальнейших манипуляций:

Какие значения возвращает stream_get_meta_data() хорошо описано в документации, но нас больше интересует абсолютный путь к файлу, связанный с потоком. Его можно извлечь по ключу uri из полученного массива:

В случае с php://temp/maxmemory:0 мы не сможем получить URI файла из метаданных потока, но фактически файл будет создан во временной папке. Функция stream_get_meta_data() по ключу uri будет возвращать название потока. Поэтому для получения URI временного файла на диске, нужно использовать функцию tmpfile() . Другого способа узнать, где физически хранится временный файл, не существует.

Существует класс SplTempFileObject, который является всего лишь ООП-обёрткой на потоком php://temp и не более. Данный класс наследуется от SplFileObject, а тот в свою очередь от SplFileInfo. Это значит, что у SplTempFileObject должны быть доступны такие методы, как getFilename() , getPathInfo() , getSize() , но они не отработают так, как ожидается. Начиная с версии 5.3 закрался баг, который возвращает false для вышеперечисленных методов. В версии 7.4 ничего не изменилось.

Проблемы функции tmpfile()

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

К сожалению, сохранить временный файл через rename() не получится, т. к. tmpfile() для Windows накладывает блокирующий режим и файл будет всё ещё занят другим процессом. В UNIX-системах такой проблемы нет, здесь временный файл можно удалять ещё до закрытия ресурса. Другими словами, для Windows вызов rename() до fclose() приведёт к ошибке:

Использовать rename() после fclose() не целесообразно, т. к. после закрытия ресурса PHP сразу же удалит временный файл. Тем не менее, мы можем сохранить временный файл через копирование, воспользовавшись функцией copy() . Этот способ рабочий, но при копировании временного файла на сервере будет храниться две копии файла до завершения скрипта.

Создание временного файла

Для альтернативного решения создадим свой временный файл, который будет базироваться на функции tempnam() . Функция только создаёт файл с уникальным именем на диске, но автоматическое удаление файла не предусмотрено. Поэтому для его уничтожения нам понадобится написать свой обработчик. Жизненный цикл временного файла будет следующий: 1) создание файла во временной папке; 2) манипуляции с файлом; 3) автоматическое удаление.

Первым аргументом указывается расположение временной папки через sys_get_temp_dir() , а вторым — префикс в имени файла. Такой файл доступен для чтения и записи только владельцу, т. к. создаётся с правами 0600 (rw-). Для реализации автоматического удаления файла можно перенести дальнейшую логику в класс, где с помощью __destruct() попробуем удалить файл:

Объект вернёт ссылку на файл, который создала функция tempnam() , т. к. в классе прописан __toString() . Таким образом, мы избавились от работы с ресурсом. Временный файл будет удалён при освобождении всех ссылок на объект или по завершению скрипта, но до того случая, пока не будет вызвана фатальная ошибка, брошено исключение или вызвана функция exit() .

Полагаться на __destruct() плохая идея, т. к. в языках с автоматическим управлением памятью, нельзя быть уверенным на 100% в том, когда он выполнится. Деструктор не должен оставлять объект в нестабильном состоянии, поэтому в PHP обработчики уничтожения и освобождения объекта отделены друг от друга. Обработчик освобождения вызывается, когда движок полностью уверен, что объект больше нигде не применяется.

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

Мы вынесли обработчик удаления временного файла в статическое замыкание, чтобы отвязать все ссылки от объекта и сохранить вызов __destruct до register_shutdown_function :

Класс TmpFile избегает ситуации, когда на него по умолчанию открыт дескриптор. Теперь можно использовать rename() вместо copy() и не бояться, когда при сохранении временных данных в другой файл на диске хранится две копии до завершения скрипта. Главное не держать открытый дескриптор, чтобы rename() наверняка отработала в Windows.

Также мы получили возможность типизировать временный файл и декларировать его тип через конструктор или методы классов, чтобы быть увереным, что приходит временный файл, а не строка, т. к. в PHP нет валидации на тип resource в сигнатурах.

Пример с загрузкой файлов

Бизнес-логика предполагала валидацию файла на другом уровне и здесь важно было позаботиться об удалении файла в самом начале его пути, если проверка будет провалена. С помощью функции register_shutdown_function() мы можем гарантировать, что временный файл будет удалён, когда скрипт завершится. В сниппете ниже приведён пример того, как был использован класс TmpFile вместо tmpfile() :

Временный файл в CLI и try-finally

В вебе запросы пользователей живут относительно недолго, но в CLI скрипты могут выполняться бесконечно и гарантировать выполнение функции register_shutdown_function() мы не можем. Скрипт может быть убит на системном уровне или выполнятся так долго, что все временные файлы останутся лежать без их финальной обработки. В консоле лучшим способом удаления временных файлов является использование конструкции try-finally :

Здесь процесс удаления временного файла мы помещаем в блок finally , который выполнится сразу после выхода из коллбека. В долгоживущих консольных приложениях это самый оптимальный способ обработать удаление временного файла, но при фатальных ошибках код до finally не дойдёт, а использование register_shutdown_function() не желательно, поэтому не остаётся ничего другого, как писать валидный код в секции try .

Чем класс TmpFile отличается от tmpfile()

Ниже привожу сравнительную таблицу между new TmpFile() и tmpfile() . Основные отличия заключаются в удалении временного файла: TmpFile удаляет файл по завершению скрипта или при освобождении всех ссылок на него, а tmpfile() — сразу после закрытия ресурса.

image

На основе идей из этой статьи, я написал менеджер для управления временными файлами, который доступен в репозитории denisyukphp/tmpfile-manager. Менеджер умеет много полезного: 1) настраивать путь к папке с временными файлами; 2) задавать префикс временным файлам; 3) закрывать отрытые ресурсы на временные файлы; 4) автоматически или вручную очищать временные файлы; 5) запускать свой сборщик мусора.

Вы можете использовать TmpFile независимо от менеджера, но TmpFileManager позволяет получить больше контроля над временными файлами и может гарантировать их удаление.

. и .. являются записи, которые обычно присутствуют в каждом каталоге. Их значение не связано с рабочим каталогом процесса (например, shell), но с каталогом, в котором находится запись.

.. обеспечивает двустороннюю привязку структуры дерева каталогов, а . - удобное имя для обращения к самому каталогу. Путь directory/. совпадает с directory. Теоретически пустую строку можно было бы выбрать для обозначения самой директории, но на самом деле это не так: ls '' не работает, а значение пустой строки было бы неоднозначным, поскольку в начале пути, к которому оно относится, корневой каталог уже: /file1 означает file1 в корневом каталоге или file1 в текущем рабочем каталоге?

Как показало thomasrutter, в качестве обычных записей в каталоге вы можете использовать . и .. на пути. Например, ./-filename можно использовать, чтобы избежать интерпретации символа тире - в качестве введения параметров командной строки. Путь directory1/../directory2 по сути такой же, как ./directory2, который совпадает с directory2.

Почему скрыты . и .

Файл ( и каталог) имена с . в начале по умолчанию скрыты в Unix-подобных системах, поэтому по умолчанию большинство инструментов не будут показывать каталоги . и . Это полезно, потому что мы уже знаем, что . и .. обычно присутствуют в каждом каталоге.

Команда ls -a показывает все записи в каталоге. В Nautilus Ctrl + H отображает скрытые записи, но за исключением . и . потому что они обычно не очень полезны в графическом диспетчере файлов. Для подобного поведения в командной строке вы можете использовать ls -A.

Реальные записи в . и .

Да, в часто используемой файловой системе они , (как напомнил Джонатан Леффлер). Как мы можем это проверить?

Номер inode (1-й столбец), ссылающийся на структуру данных самого каталога / файла, одинаковый для одного и того же каталога testdir1 и testdir1/.. Количество ссылок (3-й столбец), показывающее количество записей в каталоге, относящихся к inode (каталог / файл), равно 2 сразу после создания каталога, потому что в /tmp и . в /tmp/testdir1 есть testdir1. Индекс /tmp/testdir1/.. (/tmp) имеет 14 ссылок, потому что у него есть 12 подкаталогов, содержащих .. + 2 записи в качестве каждой директории.

Утилита debugfs читает ext2 ( и более новые) данные файловой системы непосредственно из дисковых секторов (обход файловой системы в ядре Linux).

Если вы не верите в список каталогов debugfs, вы можете изучить исходный дамп каталога и убедитесь, что записи . и .. действительно присутствуют.

Когда скрипты POSIX shell используют here documents , создается временный файл. Есть ли какой-нибудь способ экологически контролировать WHERE (путь) создания этих временных файлов?

В настоящее время я разрабатываю сценарий Ant, который запускает удаленную компиляцию POSIX operating system shell scripts on Linux, SunOS, HP-UX and AIX .Scripts C++ source code using GNU Autoconf/Automake . Сам сгенерированный сценарий configure использует несколько here documents . Если какой-либо из этих here documents не может создать файл в /var/tmp (на SunOS), цепная реакция неудачных задач сценария настройки приводит к отсутствию двоичных файлов из-за неудачных попыток компиляции кода C++. :-(

Так что я мог бы продолжать убирать /var/tmp . Но эти POSIX servers активно используются ночными, длительными заданиями, которые временно съедают /var/tmp пространства, а затем отказываются от него, спорадически вызывая No space left on device простоев. Очевидно, что некоторое системное администрирование могло бы облегчить эту проблему-возможно, увеличить раздел /var/tmp .

Но это не здесь и не там, на самом деле; я хочу удалить зависимость от другого файла systems/partitions entirely . Я хочу, чтобы вся моя скриптовая система была самодостаточной, чтобы икота с пространством /var/tmp не имела значения для моей системы Ant, когда удаленная платформа POSIX исчерпывает ничтожное количество пространства /var/tmp . Ерунда.

В идеальном мире POSIX я должен быть в состоянии контролировать местоположение (путь), используемое всеми shell сценариями 'here documents', верно? Должен быть настраиваемый способ; но все, что я нашел, - это способ управлять расположением временного пространства 'vi' с помощью файла .exinit с командой directory=

/tmp . Разве та же идея не была реализована оригинальными богами UNIX для сценария POSIX shell здесь?

Попробовал следующие идеи; они ничего не изменили:

Чтобы воспроизвести проблему, заполните /var/tmp (ваша система POSIX может использовать /tmp) 100%,, а затем попробуйте сохранить следующий сценарий в tst.sh . Затем сделайте его исполняемым с помощью chmod +x tst.sh . Затем запустите tst.sh , набрав его в командной строке shell, конечно.

Заранее спасибо за любые зацепки гуру!

1 ответ

Я получаю ошибку на ubunto, открывая terminal, говоря следующее: Bash: не удается создать временный файл для here-document: не осталось места ok device Хотя пространство уже существует.. Эта ошибка возникла внезапно, когда я работал над sublime text

У меня есть ошибка, и я не знаю почему, я гуглил ее много раз, но ничего не появилось. com.android.ddmlib.SyncException: на устройстве не осталось свободного места Я убираю и строю проект. Ничего. Я не знаю, что делать дальше. Я говорю об эмуляторе.

Если TMPDIR установлен, bash использует его значение в качестве имени каталога, в котором bash создает временные файлы для использования shell.

Если ваш POSIX shell не имеет такой функции, лучшее, что я знаю, это то, что вы измените свой собственный shell по исходному коду, чтобы прочитать значение экспортируемой переменной, где, если она допустима и доступна для записи, используйте ее для временных файлов. Если нет, вернитесь к умолчанию. Если вы беспокоитесь о возможных проблемах с безопасностью, подумайте о том, чтобы сделать это изменение постоянным. Если вы консервативны в отношении возможных ошибок или системных сбоев, которые могут возникнуть в результате изменения shell, подумайте о переименовании его в другой двоичный файл, например /bin/modsh или /bin/testsh .

Если у вас большая память, вы можете рассмотреть возможность подключения tmpfs к /var/tmp , но это может относиться только к Linux. Я не уверен насчет других систем. Linux в tmpfs разделяет память, которую он использует, т. е. Когда он не используется, другие программы могут использовать его. Он довольно стабилен и еще более стабилен, если его использовать вместе со свопом. И tmpfs может иметь заданный предел размера.

Похожие вопросы:

Я прикрепил новый том к экземпляру EC2. Том был успешно прикреплен. Ниже вывод команды. DF-ч Файловая система размер использовано воспользоваться Use% установленный на /dev/xvda1 32Г 8.1G 22Г 27% /.

Я запускаю свое приложение на своем телефоне, отлаживая его с помощью MAT. После того, как я попытаюсь сбросить файл HPROF в Eclipse, я получу ошибку: Не удалось сохранить данные hprof во временный.

Я получаю ошибку на ubunto, открывая terminal, говоря следующее: Bash: не удается создать временный файл для here-document: не осталось места ok device Хотя пространство уже существует.. Эта ошибка.

У меня есть ошибка, и я не знаю почему, я гуглил ее много раз, но ничего не появилось. com.android.ddmlib.SyncException: на устройстве не осталось свободного места Я убираю и строю проект. Ничего.

Я сталкиваюсь с этой ошибкой git, пытаясь протолкнуть локальную ветвь. Перепробовал возможные способы и последовал нескольким постам SO, динт помог. Любая помощь действительно ценится. remote.

Я нашел один из своих контейнеров внизу. Это уже второй раз, когда у меня возникает такая проблема. Когда я пытаюсь поднять его с помощью docker start, я получаю: Ответ на ошибку от демона.

Я работаю над обучением своей модели в экземпляре P2.xlarge. Когда я загружаю набор данных, я получаю следующую ошибку: исключение во время загрузки или извлечения: [Errno 28] на устройстве не.

Я только что создал приложение с expressJs для учреждения, где они загружают видеоуроки. Сначала видео загружалось на тот же сервер, но позже я переключился на Amazon. Я имею в виду, что только.

кто-нибудь знает, с чем связана эта ошибка ? Связано ли это с размером экземпляра базы данных RDS или с временным табличным пространством ? ERROR: не удалось записать во временный файл hash-join: на.

Эта ситуация выглядит странной и непонятной — что нужно сделать для очистки диска, если место и так есть?

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

Подобная ситуация возникала на этом же сервере накануне и помогла очистка логов, очистка кэша установленных пакетов и удаление ненужных файлов. Но на следующей день ситуация повторилась, хотя места уже точно было достаточно — несколько свободных гигабайт места на диске.

Что делать, если закончилось место в Linux

Вполне возможно, что на диске действительно закончилось место, тогда вам рекомендуются следующие статьи:

Если же место на самом деле имеется, то продолжайте чтение.

Проверьте с du и df

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

Начнём с du. Укажем ей базовую директорию на диске у которого проблемы. Это руководство подразумевает, что проблемным диском является раздел с рутом.


Для обхода всего дерева директорий потребуется время.

Теперь попробуем с df:


Добавьте корень файловой системы (рут) и файловые системы, смонтированные под ним. Например, если у вас есть «/home» на отдельном диске, добавьте это к показанию для root. Количество занятого и свободного пространства должно получиться близко к тому, что нам показала программа du. Если это не так, это может указывать на то, что удалённые файлы используются процессами.

Главное, на что следует обратить внимание, чтобы вывод этих команд о занятом пространстве соответствовал друг другу и размеру диска. Если это не так, значит имеется проблема.

Возможные причины

Возможны разные ситуации возникновения ошибки о том, что диск переполнен, когда на самом деле на нём ещё достаточно места. Если вы видите несоответствие между выводом команд du и df, то перейдите к первому варианту решения проблемы. В противном случае начните со второго.

Удаление файлов занятых процессом

Иногда файл будет удалён, но процесс все ещё использует его. Linux не освободит хранилище, связанное с файлом, пока процесс ещё запущен. Вам просто нужно найти процесс и перезапустить его.

Попробуйте найти процесс.


Если результатов нет, то попробуйте команду:

В списке должен присутствовать процесс, вызывающий проблему, затем просто перезапустите его:

Недостаточно Инод (Inode)

Для современных файловых систем Linux есть такое понятие как иноды (“inodes”) - это набор метаданных на файловой системе. Иноды отслеживают информацию о файлах. Многие файловые системы имеют фиксированное количество инод, поэтому очень возможно занять максимальное выделенное количество без заполнения самой файловой системы. Вы можете использовать для проверки команду df:


Сравните количество существующих инод с количеством занятых. Если больше нет свободных, к сожалению, вы не можете получить больше. Выход: удалите ненужные или устаревшие файлы для очистки инод.

В нормальных условиях, даже на системах интенсивно использующих постоянное хранилище, редко происходит потребление всех инод. Как правило, исчерпание inodes сигнализирует о другой проблеме. Обычно причиной является неконтролируемое создание огромного количество файлов из-за бага в системе или в программе.

В первую очередь нужно локализовать папку, в которой возникла проблема.

Следующая команда выведет количество используемых инод каждой папкой, присутствующей в корне файловой системы:

Ещё варианты команд, которые делают это же самое (по умолчанию они настроены проверять текущую папку — это можно изменить, для этого вместо точки впишите желаемую для проверки папку:

Когда найдена папка с наибольшим количеством инод, то проверьте её подпапки — для поиска проблемной. Продолжайте эти действия, пока не найдёте папку с огромным количеством нагенерированных файлов.

Например, использование первой команды для поиска по директории /src/:

Вариант для поиска по директории /var/cache/:

В разных ситуациях для пользователей проблемными папками оказывались:

  • /var/lib/php/sessions/
  • /var/cache/fontconfig
  • /usr/src/
  • /var/cache/eaccelerator/
  • /var/log/squid3/

В /usr/src/ накапливалось слишком большое количество файлов, имеющих отношение к предыдущим ядрам. В /var/lib/php/sessions/ - бесконечные сессии phpMyAdmin. В /var/log/squid3/ и вообще в папке /var/log/ может накопиться огромное количество файлов с журналами от неправильно работающей программы или просто за много лет. В папке /var/cache/ может скопиться огромное количество файлов, имеющих отношение к кэшированию.

В моём случае причиной проблемы оказалась папка /var/cache/fontconfig — в этой папке постоянно накапливаются новые файлы (я не знаю, насколько это нормально) и по итогу работы за 4 года из-за этой папки закончились иноды.

Когда проблемная папка найдена, то нужно её очистить. Скорее всего все файлы в ней не нужны (оцените это исходя из вашей ситуации). Также весьма вероятно, что файлов там астрономическое количество и их обработка может затянуться на часы, поэтому самый быстрый вариант — удалить папку целиком, а затем создать её заново. Даже при таком подходе в моём случае удаление папки /var/cache/fontconfig заняло около 10-20 минут.

Это полностью разрешило мою проблему и снизило количество используемых инод со 100% до 13%:


Плохие блоки

Ещё одна распространённая проблема — это плохие блоки в файловой системе. Со временем из-за износа дисков, файловые системы повреждаются. Ваша операционная система, скорее всего, увидит эти блоки пригодными для использования, если они не помечены иным образом. Лучший способ найти и пометить эти блоки — использовать fsck с флагом -cc. Помните, что вы не можете использовать fsck из той же файловой системы, которую тестируете. Вам, вероятно, понадобится использовать Live CD.

Очевидно, замените /dev/sda2 на имя того диска и раздела, который вы хотите проверить. Кроме того, имейте в виду, что это, вероятно, займёт много времени.

Надеюсь, одно из этих решений решило вашу проблему. Эту проблему не всегда легко диагностировать в каждом случае. Однако, если повезёт, вы сможете устранить источник проблемы и продолжить пользоваться системой без её переустановки.

Ubuntu 1604 сообщила об ошибке «не удается создать временный файл для данного документа: на устройстве не осталось места», увеличьте емкость sda, чтобы решить эту проблему.

Источник проблемы:

cannot create temp file for here-document: No space left on device

Решение после поиска:емкости sda не хватает, мы должны расширить емкость

Метод продления следующий:

1. Установите емкость расширения в настройках виртуальной машины.

/ dev / sda1 Расширение

1. Перед расширением проверьте / dev / sda *


2. Завершите работу, настройки виртуальной машины -> жесткий диск -> развернуть


Подождите минуту или две, после завершения расширения вам будет предложено переразметить операционную систему.


3. Включите виртуальную машину и выполните [sudo fdisk -l], вы увидите


Во-вторых, передел после расширения

1. Установите Gparted

Метод первый, выполните [sudo apt-get install gparted] в терминале, этот метод не работает, потому что память заполнена, подсказка


Метод 2: Загрузите образ Gparted и загрузите его с виртуальной машины

1. Загрузите образ Gparted для подключения при запуске виртуальной машины.


2. Ввести прошивку при включении питания (после выключения питания нажатие ESC сразу после включения машина не сможет войти, и я сразу войду в BIOS своим методом)


3. Установите приоритетную загрузку после входа в BIOS и установите CD-ROM в качестве первого варианта загрузки.


(1) Войдите в следующий интерфейс и нажмите Enter напрямую.


(2) Продолжайте вводить


(3) Выберите 26 - упрощенный китайский.


(4) Выберите 0, чтобы продолжить запуск и автоматически запустить Gparted.


4. Войдите в раздел Gparted

(1) Объедините раздел с sda1, удалите linux-swap и расширьте его по порядку, и, наконец, оставьте sda1 без выделения.


(2) Щелкните правой кнопкой мыши sda1 и выберите изменить размер, здесь я настраиваю размер до 40 ГБ


(3) Щелкните правой кнопкой мыши «Нераспределенный», чтобы создать новый расширенный раздел, и двигатель «Новый»


(4) Нажмите «Применить», чтобы применить его.


(5) Эффект после расширения перегородки следующий.


В-третьих, войдите в терминал, чтобы проверить успешность расширения.

1. Снимите зеркало Gparted.


2. Включите питание, выполните [df -hl] в терминале, чтобы проверить, успешно ли увеличена емкость.

Читайте также: