Zbyt wiele linii shebang (deklaracja skryptu) - jakikolwiek sposób na zmniejszenie ich liczby?

12

Mam projekt składający się z około 20 małych .shplików. Nazywam je „małymi”, ponieważ ogólnie żaden plik nie ma więcej niż 20 linii kodu. Przyjąłem podejście modułowe, ponieważ dlatego jestem wierny filozofii Uniksa i łatwiej jest mi utrzymać projekt.

Na początku każdego .shpliku umieszczam #!/bin/bash.

Mówiąc prosto, rozumiem, że deklaracje skryptów mają dwa cele:

  1. Pomagają użytkownikowi przypomnieć sobie, jaka powłoka jest potrzebna do uruchomienia pliku (powiedzmy, po kilku latach bez używania pliku).
  2. Zapewniają, że skrypt działa tylko z określoną powłoką (w tym przypadku Bash), aby zapobiec nieoczekiwanemu zachowaniu w przypadku użycia innej powłoki.

Kiedy projekt zaczyna rosnąć z powiedzmy 5 plików do 20 plików lub z 20 plików do 50 plików (nie w tym przypadku, ale tylko w celu wykazania), mamy 20 linii lub 50 linii deklaracji skryptu. Przyznaję, choć niektórym może to być zabawne, wydaje mi się, że używanie 20 lub 50 jest trochę zbędne, zamiast tego mówię tylko o 1 na projekt (może w głównym pliku projektu).

Czy istnieje sposób na uniknięcie rzekomej redundancji 20 lub 50 lub znacznie większej liczby wierszy deklaracji skryptu poprzez użycie jakiejś „globalnej” deklaracji skryptu w jakimś głównym pliku?

użytkownik9303970
źródło
2
Nie powinieneś, ale możesz; usuń go i uruchom wszystkie skrypty za pomocą/bin/bash -x "$script"
jesse_b
... Zwykle zanim coś dojdzie do tego punktu, doszedłem już do wniosku, że nadszedł czas, aby przestać używać skryptów powłoki i uzyskać właściwy język skryptowy do wykonania pracy.
Shadur

Odpowiedzi:

19

Nawet jeśli twój projekt może teraz składać się wyłącznie z 50 skryptów Bash, prędzej czy później zacznie gromadzić skrypty napisane w innych językach, takich jak Perl lub Python (dla korzyści, jakie te języki skryptowe mają, których Bash nie ma).

Bez odpowiedniej linii #!w każdym skrypcie bardzo trudno byłoby używać różnych skryptów, nie wiedząc również, jakiego interpretera użyć. Nie ma znaczenia, czy każdy skrypt jest wykonywany z innych skryptów , to tylko przenosi trudność od użytkowników końcowych do programistów. Żadna z tych dwóch grup osób nie powinna wiedzieć, w jakim języku został napisany skrypt, aby móc go używać.

Skrypty powłoki wykonywane bez linii-line #!i bez wyraźnego interpretera są wykonywane na różne sposoby w zależności od tego, która powłoka je wywołuje (patrz np. Pytanie Który interpreter powłoki uruchamia skrypt bez shebang? A zwłaszcza odpowiedź Stéphane'a ), co nie jest tym, czego chcesz w środowisku produkcyjnym (potrzebujesz spójnego zachowania, a być może nawet przenośności).

Skrypty wykonane za pomocą jawnego interpretera będą uruchamiane przez tego tłumacza niezależnie od tego, co #!mówi linia. Spowoduje to problemy w dalszej kolejności, jeśli zdecydujesz się na ponowne wdrożenie, powiedzmy, skryptu Bash w Pythonie lub innym języku.

Powinieneś wydać te dodatkowe naciśnięcia klawiszy i zawsze dodawać #!-linię do każdego skryptu.


W niektórych środowiskach w każdym skrypcie każdego projektu znajdują się legalne teksty zawierające wiele akapitów. Bardzo się ciesz, że to tylko #!linia, która wydaje się „zbędna” w twoim projekcie.

Kusalananda
źródło
Odpowiedź powinna zostać poszerzona o: Powłoka określa interpretera po #!linii, gdy tylko plik ma uzasadnione uprawnienia i nie jest ładowany przez interpretera, ale przez powłokę. Oznacza to, że /path/to/myprog.pluruchamia program perla, jeśli #!linia jest obecna (wskazując na interpreter perla), a plik ma uprawnienia do wykonania.
rexkogitans
11

Nie rozumiesz sensu #!. Uruchamianie tego programu w ramach zdefiniowanego interpretera jest w istocie dyrektywą dla systemu operacyjnego.

Tak więc skrypt może być, #!/usr/bin/perla wynikowy program może być uruchomiony jako ./myprogrami można użyć interpretera perla. Podobnie #!/usr/bin/pythonspowodowałoby, że program działałby w Pythonie.

Więc linia #!/bin/bashmówi systemowi operacyjnemu, aby uruchomił ten program w bash.

Oto przykład:

$ echo $0
/bin/ksh

$ cat x
#!/bin/bash

ps -aux | grep $$

$ ./x
sweh      2148  0.0  0.0   9516  1112 pts/5    S+   07:58   0:00 /bin/bash ./x
sweh      2150  0.0  0.0   9048   668 pts/5    S+   07:58   0:00 grep 2148

Tak więc, mimo że moją powłoką jest ksh, program „x” działa pod kontrolą bash z powodu #!linii i możemy to wyraźnie zobaczyć na liście procesów.

Stephen Harris
źródło
ps -auxmoże dać ostrzeżenie,
Weijun Zhou
@WeijunZhou Powiedz więcej ...
Kusalananda
Niektóre wersje psdają ostrzeżenie Warning: bad syntax, perhaps a bogus '-'?.
Weijun Zhou
2
pssuooprts zarówno składnia argumentów BSD, jak i UXIX . -auxjest źle UNIX Stephen prawdopodobnie oznacza, że auxjest poprawny BSD
Jasen
1
Ludzie, 2 rzeczy; (1) skup się na przykładowej koncepcji (która pspokazuje wywołanie bash), a nie na jawnej składni; (2) ps -auxdziała doskonale na CentOS 7 i Debian 9 bez ostrzeżeń; ta cut'n'paste była dokładnie wyjściem z mojej maszyny; cała dyskusja na temat tego, czy -jest potrzebna czy nie, dotyczy starszych wersji systemu Linux, a nie bieżących wersji.
Stephen Harris
9

Na .sh

Złe jest .shna końcu nazwy pliku.

Wyobraź sobie, że przepisujesz jeden ze skryptów w Pythonie.

Cóż, teraz musisz zmienić pierwszy wiersz na #!/usr/bin/python3taki, który nie jest taki zły, ponieważ musiałeś zmienić również co drugi wiersz kodu w pliku. Jednak musisz także zmienić nazwę pliku z prog.shna prog.py. Następnie musisz znaleźć wszędzie w innych skryptach i wszystkich skryptach użytkownika (te, o których istnieniu nawet nie wiedziałeś, że istnieją), i zmienić je, aby używały nowego skryptu. sed -e 's/.sh/.py/g'może pomóc tym, o których wiesz. Ale teraz Twoje narzędzie kontroli wersji pokazuje zmianę w wielu niepowiązanych plikach.

Alternatywnie zrobić to tak, Unix i nazwa programu prognie prog.sh.

Na #!

Jeśli masz prawidłową #!wartość i ustaw uprawnienie / tryb na wykonanie. Więc nie musisz znać tłumacza. Komputer zrobi to za Ciebie.

chmod +x prog #on gnu adds execute permission to prog.
./prog #runs the program.

W przypadku systemów innych niż GNU przeczytaj instrukcję chmod(i naucz się ósemki), być może i tak ją przeczytasz.

ctrl-alt-delor
źródło
1
Hmmm, rozszerzenia niekoniecznie są złe, przynajmniej pomagają komunikować się z innymi użytkownikami, jaki jest typ pliku.
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy Ale nie musisz znać języka, wystarczy go uruchomić. Nawet Microsoft próbuje odejść od rozszerzeń (niestety robią to, ukrywając je). Zgadzam się jednak, że mogą być dobrym pomysłem: .imagena zdjęcia. .audiodla dźwięku itp. Mówiąc ci, co to jest, ale nie o implementacji / kodowaniu.
ctrl-alt-delor
1
@ ctrl-alt-delor Jeśli plik się nazywa launch-missileslub delete-project-and-make-manager-pissednie chcę „po prostu go uruchomić”, gdy byłem tylko ciekawy języka :)
Sergiy Kolodyazhnyy
2
jeśli jesteś ciekaw języka, użyj filepolecenia. rozpoznaje większość typów plików.
Jasen
2
Zgadzam się, że rozszerzenia są złe dla skryptów wykonywalnych, z dokładnie podanych powodów. Używam rozszerzenia .sh dla skryptu, który musi pochodzić z innego skryptu powłoki (tj. Skryptu niewykonywalnego) - w tym scenariuszu przyrostek to ważne / ważne, ponieważ mówi nam, jak możemy korzystać z pliku.
Laurence Renshaw,
8

[Linie hashbanga] zapewniają, że skrypt działa tylko z określoną powłoką (w tym przypadku Bash), aby zapobiec nieoczekiwanemu zachowaniu w przypadku użycia innej powłoki.

Prawdopodobnie warto zauważyć, że jest to całkowicie błędne. Linia hashbanga w żaden sposób nie uniemożliwia próby podania pliku do niepoprawnej powłoki / interpretera:

$ cat array.sh
#!/bin/bash
a=(a b c)
echo ${a[1]} $BASH_VERSION
$ dash array.sh
array.sh: 2: array.sh: Syntax error: "(" unexpected

(lub podobnie z awk -f array.shitp.)


Ale w każdym razie innym podejściem do modułowości byłoby zdefiniowanie funkcji powłoki w plikach, jednej funkcji na plik, jeśli chcesz, a następnie sourceplików z programu głównego. W ten sposób nie potrzebujesz hashbangów: będą one traktowane jak inne komentarze i wszystko będzie działało przy użyciu tej samej powłoki. Minusem byłoby to, że potrzebujesz sourceplików z funkcjami (np. for x in functions/*.src ; do source "$x" ; done), A wszystkie one działałyby przy użyciu tej samej powłoki, więc nie można bezpośrednio zastąpić jednego z nich implementacją Perla.

ilkkachu
źródło
+1 na przykład z tablicami bash. Użytkownicy niezaznajomieni z wieloma powłokami lub niektórymi definicjami POSIX mogą założyć , że niektóre funkcje jednej powłoki istnieją w innej. Może to również dotyczyć funkcji: unix.stackexchange.com/q/313256/85039
Sergiy Kolodyazhnyy
4

Czy istnieje sposób na uniknięcie rzekomej redundancji 20 lub 50 lub znacznie większej liczby wierszy deklaracji skryptu poprzez użycie jakiejś „globalnej” deklaracji skryptu w jakimś głównym pliku?

Istnieje - nazywa się to przenośnością lub zgodnością z POSIX. Staraj się zawsze pisać skrypty w przenośny, neutralny dla powłoki sposób, źródłuj je tylko ze skryptu, który używa #!/bin/shlub gdy używasz /bin/shinteraktywnie (lub przynajmniej powłoki zgodnej z POSIX, takiej jak ksh).

Jednak przenośne skrypty nie ratują Cię przed:

  • konieczność korzystania z funkcji jednej z muszli ( odpowiedź ilkkachu jest przykładem)
  • potrzeba używania języków skryptowych bardziej zaawansowanych niż powłoki (Python i Perl)
  • Błąd PEBKAC: skrypt wpada w ręce J.Doe użytkownik, który uruchamia skrypt nie jak ty ma go, na przykład działają one /bin/shskrypt csh.

Jeśli mogę wyrazić swoją opinię, „unikanie redundancji” poprzez wyeliminowanie #!jest złym celem. Celem powinna być spójna wydajność, a właściwie działający skrypt, który działa i uwzględnia przypadki narożników, przestrzega pewnych ogólnych zasad, takich jak nieparsowanie-ls lub zawsze cytowanie zmiennych-chyba, że ​​trzeba rozdzielać słowa .

Pomagają użytkownikowi przypomnieć sobie, jaka powłoka jest potrzebna do uruchomienia pliku (powiedzmy, po kilku latach bez używania pliku).

Naprawdę nie są przeznaczone dla użytkownika - służą systemowi operacyjnemu do prawidłowego działania interpretera. „Właściwy” w tym przypadku oznacza, że ​​OS znajduje to, co jest w shebang; jeśli kod pod nim dla złej powłoki - to wina autora. Jeśli chodzi o pamięć użytkownika, nie sądzę, aby „użytkownik” programów takich jak Microsoft Word lub Google Chrome kiedykolwiek potrzebował wiedzieć, w jakim języku są napisane programy; autorzy mogą potrzebować.

Z drugiej strony skrypty napisane przenośnie mogą wyeliminować konieczność pamiętania, która powłoka była pierwotnie używana, ponieważ skrypty przenośne powinny działać w dowolnej powłoce zgodnej z POSIX.

Zapewniają, że skrypt działa tylko z określoną powłoką (w tym przypadku Bash), aby zapobiec nieoczekiwanemu zachowaniu w przypadku użycia innej powłoki.

Oni nie. Tym, co faktycznie zapobiega nieoczekiwanemu zachowaniu, jest pisanie przenośnych skryptów.

Sergiy Kolodyazhnyy
źródło
1

Powodem, dla którego musisz umieścić #!/bin/bashna górze każdego skryptu, jest to, że uruchamiasz go jako osobny proces.

Po uruchomieniu skryptu jako nowego procesu, system operacyjny widzi, że jest to plik tekstowy (w przeciwieństwie do binarnego pliku wykonywalnego) i musi wiedzieć, który interpreter ma wykonać. Jeśli tego nie powiesz, system operacyjny może tylko zgadywać, używając domyślnego ustawienia (które administrator może zmienić). Tak więc #!linia (plus oczywiście dodawanie uprawnień do plików wykonywalnych) zamienia prosty plik tekstowy w plik wykonywalny, który można uruchomić bezpośrednio, mając pewność, że system operacyjny wie, co z tym zrobić.

Jeśli chcesz usunąć #!linię, masz następujące opcje:

  1. Wykonaj skrypt bezpośrednio (tak jak teraz) i mam nadzieję, że domyślny interpreter pasuje do skryptu - prawdopodobnie jesteś bezpieczny na większości systemów Linux, ale nie można go przenosić na inne systemy (np. Solaris) i można go zmienić na cokolwiek (mógłbym nawet ustawić, aby działało vimna pliku!).

  2. Wykonaj skrypt za pomocą bash myscript.sh. Działa to dobrze, ale musisz podać ścieżkę do skryptu, i jest to bałagan.

  3. Skrypt źródłowy należy użyć . myscript.sh, który uruchamia skrypt w ramach bieżącego procesu tłumacza (tj. Nie jako podproces). Ale prawie na pewno będzie to wymagać modyfikacji twoich skryptów i sprawi, że będą mniej modułowe / wielokrotnego użytku.

W przypadkach 2 i 3 plik nie potrzebuje (i nie powinien mieć) uprawnień do wykonywania, a nadanie mu .sh(lub .bash) sufiksu jest dobrym pomysłem.

Ale żadna z tych opcji nie wygląda dobrze dla twojego projektu, ponieważ powiedziałeś, że chcesz zachować swoje skrypty modułowe (=> niezależne i niezależne), więc powinieneś trzymać #!/bin/bashlinię na górze skryptów.

W swoim pytaniu powiedziałeś również, że postępujesz zgodnie z filozofią uniksową. Jeśli to prawda, powinieneś dowiedzieć się, do czego #!tak naprawdę jest linia, i używać jej w każdym skrypcie wykonywalnym!

Laurence Renshaw
źródło
Dzięki za dobrą odpowiedź. Uwielbiałem wszystko w odpowiedzi i myśli upvoting aż tak: then you should learn what the #! line is really for, and keep using it in every executable script!. Filozofię U. można podążać w górę do pewnego punktu wiedzy. Po tych wszystkich odpowiedziach zrozumiałem, dlaczego można je włączyć do tego terminu. Proponuję zredagować odpowiedź, zastępując ją: „Zobacz shebang jako wewnętrzną część filozofii uniksowej, którą szanujesz i adoptujesz”.
user9303970
1
Przepraszam, jeśli byłem bezpośredni lub jeśli ten komentarz wydawał się niegrzeczny. Twoje komentarze sugerują, że wolisz pracować w środowisku IDE - z „projektem”, który pozwala zdefiniować globalne reguły dla skryptów w tym projekcie. Jest to prawidłowy sposób rozwoju, ale nie pasuje do traktowania każdego skryptu jako niezależnego, niezależnego programu (filozofia uniksowa, o której mówiłeś).
Laurence Renshaw,