Niepoprawny status wyjścia po ocenie zastąpienia polecenia przez ssh?

1

Mam następującą prostą komendę powłoki, która, jak się spodziewam, zawiedzie i działa na moim lokalnym:

$ DIR=$(false) && echo ok || echo fail
fail
$ sh -c 'DIR=$(false) && echo ok || echo fail'
fail

Ale kiedy przekazuję to polecenie przez ssh, nie działa już zgodnie z oczekiwaniami:

$ ssh user@host sh -c 'DIR=$(false) && echo ok || echo fail'
ok

Nie jestem więc pewien, gdzie jest problem. Już używam apostrofu, aby uniknąć zbyt wczesnego rozszerzania zmiennych .

Co się dzieje i jak sprawić, by podstawienie polecenia działało poprawnie na podstawie kodu wyjścia zwróconego z przypisania zmiennej?


Znalazłem kolejną anomalię w następującym przykładzie:

$ ssh user@host sh -c 'echo 1; echo 2;'

2

który drukuje tylko 2, zamiast drukować zarówno 1, jak i 2.

kenorb
źródło

Odpowiedzi:

2

Myślę, że problem polega na tym, że w grę wchodzą trzy pociski:

  1. lokalna powłoka, która przetwarza sshpolecenie;
  2. zdalna powłoka, która sshwywołuje przetwarzanie polecenia na hoście docelowym;
  3. dodatkowa powłoka wywołana przez sh -cpolecenie zdalne.

Cytaty robią dwie rzeczy:

  • zatrzymują lokalną powłokę oceniającą dowolne zmienne w lokalnej powłoce (1);
  • upewniają się, że istnieje jeden parametr dla pilota sh -c.

Zatem sshwidzi cztery parametry: user@host, sh, -ci odpowiednią komendę. Jednak cytaty są usuwane podczas budowania czwartego parametru, a gdy zdalna powłoka (2) odbiera swoje parametry, interpretuje $(false)siebie, a kod zakończenia jest ustawiany we własnym środowisku przed wywołaniem podpowłoki (3).

W ten sposób dodatkowa powłoka (3) widzi DIR= && echo ok || echo faili DIR=jest całkowicie poprawną, wolną od błędów komendą, stąd okgałąź.

Ten sam efekt można zobaczyć w:

sh -c 'sh -c "DIR=$(false) && echo ok || echo not"'

lub:

sh -c "sh -c 'DIR=$(false) && echo ok || echo not'"

W obu przypadkach działa „poprawnie”, jeśli umieścisz \przed nim $, ponieważ to powstrzymuje ekspansję drugiej powłoki $(false). Użyłem podwójnych cudzysłowów, aby wyjaśnić mechanizmy - jeśli wolisz, są raczej kręte:

sh -c 'sh -c '\''DIR=$(false) && echo ok || echo not'\'

Przykład Richarda działa, ponieważ całe rozwinięcie odbywa się w jednej zdalnej powłoce, a inicjał false;nie ma wpływu na kolejne polecenie. Nie ma to nic wspólnego z tematem omawianym w jego łączu.

AFH
źródło
Dzięki, działa. Zobacz: Umieszczanie znaków w pojedynczych cudzysłowach w celu uwzględnienia pojedynczego cudzysłowu w objaśnieniu ciągu pojedynczego cudzysłowu.
kenorb
1
Nowy przykład w edytowanym poście to dokładnie ten sam problem: cytaty gubią się, a zdalna powłoka widzi sh -c echo 1; echo 2. echoUżywany jest tylko pierwszy parametr sh -c, co wyjaśnia dodatkowy nowy wiersz ( 1jest ignorowany jako zbyteczny parametr); a średnik ogranicza shpolecenie, więc echo 2jest on wykonywany przez zdalną powłokę, a nie podpowłokę. Ponieważ nie ma rozszerzenia parametrów, możesz użyć obu rodzajów cudzysłowów, dzięki czemu ssh user@host sh -c "'echo 1; echo 2;'"będzie działać zgodnie z oczekiwaniami.
AFH
1

Powinno to działać zgodnie z oczekiwaniami:

ssh user@host 'DIR=$(false) && echo ok || echo fail'

Wygląda na to, że po wywołaniu jak powyżej pierwsza instrukcja w potoku powłoki nie będzie działać, jeśli jest to przypisanie zmiennej.

O dziwo, działa to również zgodnie z oczekiwaniami:

ssh user@host sh -c 'false; DIR=$(false) && echo ok || echo fail'

Nie jestem w 100% pewien, co się tutaj dzieje, ale może to być związane z tym pytaniem: https://unix.stackexchange.com/questions/126938/why-is-setting-a-variable-before-a-command- legalny w bash

niezrozumiały
źródło