АБО з 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.