![ORing с true в команде через ssh](https://rvso.com/image/122433/ORing%20%D1%81%20true%20%D0%B2%20%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B5%20%D1%87%D0%B5%D1%80%D0%B5%D0%B7%20ssh.png)
Когда я пытаюсь запустить pkill -f
удаленно через ssh и пытаюсь сбросить возможный код ошибки (чтобы продолжить выполнение остальной части моего скрипта, даже если ни один процесс не найден), || true
поведение не соответствует моим ожиданиям.
$ pkill asdf || true
$ echo $?
0
$ pkill -f asdf || true
$ echo $?
0
$ ssh [email protected] "pkill asdf || true"
$ echo $?
0
$ ssh [email protected] "pkill -f asdf || true"
255
Я полагаю, что это таксшкоторая возвращает 255, а не команду в кавычках, но почему?
решение1
Ваше предположение, что он ssh
сам возвращает код выхода 255, верно. ssh
На странице руководства указано, что:
ssh завершается со статусом завершения удаленной команды или с кодом 255, если произошла ошибка.
Если бы вы просто запустили , вы, скорее всего, получили бы статус выхода , соответствующий статусу для «ssh [email protected] "pkill -f asdf"
1
pkill
Нет соответствующих процессов”.
Самое сложное — понятьпочемувозникает ошибка SSH при запуске
ssh [email protected] "pkill -f asdf || true"
Удаленные команды SSH
SSH-сервер запускает оболочку для выполнения удаленной команды(-й). Вот пример этого в действии:
$ ssh server "ps -elf | tail -5"
4 S root 35323 1024 12 80 0 - 43170 poll_s 12:01 ? 00:00:00 sshd: anthony [priv]
5 S anthony 35329 35323 0 80 0 - 43170 poll_s 12:01 ? 00:00:00 sshd: anthony@notty
0 S anthony 35330 35329 0 80 0 - 28283 do_wai 12:01 ? 00:00:00 bash -c ps -elf | tail -5
0 R anthony 35341 35330 0 80 0 - 40340 - 12:01 ? 00:00:00 ps -elf
0 S anthony 35342 35330 0 80 0 - 26985 pipe_w 12:01 ? 00:00:00 tail -5
Обратите внимание, что оболочка по умолчанию — , bash
и что удаленная команда — это не простая команда, атрубопровод, «последовательность из одной или нескольких команд, разделенных оператором управления |
».
Оболочка Bash достаточно умна, чтобы понимать, что если команда, передаваемая ей параметром, -c
являетсяпростая команда, он может оптимизироваться, фактически не разветвляя новый процесс, т. е. он напрямую exec
выполняет простую команду вместо того, чтобы проходить дополнительный шаг fork
ing перед ее exec
выполнением. Вот пример того, что происходит при запуске удаленной простой команды ( ps -elf
в данном случае):
$ ssh server "ps -elf" | tail -5
1 S root 34740 2 0 80 0 - 0 worker 11:49 ? 00:00:00 [kworker/0:1]
1 S root 34762 2 0 80 0 - 0 worker 11:50 ? 00:00:00 [kworker/0:3]
4 S root 34824 1024 31 80 0 - 43170 poll_s 11:51 ? 00:00:00 sshd: anthony [priv]
5 S anthony 34829 34824 0 80 0 - 43170 poll_s 11:51 ? 00:00:00 sshd: anthony@notty
0 R anthony 34830 34829 0 80 0 - 40340 - 11:51 ? 00:00:00 ps -elf
Я уже сталкивался с таким поведением, но не смог найти лучшего источника, кромеэтот ответ AskUbuntu.
поведение pkill
Так как pkill -f asdf || true
это не простая команда (этосписок команд), вышеуказанная оптимизация не может быть выполнена, поэтому при запуске процесс разветвляется и выполняется .ssh [email protected] "pkill -f asdf || true"
sshd
bash -c "pkill -f asdf || true"
Как указывает ответ ctx, pkill
не убьет свой собственный процесс. Однако, онволяубить любой другой процесс, чья командная строка соответствует -f
шаблону. bash -c
Команда соответствует этому шаблону, поэтому она убивает этот процесс – его собственного родителя (так уж получилось).
Затем сервер SSH обнаруживает, что процесс оболочки, запущенный им для выполнения удаленных команд, был неожиданно завершен, и сообщает об ошибке клиенту SSH.
решение2
Ваша удаленная команда завершает работу сама себя:
$ ssh 10.0.3.70 'pgrep -af asdf'
$ ssh 10.0.3.70 'pgrep -af asdf || true'
1018 bash -c pgrep -af asdf || true
pgrep и pkill будут игнорировать свой собственный процесс, но с флагом -f они найдут родительскую оболочку:
$ pgrep -af asdf
$ pgrep -af asdf || true
$ bash -c 'pgrep -af asdf'
$ bash -c 'pgrep -af asdf || true'
9803 bash -c pgrep -af asdf || true
решение3
Вы просите pkill уничтожить все, что соответствует "asdf". Вы должны указать ему соответствовать [a]sdf, таким образом он все равно будет искать все, что называется "asdf", но не увидит себя (если вы выровняете asdf с [a]sdf, обратите внимание, что s выровнена с ] а не с s.)
ssh 10.0.3.70 'pgrep -af "[a]sdf" || true'
Это распространенный прием, который также используется с grep/egrep/awk и т. д.:
ps -ef | grep "something" # will sometimes match itself too
ps -ef | grep "[s]omething" # will not match itself
# why it works:
# the commandline contains: ps -ef | grep [s]omething
# and grep tries to find: something
Этот трюк старый, и я видел его несколько десятилетий назад в разделе «Часто задаваемые вопросы» по Unix (который до сих пор интересно читать!)
«Автоматизировать» это непросто, но обычно каждый раз, когда вам нужно выполнить grep для переменной строки regexp="something", вы можете попробовать сделать:
grep "$(echo "${regexp}" | LC_ALL='C' sed -e 's/[a-zA-Z0-9_-]/[&]/')"
# if regexp="something", it does: grep "[s]omething"
# if regexp="otherthing", it does: grep "[o]therthing"
# if regexp="^thirdthing", it does: grep "^[t]hirdthing" #ok, kept the "^"
#BUT fails on : regexp="[abc]def", as it does: grep "[[a]bc]def" instead of grep "[abc][d]ef" ...