Как я могу достичь ловушки bash EXIT при выполнении другого двоичного кода?

Я хотел бы использовать ловушку bash EXIT и использовать exec, чтобы избежать появления нового процесса. Возможно ли это?

То есть

#!/bin/bash
touch $0.$$
trap "rm -v $0.$$" EXIT
/bin/echo Hello

удаляет временный файл $0.$$ с помощью bash EXIT trap

#!/bin/bash
touch $0.$$
trap "rm -v $0.$$" EXIT
exec /bin/echo Hello

никогда не "запускает" ловушку (после завершения сообщения нет сообщения rm, файл $0.$$).

Конечно, имеет смысл, что ловушка не может срабатывать, поскольку bash больше не контролируется после exec. Есть ли способ заставить его работать и использовать exec? Это, по общему признанию, из любопытства, чем практические вопросы.

Ответ 1

Как правило, нет. Это невозможно по той причине, о которой вы говорите.

Это скучный ответ. Давайте рассмотрим наши варианты обходных решений:

Если мы больше заботимся о семантике exec и меньше о запуске нескольких процессов, мы можем для произвольных исполняемых файлов:

{ while kill -0 $$; do sleep 5; done; rm "$0.$$"; } &
exec ./file

который будет exec файла, а другой процесс опросит его и выполнит очистку, когда это будет сделано.

Если мы хотим избежать вилок и что мы выполняем, это еще одна оболочка script, мы можем сделать

exec bash --rcfile <(echo 'trap "..." exit') -i ./file

to exec и выполнить очистку после этого (пока script не exec или переопределяет ловушку), не запуская новый процесс. source ing вместо exec ing будет иметь такой же эффект:

trap "..." exit
source ./file

Если мы хотим получить действительно хаки, мы можем использовать LD_PRELOAD для переопределения exit(3) и выполнения команды по нашему выбору:

#include <stdlib.h>

void exit(int c) {
    char* cmd = getenv("EXIT");
    char *argv[] = { "bash", "-c", cmd, NULL };
    char *envp[] = { NULL };
    execvpe("bash", argv, envp);
}

Мы можем скомпилировать это как библиотеку:

$ gcc -shared -fPIC foo.c -o libfoo.so

а затем предварительно загрузите его в произвольные динамически связанные исполняемые файлы:

$ LD_PRELOAD=./libfoo.so EXIT='echo "This is a hack"' ls *foo*
foo.c  libfoo.so
This is a hack

Эти хаки забавны, но редко необходимы в реальном мире. Более простое, лучшее и более каноническое решение - это просто не exec.