Как избежать конфликтов / загрязнения пространства имен в сценарии, который должен быть получен?

Вам нужно сказать теперь фоновому приложению, чтобы оно остановилось запись на устройстве tty. Нет универсального способа сделать это.

Вы можете:

stty tostop

сделать так, чтобы фоновые задания приостанавливались (с сигналом SIGTTOU ) при попытке записи на tty.

Вы можете присоединить к процессу отладчик и заставить его повторно открывать файловые дескрипторы, открытые на tty-устройстве, в / dev / null . Например (здесь только для stdout):

gdb --batch -ex 'call close(1)' -ex 'call open("/dev/null",2)' -p "$pid"

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

Для динамически связанных приложений вы можете запустить их с помощью большого двоичного объекта LD_PRELOAD , который устанавливает обработчик на SIGTTOU (и выполняет stty tostop ), который повторно открывает stdout и stderr на / dev / null , если они собирались использовать терминал. Это будет обрабатывать случай, когда приложения, отличные от setuid / setgid, записываются на терминал через их stdout / stderr и не обрабатывают SIGTTOU сами.

Выполнить:

 gcc -Wall -shared -fPIC -nostartfiles -o ~/lib/handle_tostop.so handle_tostop.c

Где handle_tostop.c содержит:

#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

static void reopen_on_null(int req_fd)
{
  int fd;
  if (close(req_fd) == 0) {
    fd = open("/dev/null", O_RDWR);
    if (fd != req_fd && fd >= 0) {
      dup2(fd, req_fd);
      close(fd);
    }
  }
}

static void handle_sigttou(int sig)
{
  if (isatty(1)) reopen_on_null(1);
  if (isatty(2)) reopen_on_null(2);
}
void _init()
{
  signal(SIGTTOU, handle_sigttou);
}

А затем:

LD_PRELOAD=~/lib/handle_tostop.so
export LD_PRELOAD
stty tostop

Вместо / dev / null вы можете перенаправить вывод в какой-либо файл журнала (который затем вы можете открыть с помощью ] O_APPEND и, возможно, включите pid процесса в имя файла, чтобы вы знали, чей процесс выводится), чтобы он не отбрасывался, если он вам пригодится.

3
12.11.2015, 01:01
2 ответа

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

function {
  local my_var=foo
  your_var=bar
  : ...
}

Но для функций, которые не будут работать, поскольку они не могут быть объявлены местный . У меня была эта проблема в моем zshrc , и я решил добавить ко всем именам моих «локальных» функций короткую строку (как вы делаете пространства имен в C) и выполнить функцию подстановочного знака в конец:

function foo-do-something () {
  : ...
}
function foo-do-some-more-stuff () {
  : ...
}
function foo-main () {
  local my_var=oof
  you_var=rab
  foo-do-something for bar
  foo-do-some-more-stuff with baz
}
foo-main
unfunction -m 'foo-*'

На мой взгляд, это не оптимальное решение (я бы предпочел локальные функции), но у меня оно работает.

1
27.01.2020, 21:31

Решение заключается в использовании подоболочек, вложенных функций и unsetдля основной функции:

my_app_main()
(
  unset -f my_app_main

  a()
  (
    x=1
    echo "a.x is $x"
  )

  b()
  (
    x=2
    echo "b.x is $x"
  )

  c()
  (
    echo "c.x is $x (empty)"
  )

  a
  b
  c
)

my_app_main "$@"

echo "$x (empty - x is gone)"

# all gone
type my_app_main a b c
2
24.04.2021, 21:30

Теги

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