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
будет содержать канонический путь.
все
недопустимы в поле 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
, который сделает файл исполняемым только для членов некоторой _ группы
.
Отличный классический вопрос об управлении рабочими местами и сигналами с хорошими примерами! Я разработал сценарий испытаний, чтобы сосредоточиться на механике обработки сигналов.
Для этого после запуска нижестоящих элементов (loop.sh) в фоновом режиме вызовите wait
и после получения сигнала INT убейте
группу процессов, PGID которой равен вашему PID.
Для рассматриваемого сценария, play.sh
, это может быть выполнено следующим образом:
В функции stop ()
замените exit 1
на
kill -TERM - $ $ # запишите тире, отрицательный PID, убивает группу процесса
Запуск loop.sh
в качестве фонового процесса (здесь можно запустить несколько фоновых процессов и управлять ими play.sh
)
loop.sh &
Добавьте 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.
Если требуется отсортировать файлы в данном каталоге по возрасту изменения (последний первый):
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
При выходе из ловушки невозможно вернуть вложенную оболочку.
"Я понимаю, что когда процесс получает сигнал 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) не видел сигнала, поскольку он больше не находится в группе процессов переднего плана.
Проблема заключается в sleep
Когда вы CTRL+C
убиваете sleep
- не ваш sh
(даже если синтаксис в вашем #!/bin/sh
немного подозрителен). Однако сигнал получает вся группа процессов, потому что вы не установили свой обработчик в loop.sh
. которая является вложенной оболочкой - она завершается сразу после этого.
Вам нужна ловушка в loop.sh
. Ловушки очищаются для каждой запущенной подоболочки, если они явно не trap ''
SIG
игнорируются родителем.
Вам также необходимо контролировать работу -m
onitoring в вашем родителе, чтобы он отслеживал своих дочерних команд. wait
, например, вообще работает только с контролем заданий. -m
onitor 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]$