ORing с true в команде через ssh

ORing с true в команде через ssh

Когда я пытаюсь запустить 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"1pkillНет соответствующих процессов”.

Самое сложное — понятьпочемувозникает ошибка 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выполняет простую команду вместо того, чтобы проходить дополнительный шаг forking перед ее 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"sshdbash -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" ...

Связанный контент