Jak działa ten shebang, który zaczyna się od podwójnego łącznika (-)?


14

Znalazłem następujący rodzaj shebang na stronie RosettaCode:

--() { :; }; exec db2 -txf "$0"

Działa z Db2 i podobnie z Postgres. Nie rozumiem jednak całej linii.

Wiem, że podwójny myślnik jest komentarzem w SQL, a następnie wywołuje plik wykonywalny Db2 z niektórymi parametrami przekazującymi sam plik jako plik. Ale co z nawiasami, kręconymi hamulcami, dwukropkiem i średnikiem i jak zastąpić prawdziwy shebang #! ?

https://rosettacode.org/wiki/Multiline_shebang#PostgreSQL

Odpowiedzi:


18

Powiązane: Który interpreter powłoki uruchamia skrypt bez shebang?

Skrypt nie ma #!linii shebang / hashbang / line, po prostu dlatego, że nie ma podwójnego myślnika #!.

Jednak skrypt zostanie wykonany przez powłokę (patrz wyżej powiązane pytanie i odpowiedzi), aw tej powłoce, jeśli -jest poprawnym znakiem w nazwie funkcji, linia deklaruje wywołaną funkcję powłoki --, która nic nie robi (cóż, działa :, który nic nie robi ) i który nigdy nie jest nazywany.

Funkcja, w bardziej powszechnej notacji wieloliniowej (aby po prostu pokazać, jak to wygląda, ponieważ jej dziwna nazwa niejasno tłumi fakt, że w rzeczywistości jest funkcją):

-- () {
  :
}

Jedynym celem definicji funkcji jest posiadanie wiersza, który jest poprawny w skrypcie powłoki, a jednocześnie poprawne polecenie SQL (komentarz). Ten rodzaj kodu nazywa się poliglotą .

Po zadeklarowaniu fałszywej funkcji powłoki skrypt wykonywany przez interpreter skryptu powłoki execzastępuje bieżącą powłokę procesem wynikającym z uruchomienia db2 -txf "$0", który byłby taki sam, jak użycie db2 -txfnazwy ścieżki skryptu z wiersza poleceń.

Ta sztuczka prawdopodobnie nie działałaby niezawodnie w systemach, w których dashlub ashpowłoki oparte na innych powłokach, yashpowłoka Bourne'a ksh88lub ksh93jest używana jako /bin/sh, ponieważ powłoki te nie akceptują funkcji, których nazwa zawiera myślniki.

Powiązane również:


Przypuszczam, że następujące elementy również by działały (nie tak naprawdę przetestowane):

--() { exec db2 -txf "$0"; }; --

@ilkkachu Lepiej teraz?
Kusalananda

1
o tak! I dzięki za przypomnienie mi, jak to się nazywa. :)
ilkkachu

6

Jak już powiedział @Kusalananda, ta sztuczka jest zepsuta i nie będzie działać we wszystkich powłokach.

Oto moje podejście do robienia tego przenośnie:

--/.. 2>/dev/null; exec db2 -txf "$0"

Pierwsze polecenie powinno zakończyć się niepowodzeniem, nawet jeśli nazwa pliku / katalogu --istnieje w bieżącym katalogu, a wszelkie błędy zostaną zamknięte przez 2>/dev/null; powłoka będzie postępować z drugim komendzie exec.


Nadal nie jest tak naprawdę przenośny. To nie jest poprawny skrypt i nadal polegasz na powłoce wywołującej, aby obejść fakt, że jądro odmówi uruchomienia skryptu i powróci, ENOEXECjeśli spróbujesz. Spróbuj uruchomić skrypt pod, straceaby zobaczyć, co mam na myśli.
kasperd

@kasperd, powinien być nadal przenośny, powłoka powinna uruchamiać skrypt jako skrypt powłoki, jeśli exec()na nim nie działa. „Jeśli funkcja execl () zawiedzie z powodu błędu równoważnego z błędem [ENOEXEC], powłoka wykonuje polecenie równoważne z wywołaniem powłoki z nazwą polecenia jako pierwszym operandem, ...” (patrz pubs.opengroup .org / onlinepubs / 9699919799.2018edition / utilities /… )
ilkkachu

@ilkkachu Ale skrypty nie zawsze są wykonywane z powłoki. Jeśli spróbujesz użyć skryptu w innym kontekście, w którym działałby plik wykonywalny, zakończy się niepowodzeniem. Co więcej, powłoki nie zgadzają się, którego interpretatora użyć. Twój skrypt będzie się teraz zachowywał inaczej lub całkowicie zawiedzie, w zależności od kontekstu, z którego został wywołany.
kasperd

@kasperd, cóż, jasne, to nie będzie działać, jeśli exec()bezpośrednio z innej powłoki. Ale jaka byłaby ta sprawa? Możesz chcieć uruchomić skrypt cronlub coś takiego, ale myślę, że i tak wszystko przepuszcza przez powłokę, a nawet jeśli nie, łatwo to przeliterować db2 -txf /path/to/scriptw tym przypadku, ponieważ musisz to zrobić tylko raz. Praca stenograficzna jest najbardziej przydatna w interaktywnej powłoce. Ale oczywiście osobny skrypt opakowania może być bardziej niezawodny.
ilkkachu

1
@kasperd Nie będę cię drażnić dokumentami i standardami; po prostu spróbuj! echo 'int main(int c,char**a){execvp(a[1],a+1);}' | cc -include unistd.h -xc -; echo echo yeah > a.sh; chmod 755 a.sh; ./a.out ./a.sh; PATH=`pwd` ./a.out a.sh
Wujek Billy,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.