Ошибочно думать, что правило преуспело из-за 0-размерного файла, сгенерированного перенаправлением вывода

В make-файле у меня есть несколько правил, которые выглядят так:

out.txt: foo.sh input.txt ./foo.sh -i input.txt > out.txt 

Если foo.sh не удается, то out.txt будет создан как файл размера 0. Если я снова запустил make , ошибочно предположим, что файл out.txt был успешно создан, и он снова не будет запускать правило.

Каков правильный способ справиться с подобным сценарием?

  • Некоторые вопросы компиляции linux ядро ​​с make-kpkg
  • Файлы, созданные «make», не получают исполняемых разрешений по умолчанию
  • Как создать пользовательскую версию FreeBSD без привилегий root?
  • Разница между sudo make и sudo -s; делать
  • Нет dpkg или apt, no make или gcc. Необходимо установить их все
  • Не указаны цели и не найден файл makefile. Стоп. Makefile ubuntu
  • Установить команду, если команда не существует
  • Компиляция источников с компилятором, отличным от стандартного
  • 4 Solutions collect form web for “Ошибочно думать, что правило преуспело из-за 0-размерного файла, сгенерированного перенаправлением вывода”

    Вы можете запросить удаление целевого файла, если это правило не удается, .DELETE_ON_ERROR специальную цель с именем .DELETE_ON_ERROR . Ему не нужно ничего делать или иметь какие-либо зависимости, поэтому просто добавьте это в свой make-файл:

     .DELETE_ON_ERROR: 

    Затем вы получаете следующее:

     $ cat Makefile .DELETE_ON_ERROR: foo: false > foo $ make false > foo make: *** [foo] Error 1 make: *** Deleting file `foo' zsh: exit 2 make $ stat foo stat: cannot stat `foo': No such file or directory 

    Из ошибок в рецептах :

    Обычно, когда строка рецепта терпит неудачу, если он вообще изменил целевой файл, файл поврежден и не может быть использован – или, по крайней мере, он не полностью обновлен. Тем не менее, временная метка файла говорит о том, что теперь она обновлена, поэтому в следующий раз make прогоны она не будет пытаться обновить этот файл. Ситуация такая же, как когда оболочка убита сигналом; см. Прерывания . Поэтому, как правило, правильная вещь – удалить целевой файл, если рецепт завершился неудачно после начала изменения файла. make сделает это, если .DELETE_ON_ERROR появляется как цель. Это почти всегда то, что вы хотите make , но это не историческая практика; поэтому для совместимости вы должны явно запросить его.

    Когда это возможно, правила makefile должны сначала создавать свою цель под временным именем, а затем перемещать ее на место. Таким образом, если процесс сборки прерывается по какой-либо причине, не будет полуписанного целевого файла, который нельзя отличить от полностью написанного файла.

     out.txt: foo.sh input.txt ./foo.sh -i input.txt >$@.tmp mv -f $@.tmp $@ 

    mv -f $@.tmp $@ – обычная идиома make-файла.

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

    Поскольку кажется, что foo.sh – это то, что вы написали (или генерируется тем, что вы написали), я бы подумал о добавлении к нему флага -o , который принимает имя выходного файла. Тогда вам не нужно зависеть от поведения перенаправления ввода-вывода по умолчанию. Ваш скрипт может очищать частичные выходные файлы или, возможно, даже не создавать файл в первую очередь, если вы можете обнаружить ошибку достаточно рано.

    Я полагаю, что foo.sh правильно возвращает ненулевое значение при ошибке. Затем вы должны сделать временный и только перезаписать результат с успехом.

     tmp=$$(mktemp) && ./foo.sh input.txt > $$tmp && mv $$tmp $@ || rm -f $$tmp && false 
    Linux и Unix - лучшая ОС в мире.