Po co pisać cały skrypt bash w funkcjach?

59

W pracy często piszę skrypty bash. Mój przełożony zasugerował, aby cały skrypt był podzielony na funkcje, podobnie jak w poniższym przykładzie:

#!/bin/bash

# Configure variables
declare_variables() {
    noun=geese
    count=three
}

# Announce something
i_am_foo() {
    echo "I am foo"
    sleep 0.5
    echo "hear me roar!"
}

# Tell a joke
walk_into_bar() {
    echo "So these ${count} ${noun} walk into a bar..."
}

# Emulate a pendulum clock for a bit
do_baz() {
    for i in {1..6}; do
        expr $i % 2 >/dev/null && echo "tick" || echo "tock"
        sleep 1
    done
}

# Establish run order
main() {
    declare_variables
    i_am_foo
    walk_into_bar
    do_baz
}

main

Czy istnieje jakiś inny powód, niż „czytelność”, który moim zdaniem mógłby być równie dobrze ustalony z kilkoma dodatkowymi komentarzami i pewnymi odstępami między wierszami?

Czy sprawia, że ​​skrypt działa wydajniej (w rzeczywistości spodziewałbym się czegoś przeciwnego), czy też ułatwia modyfikację kodu poza wyżej wymienionym potencjałem czytelności? Czy to naprawdę tylko preferencje stylistyczne?

Należy pamiętać, że chociaż skrypt nie pokazuje tego dobrze, „kolejność uruchamiania” funkcji w naszych rzeczywistych skryptach jest zwykle bardzo liniowa - walk_into_barzależy od rzeczy, które i_am_foozostały wykonane, i do_bazdziała na podstawie rzeczy skonfigurowanych przez walk_into_bar- tak więc możliwość arbitralnej zamiany kolejności uruchamiania nie jest czymś, co ogólnie robilibyśmy. Na przykład, nie będzie nagle chcesz umieścić declare_variablespo walk_into_bar, to by złamać rzeczy.

Przykładem tego, jak napisałbym powyższy skrypt, jest:

#!/bin/bash

# Configure variables
noun=geese
count=three

# Announce something
echo "I am foo"
sleep 0.5
echo "hear me roar!"

# Tell a joke
echo "So these ${count} ${noun} walk into a bar..."

# Emulate a pendulum clock for a bit
for i in {1..6}; do
    expr $i % 2 >/dev/null && echo "tick" || echo "tock"
    sleep 1
done
Doktor J
źródło
30
Lubię twojego szefa. W moich skryptach również umieszczam main()na górze i dodaję main "$@"na dole, aby to nazwać. Dzięki temu zobaczysz logikę skryptów wysokiego poziomu po jej otwarciu.
John Kugelman
22
Nie zgadzam się z poglądem, że czytelność można „ustalić na równi z kilkoma dodatkowymi komentarzami i odstępami między wierszami”. Z wyjątkiem fikcji nie chciałbym mieć do czynienia z książką, która nie ma spisu treści i opisowych nazw dla każdego rozdziału i sekcji. W językach programowania taka czytelność zapewnia funkcje, a komentarze nie.
Rhymoid 30.09.16
6
Zauważ, że zmienne deklarowane w funkcjach powinny być deklarowane local- zapewnia to zakres zmiennych, co jest niezwykle ważne w każdym nietrywialnym skrypcie.
Boris the Spider
8
Nie zgadzam się z twoim szefem. Jeśli musisz podzielić skrypt na funkcje, prawdopodobnie nie powinieneś pisać skryptu powłoki. Zamiast tego napisz program.
el.pescado
6
Funkcje dotyczą powtarzających się procesów w skrypcie lub w więcej niż jednym skrypcie. Pozwalają również na wprowadzenie jednolitych metodologii. Np. Użycie funkcji do zapisu do syslog. Tak długo, jak wszyscy korzystają z tej samej funkcji, wpisy syslog są bardziej spójne. Funkcje jednorazowego użytku, takie jak twój przykład, niepotrzebnie komplikują skrypt. W niektórych przypadkach wprowadzają problemy (zakres zmiennych).
Xalorous,

Odpowiedzi:

39

Zacząłem używać tego samego stylu programowania bash po przeczytaniu wpisu na blogu Kfir Lavi „Defensive Bash Programming” . Podaje kilka dobrych powodów, ale osobiście uważam te najważniejsze:

  • procedury stają się opisowe: o wiele łatwiej jest dowiedzieć się, co ma robić konkretna część kodu. Zamiast ściany kodu widzisz „Och, find_log_errorsfunkcja odczytuje ten plik dziennika pod kątem błędów”. Porównaj to ze znalezieniem wielu linii awk / grep / sed, które używają boga, wie, jaki typ wyrażenia regularnego w środku długiego skryptu - nie masz pojęcia, co on tam robi, chyba że są komentarze.

  • możesz debugować funkcje, dołączając do set -xi set +x. Gdy już wiesz, że reszta kodu działa poprawnie, możesz użyć tej sztuczki, aby skupić się na debugowaniu tylko tej konkretnej funkcji. Jasne, możesz załączyć części skryptu, ale co, jeśli jest to długa część? Łatwiej jest zrobić coś takiego:

     set -x
     parse_process_list
     set +x
    
  • użycie drukowania za pomocą cat <<- EOF . . . EOF. Użyłem go kilka razy, aby mój kod był bardziej profesjonalny. Ponadto parse_args()z getoptsfunkcją jest dość wygodny. Ponownie, pomaga to w czytelności, zamiast popychać wszystko do skryptu jako gigantyczną ścianę tekstu. Wygodne jest również ich ponowne użycie.

I oczywiście jest to o wiele bardziej czytelne dla kogoś, kto zna C, Javę lub Vala, ale ma ograniczone doświadczenie bash. Jeśli chodzi o wydajność, nie ma wiele do zrobienia - samo bash nie jest najbardziej wydajnym językiem, a ludzie wolą perl i python, jeśli chodzi o szybkość i wydajność. Możesz nicejednak funkcję:

nice -10 resource_hungry_function

W porównaniu do miłego wywoływania w każdym wierszu kodu, zmniejsza to całe pisanie ORAZ może być wygodnie używane, gdy chcesz, aby tylko część skryptu działała z niższym priorytetem.

Uruchamianie funkcji w tle, moim zdaniem, pomaga również, gdy chcesz mieć całą grupę instrukcji do działania w tle.

Niektóre przykłady, w których użyłem tego stylu:

Sergiy Kolodyazhnyy
źródło
3
Nie jestem pewien, czy powinieneś bardzo poważnie traktować sugestie z tego artykułu. To prawda, ma kilka dobrych pomysłów, ale najwyraźniej nikt nie jest przyzwyczajony do wykonywania skryptów. Nie jest to pojedynczy zmienny w każdym z przykładów jest cytowany (!), A to sugeruje użycie wielkimi literami nazw zmiennych, które jest często bardzo zły pomysł, ponieważ mogą one kolidować z istniejącymi env vars. Twoje punkty w tej odpowiedzi mają sens, ale link do artykułu wydaje się napisany przez kogoś, kto jest przyzwyczajony do innych języków i próbuje zmusić swój styl do bashu.
terdon
1
@terdon Wróciłem do artykułu i ponownie go przeczytałem. Jedynym miejscem, w którym autor wspomina nazewnictwo zmiennych wielkimi literami, są „Niezmienne globalne zmienne”. Jeśli weźmiesz pod uwagę zmienne globalne jako te, które muszą znajdować się w środowisku funkcji, wówczas sensowne jest uczynienie ich kapitałami. Na marginesie, instrukcja bash nie określa konwencji zmiennej wielkości liter. Nawet tutaj zaakceptowana odpowiedź mówi „zwykle”, a jedynym „standardem” jest Google, który nie reprezentuje całej branży IT.
Sergiy Kolodyazhnyy
@terdon na innej notatce, zgadzam się w 100%, że cytowanie zmiennych powinno być wspomniane w artykule, i zostało to wskazane w komentarzach na blogu. Nie oceniałbym też nikogo, kto używałby tego stylu kodowania, niezależnie od tego, czy jest przyzwyczajony do innego języka. Całe to pytanie i odpowiedzi wyraźnie pokazują, że ma to swoje zalety, a stopień, w jakim osoba jest przyzwyczajona do innego języka, prawdopodobnie nie ma tutaj znaczenia.
Sergiy Kolodyazhnyy
1
@terdon dobrze, artykuł został opublikowany jako część materiału „źródłowego”. Mógłbym opublikować wszystko jako własne opinie, ale po prostu musiałem przyznać, że niektóre z rzeczy, których nauczyłem się z tego artykułu, i że wszystko to wynikało z badań z czasem. Link do strony autora pokazuje, że mają dobre doświadczenia z Linuksem i IT ogólnie, więc przypuszczam, że ten artykuł tak naprawdę tego nie pokazuje, ale ufam twojemu doświadczeniu, jeśli chodzi o Linux i skrypty powłoki, więc możesz mieć rację .
Sergiy Kolodyazhnyy
1
To doskonała odpowiedź, ale chciałbym również dodać, że zmienny zakres w Bash jest funky. Z tego powodu wolę deklarować moje zmienne wewnątrz funkcji, używając locali wywołując wszystko przez main()funkcję. To znacznie ułatwia zarządzanie i pozwala uniknąć potencjalnie niechlujnej sytuacji.
Housni
65

Czytelność to jedno. Ale modularyzacja to coś więcej niż tylko to. ( Semi-modularyzacja może być bardziej poprawna dla funkcji.)

W funkcjach możesz zachować niektóre zmienne lokalne, co zwiększa niezawodność , zmniejszając ryzyko pomyłki.

Kolejną zaletą funkcji jest możliwość ponownego użycia . Po zakodowaniu funkcji można ją zastosować wiele razy w skrypcie. Możesz także przenieść go na inny skrypt.

Twój kod może być teraz liniowy, ale w przyszłości możesz wejść w sferę wielowątkowości lub wieloprzetwarzania w świecie Bash. Gdy nauczysz się robić rzeczy w funkcjach, będziesz dobrze przygotowany do przejścia do równoległości.

Jeszcze jeden punkt do dodania. Jak zauważa Etsitpab Nioliv w komentarzu poniżej, łatwo jest przekierować z funkcji jako spójnej jednostki. Ale jest jeszcze jeden aspekt przekierowań z funkcjami. Mianowicie przekierowania można ustawić wzdłuż definicji funkcji. Na przykład.:

f () { echo something; } > log

Teraz wywołania funkcji nie wymagają wyraźnych przekierowań.

$ f

Może to oszczędzić wiele powtórzeń, co ponownie zwiększa niezawodność i pomaga utrzymać porządek.

Zobacz też

Tomasz
źródło
55
Bardzo dobra odpowiedź, choć byłoby znacznie lepiej, gdyby został podzielony na funkcje.
Pierre Arlaud,
1
Być może dodanie tych funkcji pozwala zaimportować ten skrypt do innego skryptu (przy użyciu sourcelub . scriptname.shi używać tych funkcji tak, jakby były w nowym skrypcie.
SnakeDoc 30.09.16
To już zostało omówione w innej odpowiedzi.
Tomasz
1
Doceniam to. Ale wolałbym, żeby inni też byli ważni.
Tomasz
7
Dzisiaj spotkałem się ze sprawą, w której musiałem przekierować część wyniku skryptu do pliku (aby wysłać go pocztą e-mail) zamiast echa. Po prostu musiałem wykonać myFunction >> myFile, aby przekierować wyjście żądanych funkcji. Całkiem wygodne. Może mieć znaczenie.
Etsitpab Nioliv
39

W moim komentarzu wspomniałem o trzech zaletach funkcji:

  1. Łatwiej je przetestować i zweryfikować poprawność.

  2. Funkcje mogą być łatwo ponownie wykorzystane (pozyskane) w przyszłych skryptach

  3. Twój szef je lubi.

I nigdy nie lekceważ znaczenia liczby 3.

Chciałbym rozwiązać jeszcze jeden problem:

... więc możliwość arbitralnej zamiany kolejności uruchamiania nie jest czymś, co ogólnie robilibyśmy. Na przykład, nie będzie nagle chcesz umieścić declare_variablespo walk_into_bar, to by złamać rzeczy.

Aby skorzystać z podziału kodu na funkcje, należy starać się, aby funkcje były jak najbardziej niezależne. Jeśli walk_into_barwymaga zmiennej, która nie jest używana gdzie indziej, to zmienna ta powinna zostać zdefiniowana w lokalizacji lokalnej walk_into_bar. Proces dzielenia kodu na funkcje i minimalizowania ich wzajemnych zależności powinien sprawić, że kod będzie wyraźniejszy i prostszy.

Idealnie byłoby, gdyby funkcje były łatwe do przetestowania indywidualnie. Jeśli z powodu interakcji nie są one łatwe do przetestowania, oznacza to, że mogą skorzystać z refaktoryzacji.

John1024
źródło
Twierdziłbym, że czasem rozsądne jest modelowanie i egzekwowanie tych zależności, a nie refaktoryzacja, aby ich uniknąć (ponieważ jeśli jest ich wystarczająco dużo i są wystarczająco owłosione, może to prowadzić do przypadku, w którym rzeczy nie są już modulowane na w ogóle funkcje). Bardzo skomplikowany przypadek użycia kiedyś zainspirował do tego właśnie framework .
Charles Duffy,
4
To, co należy podzielić na funkcje, powinno być, ale przykład posuwa się za daleko. Myślę, że jedyną, która naprawdę mnie wkurza, jest funkcja deklaracji zmiennej. Zmienne globalne, zwłaszcza statyczne, powinny być zdefiniowane globalnie w komentowanej części poświęconej temu celowi. Zmienne dynamiczne powinny być lokalne dla funkcji, które ich używają i modyfikują.
Xalorous,
@Xalorous Widziałem praktyki, w których zmienne globalne są inicjowane w procedurze, jako pośredni i szybki krok przed opracowaniem procedury, która odczytuje ich wartość z zewnętrznego pliku ... Zgadzam się, że oddzielne zdefiniowanie i zdefiniowanie inicjalizacja, ale rzadko trzeba zginać się, aby przejść do przewagi numer 3;-)
Hastur
13

Dzielisz kod na funkcje z tego samego powodu, dla którego robisz to dla C / C ++, Pythona, Perla, Ruby lub innego kodu języka programowania. Głębszym powodem jest abstrakcja - hermetyzujesz zadania niższego poziomu w operacje podstawowe wyższego poziomu (funkcje), abyś nie musiał niepokoić się tym, jak się to robi. W tym samym czasie kod staje się bardziej czytelny (i łatwy do utrzymania), a logika programu staje się bardziej przejrzysta.

Jednak patrząc na twój kod, wydaje mi się dość dziwne mieć funkcję deklarowania zmiennych; to naprawdę sprawia, że ​​podnoszę brew.

przeciwdziałanie
źródło
Niedoceniana odpowiedź IMHO. Czy sugerujesz zatem zadeklarowanie zmiennych w mainfunkcji / metodzie?
David Tabernero M.
12

Chociaż całkowicie zgadzam się z możliwością ponownego użycia , czytelnością i delikatnym całowaniem szefów, ale jest jeszcze jedna zaleta funkcji w : zmienny zakres . Jak pokazuje LDP :

#!/bin/bash
# ex62.sh: Global and local variables inside a function.

func ()
{
  local loc_var=23       # Declared as local variable.
  echo                   # Uses the 'local' builtin.
  echo "\"loc_var\" in function = $loc_var"
  global_var=999         # Not declared as local.
                         # Therefore, defaults to global. 
  echo "\"global_var\" in function = $global_var"
}  

func

# Now, to see if local variable "loc_var" exists outside the function.

echo
echo "\"loc_var\" outside function = $loc_var"
                                      # $loc_var outside function = 
                                      # No, $loc_var not visible globally.
echo "\"global_var\" outside function = $global_var"
                                      # $global_var outside function = 999
                                      # $global_var is visible globally.
echo                      

exit 0
#  In contrast to C, a Bash variable declared inside a function
#+ is local ONLY if declared as such.

Nie widzę tego bardzo często w skryptach powłoki w świecie rzeczywistym, ale wydaje się, że to dobry pomysł na bardziej złożone skrypty. Zmniejszenie spójności pomaga uniknąć błędów, gdy blokujesz zmienną oczekiwaną w innej części kodu.

Wielokrotnego użytku często oznacza utworzenie wspólnej biblioteki funkcji i umieszczenie sourcetej biblioteki we wszystkich skryptach. To nie pomoże im biegać szybciej, ale pomoże ci je pisać szybciej.

pisklęta
źródło
Niewiele osób używa tego wprost local, ale myślę, że większość ludzi piszących skrypty podzielone na funkcje nadal przestrzega zasady projektowania. Usign localtylko utrudnia wprowadzanie błędów.
Voo,
localudostępnia zmienne dla funkcji i jej potomków, więc naprawdę miło jest mieć zmienną, która może być przekazywana z funkcji A, ale niedostępna dla funkcji B, która może chcieć mieć zmienną o tej samej nazwie, ale o innym celu. To dobrze, aby zdefiniować zakres i, jak powiedział Voo - mniej błędów
Sergiy Kolodyazhnyy,
10

Zupełnie inny powód niż te, które podano już w innych odpowiedziach: jednym z powodów, dla których czasami stosuje się tę technikę, w której jedynym wezwaniem do braku definicji funkcji na najwyższym poziomie jest wywołanie main, aby upewnić się, że skrypt nie zrobi nic nieprzyjemnego jeśli skrypt jest obcięty. Skrypt może zostać obcięty, jeśli jest przesyłany strumieniowo z procesu A do procesu B (powłoki), a proces A kończy się z dowolnego powodu, zanim zakończy pisanie całego skryptu. Jest to szczególnie prawdopodobne, jeśli proces A pobierze skrypt ze zdalnego zasobu. Chociaż ze względów bezpieczeństwa nie jest to dobry pomysł, jest to coś, co zostało zrobione, a niektóre skrypty zostały zmodyfikowane, aby przewidzieć problem.

hvd
źródło
5
Ciekawy! Ale niepokoi mnie to, że trzeba dbać o te rzeczy w każdym z programów. Z drugiej strony, dokładnie ten main()wzorzec jest zwykle w Pythonie, gdzie używa się go if __name__ == '__main__': main()na końcu pliku.
Martin Ueding
1
Idiom python ma tę zaletę, że pozwala innym skryptom importna bieżący skrypt bez uruchamiania main. Podejrzewam, że podobny strażnik mógłby zostać umieszczony w skrypcie bash.
Jake Cobb
@Jake Cobb Tak. Robię to teraz we wszystkich nowych skryptach bash. Mam skrypt, który zawiera podstawową infrastrukturę funkcji używanych przez wszystkie nowe skrypty. Ten skrypt może być pozyskiwany lub wykonywany. W przypadku pozyskania jego główna funkcja nie jest wykonywana. Wykrywanie źródła vs wykonanie odbywa się poprzez fakt, że BASH_SOURCE zawiera nazwę skryptu wykonawczego. Jeśli jest taki sam jak skrypt podstawowy, skrypt jest wykonywany. W przeciwnym razie jest pozyskiwany.
DocSalvager,
7

Proces wymaga sekwencji. Większość zadań jest sekwencyjna. Nie ma sensu mieszać się z zamówieniem.

Ale najważniejszą rzeczą w programowaniu - w tym w skryptach - jest testowanie. Testowanie, testowanie, testowanie. Jakie skrypty testowe musisz obecnie zweryfikować poprawność swoich skryptów?

Twój szef próbuje cię od bycia skrypciarzem do bycia programistą. To dobry kierunek, aby wejść. Ludzie, którzy przyjdą po tobie, polubią cię.

ALE. Zawsze pamiętaj o zorientowanych na proces korzeniach. Jeśli sensowne jest uporządkowanie funkcji w kolejności, w jakiej są one zwykle wykonywane, zrób to, przynajmniej w pierwszym przejściu.

Później zobaczysz, że niektóre funkcje obsługują dane wejściowe, inne dane wyjściowe, inne przetwarzają, inne modelują dane i inne manipulują danymi, więc może być mądre grupowanie podobnych metod, być może nawet przenoszenie ich do osobnych plików .

Później możesz zdać sobie sprawę, że napisałeś biblioteki małych funkcji pomocniczych, których używasz w wielu swoich skryptach.

Dewi Morgan
źródło
6

Komentarze i odstępy nie mogą zbliżyć się do czytelności, jaką mogą pełnić funkcje, co pokażę. Bez funkcji nie widać lasu dla drzew - duże problemy kryją się w wielu liniach szczegółów. Innymi słowy, ludzie nie mogą jednocześnie skupiać się na drobnych szczegółach i dużym obrazie. To może nie być oczywiste w krótkim skrypcie; dopóki jest krótki, może być wystarczająco czytelny. Oprogramowanie staje się jednak większe, a nie mniejsze, a na pewno jest częścią całego systemu oprogramowania Twojej firmy, który z pewnością jest znacznie większy, prawdopodobnie miliony linii.

Zastanów się, czy dałem ci takie instrukcje:

Place your hands on your desk.
Tense your arm muscles.
Extend your knee and hip joints.
Relax your arms.
Move your arms backwards.
Move your left leg backwards.
Move your right leg backwards.
(continue for 10,000 more lines)

Gdy dotrzesz do połowy, a nawet 5%, zapomnisz, jakie były pierwsze kroki. Nie można było dostrzec większości problemów, ponieważ nie można było zobaczyć lasu dla drzew. Porównaj z funkcjami:

stand_up();
walk_to(break_room);
pour(coffee);
walk_to(office);

Jest to z pewnością dużo bardziej zrozumiałe, bez względu na to, ile komentarzy możesz umieścić w kolejnej wersji linia po linii. Dzięki temu znacznie bardziej prawdopodobne jest, że zauważysz, że zapomniałeś zrobić kawę i prawdopodobnie zapomniałeś sit_down () na końcu. Kiedy myślisz o szczegółach wyrażeń grep i awk, nie możesz myśleć o dużym obrazie - „co, jeśli nie zrobi się kawy”?

Funkcje przede wszystkim pozwalają zobaczyć duży obraz i zauważyć, że zapomniałeś zrobić kawę (lub że ktoś może preferować herbatę). Innym razem, mając inne zdanie, martwisz się szczegółową implementacją.

Oczywiście są też inne korzyści omówione w innych odpowiedziach. Kolejną korzyścią, która nie została wyraźnie wymieniona w innych odpowiedziach, jest to, że funkcje dają gwarancję, która jest ważna w zapobieganiu i naprawianiu błędów. Jeśli odkryjesz, że jakaś zmienna $ foo w odpowiedniej funkcji walk_to () była błędna, wiesz, że wystarczy spojrzeć na pozostałe 6 wierszy tej funkcji, aby znaleźć wszystko, na co mógł mieć wpływ ten problem, i wszystko, co mogło spowodował, że to źle. Bez (właściwych) funkcji wszystko i wszystko w całym systemie może być przyczyną niepoprawności $ foo, a wszystko i wszystko może mieć wpływ na $ foo. Dlatego nie możesz bezpiecznie naprawić $ foo bez ponownego zbadania każdej linii programu. Jeśli $ foo jest lokalny dla funkcji,

Ray Morris
źródło
1
To nie jest bashskładnia. Szkoda; Nie sądzę, że istnieje sposób na przekazanie danych wejściowych do takich funkcji. (tj. pour();< coffee). Wygląda bardziej jak c++lub php(myślę).
głosy
2
@ tjt263 bez nawiasów, to składnia bash: wlej kawę. W przypadku parens jest to prawie każdy inny język. :)
Ray Morris
5

Kilka istotnych truizmów dotyczących programowania:

  • Twój program się zmieni, nawet jeśli szef nalega, że ​​tak nie jest.
  • Tylko kod i dane wejściowe wpływają na zachowanie programu.
  • Nazewnictwo jest trudne.

Komentarze zaczynają się od przerwy, ponieważ nie są w stanie jasno wyrazić swoich pomysłów w kodzie *, a wraz ze zmianą stają się gorzej (lub po prostu źle). Dlatego, jeśli to w ogóle możliwe, wyrażaj pojęcia, struktury, rozumowanie, semantykę, przepływ, obsługę błędów i wszystko inne, co ma znaczenie dla zrozumienia kodu jako kodu.

To powiedziawszy, funkcje Bash mają pewne problemy, których nie znaleziono w większości języków:

  • Przestrzeń nazw w Bash jest okropna. Na przykład zapomnienie użycia localsłowa kluczowego powoduje zanieczyszczenie globalnej przestrzeni nazw.
  • Używanie local foo="$(bar)"powoduje utratę kodu wyjściabar .
  • Nie ma nazwanych parametrów, więc musisz pamiętać, co "$@"oznacza w różnych kontekstach.

* Przepraszam, jeśli to obraża, ale po użyciu komentarzy przez kilka lat i rozwijaniu się bez nich ** przez kolejne lata jest całkiem jasne, co jest lepsze.

** Używanie komentarzy do licencjonowania, dokumentacji API i tym podobnych jest nadal konieczne.

l0b0
źródło
Ustawić niemal wszystkich zmiennych lokalnych, oświadczając im null na początku funkcji ... local foo=""Potem ustawiając je za pomocą wykonanie polecenia do działania w wyniku ... foo="$(bar)" || { echo "bar() failed"; return 1; }. To szybko wyciąga nas z funkcji, gdy nie można ustawić wymaganej wartości. Nawiasy klamrowe są konieczne, aby zapewnić, że return 1jest wykonywany tylko w przypadku awarii.
DocSalvager,
5

Czas to pieniądz

Istnieją inne dobre odpowiedzi, które rzucają światło na techniczne przyczyny napisania modułowego skryptu, potencjalnie długiego, opracowanego w środowisku pracy, opracowanego do użytku przez grupę osób i nie tylko na własny użytek.

Chcę się skupić na jednym oczekiwaniu: w środowisku pracy „czas to pieniądz” . Zatem brak błędów i wydajność twojego kodu są oceniane wraz z czytelnością, testowalnością, łatwością konserwacji , refaktowalności , możliwości ponownego użycia ...

Napisanie w „modułach” kodu skróci czas czytania potrzebny nie tylko samemu programistowi , ale nawet czasowi używanemu przez testerów lub szefa. Ponadto zauważ, że czas szefa jest zwykle płacony więcej niż czas programisty i że szef oceni jakość twojej pracy.

Ponadto pisanie w niezależnych „modułach” kodu (nawet skryptu bash) pozwoli ci pracować „równolegle” z innym komponentem twojego zespołu, skracając całkowity czas produkcji i wykorzystując, w najlepszym wypadku, wiedzę specjalistyczną singla, do przeglądu lub przepisania części brak efektu ubocznego na innych, aby przetworzyć kod, który właśnie napisałeś „tak, jak jest”dla innego programu / skryptu, aby utworzyć biblioteki (lub biblioteki fragmentów), aby zmniejszyć ogólny rozmiar i powiązane prawdopodobieństwo błędów, aby debugować i dokładnie przetestować każdą część ... i oczywiście zorganizuje w logicznej sekcji twój program / skrypt i zwiększ jego czytelność. Wszystkie rzeczy, które pozwolą zaoszczędzić czas i pieniądze. Wadą jest to, że musisz trzymać się standardów i komentować swoje funkcje (które jednak musisz robić w środowisku pracy).

Przestrzeganie standardu spowolni pracę na początku, ale później przyspieszy pracę wszystkich innych (i ciebie). Rzeczywiście, gdy współpraca wzrasta, liczba zaangażowanych osób staje się nieuniknioną potrzebą. Na przykład, nawet jeśli uważam, że zmienne globalne muszą być zdefiniowane globalnie, a nie w funkcji, rozumiem standard, który inicjuje je w funkcji o nazwie declare_variables()wywoływanej zawsze w pierwszym wierszu main()jednej ...

Na koniec, nie lekceważ możliwości współczesnych edytorów kodu źródłowego do pokazywania lub ukrywania selektywnie oddzielnych procedur ( zwijanie kodu ). Pozwoli to zachować kompaktowy kod i skoncentrować użytkownika na oszczędności czasu.

wprowadź opis zdjęcia tutaj

Powyżej widać, jak rozkłada się tylko walk_into_bar()funkcja. Nawet pozostałe z nich miały po 1000 linii każda, nadal można było kontrolować cały kod na jednej stronie. Zauważ, że jest on złożony nawet w sekcji, w której udajesz się zadeklarować / zainicjować zmienne.

Hastur
źródło
1

Oprócz powodów podanych w innych odpowiedziach:

  1. Psychologia: programista, którego produktywność jest mierzona w liniach kodu, będzie zachęcany do pisania niepotrzebnie pełnego kodu. Im bardziej kierownictwo koncentruje się na liniach kodu, tym większa zachęta dla programisty do rozszerzenia kodu z niepotrzebną złożonością. Jest to niepożądane, ponieważ zwiększona złożoność może prowadzić do wzrostu kosztów konserwacji i zwiększonego wysiłku wymaganego do naprawy błędów.
agc
źródło
Nie jest to taka zła odpowiedź, jak mówią głosujący. Uwaga: agc mówi, że jest to również możliwe i tak jest. Nie twierdzi, że byłaby to jedyna możliwość, i nie oskarża nikogo, jedynie podaje fakty. Chociaż myślę, że dziś prawie nie jest słyszane bezpośrednie zlecenie w stylu „linii kodu” -> „$$”, pośrednio oznacza, że ​​dość powszechne jest, że tak, masa wytworzonego kodu jest liczona przez liderów / szefów.
user259412,
0

Innym często pomijanym powodem jest analiza składni bash:

set -eu

echo "this shouldn't run"
{
echo "this shouldn't run either"

Ten skrypt oczywiście zawiera błąd składniowy i bash nie powinien go wcale uruchamiać, prawda? Źle.

~ $ bash t1.sh
this shouldn't run
t1.sh: line 7: syntax error: unexpected end of file

Gdybyśmy owinęli kod w funkcję, tak by się nie stało:

set -eu

main() {
  echo "this shouldn't run"
  {
  echo "this shouldn't run either"
}

main
~ $ bash t1.sh
t1.sh: line 10: syntax error: unexpected end of file
Hubert Grzeskowiak
źródło