АБО з true у команді над ssh


15

Коли я намагаюся запустити pkill -fвіддалений за допомогою ssh та намагаюся відкинути можливий код помилки (продовжувати роботу з рештою мого сценарію, навіть якщо процес не знайдений), || trueне веде себе так, як я очікував.

$ pkill asdf || true
$ echo $?
0
$ pkill -f asdf || true
$ echo $?
0
$ ssh pi@10.20.0.10 "pkill asdf || true"
$ echo $?
0
$ ssh pi@10.20.0.10 "pkill -f asdf || true"
255

Я думаю, що саме ssh повертає 255, а не команда між цитатами, але чому?

Відповіді:


29

Ваше припущення, що саме він sshповертає статус виходу 255, є правильним. На sshсторінці чоловіка зазначено, що:

ssh виходить зі статусом виходу віддаленої команди або з 255, якщо сталася помилка.

Якби ви просто запустили ssh pi@10.20.0.10 "pkill -f asdf", ви, швидше за все, отримаєте статус виходу 1, що відповідає pkillстатусу " Жоден процес не узгоджується ".

Найскладніша частина полягає в тому, щоб зрозуміти, чому виникає помилка при SSH при запуску

ssh pi@10.20.0.10 "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 pi@10.20.0.10 "pkill -f asdf || true", в sshdпроцесі вилки і Execs bash -c "pkill -f asdf || true".

Як вказується у відповіді ctx, pkillне вбиватиме власний процес. Тим НЕ менше, він буде вбивати будь-який інший процес , командний рядок якого відповідає -fшаблоном. bash -cКоманда відповідає цим шаблоном , так що вбиває цей процес - його власний батько (як це відбувається).

Потім сервер SSH бачить, що процес оболонки, який він розпочався для запуску віддалених команд, був несподівано вбитий, тому він повідомляє про помилку клієнту SSH.


1
Хоча відповідь правильно ідентифікує джерело проблема , як в pkillвбиває його процес оболонки батьківського , тому що його АГД список збігається з регулярним виразом, я підніму термінологічну заперечення: x || yце НЕ команда з'єднання , це список команд .
Стефан Шазелас

@ StéphaneChazelas Дякую за відгук. Я проходив шляхом включення Баша умовних конструкцій як складених команд, але я згоден, що це більш логічно узгоджено розглядати x||yяк список команд. Зараз я відредагував свою відповідь, щоб включити посилання на різні визначення POSIX.
Ентоні Г - справедливість для Моніки

1
Зауважте, що в загальному випадку це не так багато, що він не може оптимізувати, тому що це список команд, що на підвіконні є інша команда, яку можна запустити (потенційно). В zsh/ ksh93/ FreeBSD sh, виконав false || pkill -f asdfби pkillв оболонці. bashоптимізація проводиться лише тоді, коли є лише одна проста команда. true; pkill -f asdfтакож буде проблемою.
Стефан Хазелас

9

Ваша віддалена команда вбиває себе:

$ 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

Що має сенс! bash -c 'pgrep -af asdf'(без того || true) не знаходить себе. Чому ні? Це є -f.
Готьє

2
@Gauthier Насправді, я думаю, що в цьому випадку Баш досить розумний, щоб зрозуміти, що команда проста (а не складна команда), тому вона оптимізується, фактично не форсуючи новий процес. Я пам’ятаю, як раніше зустрічався з подібною поведінкою (я мушу оновити свою відповідь).
Ентоні Г - справедливість для Моніки

3

Ви просите pkill вбити все, що відповідає "asdf". Ви повинні сказати йому, що він відповідає [a] sdf, таким чином він все одно буде шукати що-небудь з назвою "asdf", але не побачить себе (якщо ви вирівняєте asdf з [a] sdf, зауважте, що 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 (який все ще добре читається!)

"Автоматизувати" це непросто, але зазвичай кожен раз, коли вам потрібно простукувати за змінною рядком regexp = "щось", ви можете спробувати зробити:

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" ...

Примітка: Я знаю, що мій приклад "провалити" греп, я міг би зберегти регулярне вираження так, як воно є, оскільки воно вже не буде відповідати самому собі (a, b або c не збігається з ']' командного рядка) . Але не банально придумати тест на регулярний вираз. Загалом фокус працює. той, який автоматизує, буде працювати більшу частину часу. Якщо ні, то знадобляться якісь хитрі хаки (або вручну).
Олів'є Дулак

Крім того, (abc)?(def)?доведеться бути ([a]bc)?([d]ef)?... Ви не можете проаналізувати регулярний вираз з регексом ?! > :-)
wizzwizz4

@ wizzwizz4 Я знаю. але ваш зразок вже не відповідає собі. це складна річ, я лише запропонував просте рішення для більш простих справ
Олів'є Дулак

@ wizzwizz4 Я вже так кажу в своєму першому коментарі ...
Олів'є Дулак
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.