Как я могу обработать ловушку SIGINT с помощью приглашения пользователя в сценарии оболочки?

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

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

Теперь, как пользователь, Вам иногда нравится знать, где тот каталог находится в дереве каталогов. С большинством Нельдов дерево каталогов является деревом без цикла. Таким образом, существует только один путь от корня дерева (/) в любой данный файл. Тот путь обычно называют каноническим путем.

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

Например, процесс, пытающийся узнать, что его текущий каталог /a/b/c, открылся бы .. каталог (относительный путь, таким образом, .. запись в текущем каталоге), и ищите файл каталога типа с тем же inode числом как ., узнайте это c соответствия, затем открывается ../.. и так далее, пока это не находит /. Там нет никакой неоднозначности.

Это что getwd() или getcwd() C функции делают или по крайней мере используемый, чтобы сделать.

В некоторых системах как современный Linux существует системный вызов для возврата канонического пути к текущему каталогу, который делает тот поиск в пространстве ядра (и позволяет Вам находить свой текущий каталог, даже если у Вас нет доступа для чтения ко всем его компонентам), и это что getcwd() вызовы там. На современном Linux можно также найти путь к текущему каталогу через readlink () на /proc/self/cwd.

Это - то, что большинство языков и ранних оболочек делают при возврате пути к текущему каталогу.

В Вашем случае можно звонить cd a как может времена, как Вы хотите, потому что это - символьная ссылка на ., текущий каталог не изменяет так весь из getcwd(), pwd -P, python -c 'import os; print os.getcwd()', perl -MPOSIX -le 'print getcwd' возвратил бы Ваш ${HOME}.

Теперь, символьные ссылки пошли, усложнив все это.

symlinks позвольте переходы в дереве каталогов. В /a/b/c, если /a или /a/b или /a/b/c символьная ссылка, затем канонический путь /a/b/c было бы что-то совершенно другое. В частности, .. запись в /a/b/c не обязательно /a/b.

В Оболочке Bourne, если Вы делаете:

cd /a/b/c
cd ..

Или даже:

cd /a/b/c/..

Нет никакой гарантии, в которой Вы закончите /a/b.

Точно так же, как:

vi /a/b/c/../d

не обязательно то же как:

vi /a/b/d

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

Для cd и pwd встроенные команды (и только для них (хотя также для popd/pushd на оболочках, которые имеют их)), оболочка поддерживает свою собственную идею текущего рабочего каталога. Это хранится в $PWD специальная переменная.

Когда Вы делаете:

cd c/d

даже если c или c/d символьные ссылки, в то время как $PWD содержит /a/b, это добавляет c/d в конец так $PWD становится /a/b/c/d. И когда Вы делаете:

cd ../e

Вместо выполнения chdir("../e"), это делает chdir("/a/b/c/e").

И pwd управляйте только возвращает содержание $PWD переменная.

Это полезно в интерактивных оболочках потому что pwd производит путь к текущему каталогу, который дает информацию о том, как Вы добрались там и, пока Вы только используете .. в аргументах cd и не другие команды, это, менее вероятно, удивит Вас, потому что cd a; cd .. или cd a/.. обычно возвращал бы Вас туда, где Вы были.

Теперь, $PWD не изменяется, если Вы не делаете a cd. До следующего раза Вы звоните cd или pwd, много вещей могло произойти, любой из компонентов $PWD мог быть переименован. Текущий каталог никогда не изменяется (это всегда - тот же inode, хотя это могло быть удалено), но его путь в дереве каталогов мог измениться полностью. getcwd() вычисляет текущий каталог каждый раз, когда это называют путем спуска с дерева каталогов, таким образом, его информация является всегда достоверной, но для логического каталога, реализованного оболочками POSIX, информацией в $PWD мог бы стать устаревшим. Таким образом после выполнения cd или pwd, некоторые оболочки могут хотеть принять меры против этого.

В том конкретном экземпляре Вы видите различные поведения с различными оболочками.

Некоторым нравится ksh93 проигнорируйте проблему полностью, так возвратит неправильную информацию даже после вызова cd (и Вы не видели бы поведения, с которым Вы видите bash там).

Некоторым нравится bash или zsh действительно проверьте это $PWD все еще путь к текущему каталогу на cd, но не на pwd.

pdksh действительно проверяет на обоих pwd и cd (но на pwd, не обновляет $PWD)

ash (по крайней мере, тот, найденный на Debian), не проверяет, и когда Вы делаете cd a, это на самом деле делает cd "$PWD/a", таким образом, если текущий каталог изменился и $PWD больше точки к текущему каталогу, это на самом деле не изменится на a каталог в текущем каталоге, но тот в $PWD (и возвратите ошибку, если она не существует).

Если Вы хотите играть с ним, можно сделать:

cd
mkdir -p a/b
cd a
pwd
mv ~/a ~/b 
pwd
echo "$PWD"
cd b
pwd; echo "$PWD"; pwd -P # (and notice the bug in ksh93)

в различных оболочках.

В Вашем случае, так как Вы используете bash, после a cd a, bash проверки это $PWD неподвижные точки к текущему каталогу. Чтобы сделать это, это звонит stat() на значении $PWD проверить его inode число и сравнить его с числом ..

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

Теперь, для разъяснения ответа Patrice та проверка количества символьных ссылок, с которыми встречаются при поиске пути, должна принять меры против циклов символьной ссылки. Самый простой цикл может быть сделан с

rm -f a b
ln -s a b
ln -s b a

Без той безопасности, на a cd a/x, система должна была бы найти где a ссылки на, находит, что это b и символьная ссылка, которая связывается с a, и это продолжилось бы неограниченно долго. Самый простой способ принять меры, который должен сдаться после разрешения больше, чем произвольное число символьных ссылок.

Теперь назад к логическому текущему рабочему каталогу и почему это не настолько хорошая функция. Важно понять, что это только для cd в оболочке и не других командах.

Например:

cd -- "$dir" &&  vi -- "$file"

не всегда то же как:

vi -- "$dir/$file"

Вот почему Вы будете иногда находить, что люди рекомендуют всегда использовать cd -P в сценариях для предотвращения беспорядка (Вы не хотите, чтобы Ваше программное обеспечение обработало аргумент ../x по-другому по сравнению с другими командами просто, потому что это записано в оболочке вместо другого языка).

-P опция состоит в том, чтобы отключить логический каталог, обрабатывающий так cd -P -- "$var" на самом деле звонит chdir() на содержании $var (кроме тех случаев, когда $var - но это - другая история). И после a cd -P, $PWD будет содержать канонический путь.

6
04.11.2015, 08:51
4 ответа

все недопустимы в поле CIDR строки хоста в pg _ hba.conf .

Из комментариев в файле pg _ hba.conf по умолчанию

# host DATABASE USER CIDR-ADDRESS METHOD [OPTION]

и

CIDR-ADDRESS указывает набор узлов, которым соответствует запись. Он состоит из IP-адреса и маски CIDR, которая является целым числом (от 0 до 32 (IPv4) или 128 (IPv6) включительно), который указывает количество старших битов в маске. Кроме того, можно писать IP-адрес и маска сети в отдельных столбцах для указания набора хостов.

Попробуйте вместо этого:

host    dbname    usname      197.xx.xx.xx/32  md5

, что позволит подключаться к dbname из usname только с удаленного узла 197 .xxxx.xx

Если вы хотите разрешить всю подсеть 197 .xx.xx/24, используйте:

host    dbname    usname      197.xx.xx.0/24  md5
-121--243227-

Классический UNIX- путь создает скрипт setuid :

$ sudo chown root gogui.sh  #not necessary if root is already the owner of the file
$ sudo chmod u+s gogui.sh

Это установит специальный бит разрешения для файла:

$ ls -l gogui.sh
  -rwsr-xr-x 1 root root [omitted] gogui.sh

(Обратите внимание на букву s вместо x в четвертой позиции.)

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

Теперь вы можете упростить скрипт gogui.sh , чтобы он содержал только

/usr/sbin/service lightdm start

Обратите внимание, что я добавил явный путь к исполняемому файлу (который я обнаружил, введя какую службу ): это необходимо в файлах setuid, потому что в противном случае будет выполнена первая программа под названием service , найденная в PATH . Забывание явного пути будет огромной проблемой безопасности, поскольку это означает, что любой пользователь может изменить значение переменной PATH и запустить программу по своему выбору с правами root.

При необходимости можно также запретить другим пользователям запускать файлы с помощью chgrp some_group gogui.sh; chmod o-x gogui.sh , который сделает файл исполняемым только для членов некоторой _ группы .

-121--57342-

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

Для этого после запуска нижестоящих элементов (loop.sh) в фоновом режиме вызовите wait и после получения сигнала INT убейте группу процессов, PGID которой равен вашему PID.

  1. Для рассматриваемого сценария, play.sh , это может быть выполнено следующим образом:

  2. В функции stop () замените exit 1 на

     kill -TERM - $ $ # запишите тире, отрицательный PID, убивает группу процесса
    
  3. Запуск loop.sh в качестве фонового процесса (здесь можно запустить несколько фоновых процессов и управлять ими play.sh )

     loop.sh &
    
  4. Добавьте wait в конце сценария для ожидания всех нижестоящих элементов.

     подождите
    

Когда сценарий запускает процесс,этот потомок становится членом группы процессов с PGID, равным PID родительского процесса, который равен $ $ в родительской оболочке.

Например, сценарий trap.sh запустил три процесса сна в фоновом режиме и теперь ждите , обратите внимание, что столбец идентификатора группы процессов (PGID) совпадает с PID родительского процесса:

  PID  PGID STAT COMMAND
17121 17121 T    sh trap.sh
17122 17121 T    sleep 600
17123 17121 T    sleep 600
17124 17121 T    sleep 600

В Unix и Linux можно послать сигнал каждому процессу в этой группе процессов, вызвав kill с отрицательным значением PGID . Если вы даете kill отрицательное число, оно будет использоваться как -PGID. Так как PID сценария ( $ ) совпадает с PGID, вы можете убить вашу группу процессов в оболочке с помощью

kill -TERM -$$    # note the dash before $$

вы должны дать номер сигнала или имя, в противном случае некоторые реализации kill скажут вам «Незаконная опция» или «Недопустимая спецификация сигнала».

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

$ cat trap.sh
#!/bin/sh

signal_handler() {
        echo
        read -p 'Interrupt: ignore? (y/n) [Y] >' answer
        case $answer in
                [nN]) 
                        kill -TERM -$$  # negative PID, kill process group
                        ;;
        esac
}

trap signal_handler INT 

for i in 1 2 3
do
    sleep 600 &
done

wait  # don't exit until process group is killed or all children die

Пример выполнения:

$ ps -o pid,pgid,stat,args
  PID  PGID STAT COMMAND
 8073  8073 Ss   /bin/bash
17111 17111 R+   ps -o pid,pgid,stat,args
$ 

нет дополнительных процессов. Запустите тестовый сценарий, прервите его ( ^ C ), проигнорируйте прерывание и приостановите его ( ^ Z ):

$ sh trap.sh 
^C
Interrupt: ignore? (y/n) [Y] >y
^Z
[1]+  Stopped                 sh trap.sh
$

Проверьте выполняющиеся процессы, запишите номера групп процессов ( PGID ):

$ ps -o pid,pgid,stat,args
  PID  PGID STAT COMMAND
 8073  8073 Ss   /bin/bash
17121 17121 T    sh trap.sh
17122 17121 T    sleep 600
17123 17121 T    sleep 600
17124 17121 T    sleep 600
17143 17143 R+   ps -o pid,pgid,stat,args
$

Выведите наш тестовый сценарий на передний план ( fg ) и повторите прерывание ( ^ C ), на этот раз выберите не , чтобы игнорировать:

$ fg
sh trap.sh
^C
Interrupt: ignore? (y/n) [Y] >n
Terminated
$

Проверьте выполняющиеся процессы, не переспав:

$ ps -o pid,pgid,stat,args
  PID  PGID STAT COMMAND
 8073  8073 Ss   /bin/bash
17159 17159 R+   ps -o pid,pgid,stat,args
$ 

Примечание о вашей оболочке:

Мне пришлось изменить ваш код, чтобы запустить его в моей системе. В качестве первой строки сценариев используется # !/bin/sh , однако сценарии используют расширения (от bash или zsh), которые недоступны в/bin/sh.

5
27.01.2020, 20:27

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

ls -t

Для сортировки по времени доступа добавьте параметр -u .

ls -tu

Однако следует помнить, что современные системы Linux не отслеживают точное время доступа по умолчанию . Таким образом, временные метки доступа могут быть ненадежными.

Если вы хотите найти самый последний файл в дереве каталогов, включая подкаталоги, самый простой метод - использовать квалификаторы zsh glob .

print -lr -- **/*(om)

Используйте oa вместо ома для использования времени доступа, а не времени изменения. Можно ограничить совпадения, например, чтобы получить 10 самые последние файлы:

print -lr -- **/*(om[1,10])
-121--47509-

В play.sh исходный файл цикла:

source ./loop.sh

При выходе из ловушки невозможно вернуть вложенную оболочку.

1
27.01.2020, 20:27

"Я понимаю, что когда процесс получает сигнал SIGINT, он распространяет его на все дочерние процессы"

Откуда вы взяли эту неверную идею?

$ perl -E '$SIG{INT}=sub { say "ouch $$" }; if (fork()) { say "parent $$"; sleep 3; kill 2, $$ } else { say "child $$"; sleep 99 }'
parent 25831
child 25832
ouch 25831
$

Если бы было распространение, как утверждается, можно было бы ожидать "ой" от дочернего процесса.

В действительности:

"Всякий раз, когда мы набираем на терминале клавишу прерывания (часто DELETE или Control-C) или клавишу выхода (часто Control-backlash), это вызывает либо сигнал прерывания или сигнал выхода посылается всем процессам в группе процессов переднего плана" -- В. Ричард Стивенс. "Advanced Программирование в среде UNIX®". Addison-Wesley. 1993. p.246.

Это можно наблюдать следующим образом:

$ perl -E '$SIG{INT}=sub { say "ouch $$" }; fork(); sleep 99'
^Couch 25971
ouch 25972
$

Поскольку все процессы группы процессов переднего плана будут получать SIGINT от Control+C, вам нужно будет спроектировать все процессы так, чтобы правильно обрабатывать или игнорировать этот сигнал, в зависимости от ситуации, или, возможно, чтобы подпроцессы стали новой группой процессов переднего плана, чтобы родитель (например, shell) не видел сигнала, поскольку он больше не находится в группе процессов переднего плана.

1
27.01.2020, 20:27

Проблема заключается в sleep

Когда вы CTRL+C убиваете sleep - не ваш sh (даже если синтаксис в вашем #!/bin/sh немного подозрителен). Однако сигнал получает вся группа процессов, потому что вы не установили свой обработчик в loop.sh. которая является вложенной оболочкой - она завершается сразу после этого.

Вам нужна ловушка в loop.sh. Ловушки очищаются для каждой запущенной подоболочки, если они явно не trap ''SIG игнорируются родителем.

Вам также необходимо контролировать работу -monitoring в вашем родителе, чтобы он отслеживал своих дочерних команд. wait, например, вообще работает только с контролем заданий. -monitor mode - это то, как оболочки взаимодействуют с терминалами.

sh  -cm '  . /dev/fd/3' 3<<""                     # `-m`onitor is important
     sh -c ' kill -INT "$$"'&                     # get the sig#
     wait %%;INT=$?                               # keep it in $INT
     trap  " stty $(stty -g;stty -icanon)" EXIT   # lose canonical input
     loop()( trap    exit INT                     # this is a child
             while   [ "$#" -le 100 ]             # same as yours
             do      echo "$#"                    # writing the iterator
                     set "" "$@"                  # iterating
                     sleep 3                      # sleeping
             done
     )
     int(){  case    $1      in                   # handler fn
             ($INT)  printf  "\33[2K\rDo you want to stop playing? "
                     case    $(dd bs=1 count=1; echo >&3)  in
                     ([Nn])  return
             esac;   esac;   exit "$1"
     }       3>&2    >&2     2>/dev/null
     while   loop ||
             int "$?"
     do :;   done

0
1
2
3
4
Do you want to stop playing? n
0
1
2
3
Do you want to stop playing? N
0
1
Do you want to stop playing? y
[mikeserv@desktop tmp]$
0
27.01.2020, 20:27

Теги

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