Jak stabilne są powłoki uniksowe „stdin / stdout API”?

20

grepping, awking, sedding i piping są codzienną rutyną użytkownika dowolnego systemu operacyjnego podobnego do Uniksa, może to być wiersz poleceń lub skrypt skryptowy ( odtąd zwane zbiorczo filtrami ).

W ich istocie, podczas pracy ze „standardowymi” programami Unix CLI i wbudowanymi powłokami (odtąd zwane zbiorczo komendami ), filtry potrzebują dokładnego oczekiwanego formatu dla stdin, stdout i stderr w każdym kroku filtra, aby działać poprawnie. Ten dokładny oczekiwany format niektórych poleceń nazywam API tego polecenia poniżej.

Jako osoba z doświadczeniem w tworzeniu stron internetowych porównuję ten rodzaj gromadzenia i przetwarzania danych technicznie ze skrobaniem stron internetowych - techniką, która jest bardzo niestabilna za każdym razem, gdy pojawia się najmniejsza zmiana w prezentacji danych.

Moje pytanie dotyczy teraz stabilności interfejsów API poleceń Uniksa.

  1. Czy polecenia w systemach operacyjnych typu Unix są zgodne z formalną normalizacją w odniesieniu do ich danych wejściowych i wyjściowych?
  2. Czy w historii zdarzały się przypadki, w których aktualizacje niektórych ważnych poleceń powodowały uszkodzenie funkcjonalności jakiegoś filtra zbudowanego przy użyciu starszej wersji tego polecenia?
  3. Czy z czasem dojrzewały polecenia Unixa, że ​​zmiana w taki sposób, że jakiś filtr może się zepsuć, jest absolutnie niemożliwa?
  4. W przypadku, gdy filtry mogą się od czasu do czasu zepsuć z powodu zmiany interfejsów API poleceń, jak mogę, jako programista, chronić moje filtry przed tym problemem?
Abdull
źródło

Odpowiedzi:

17

Standard POSIX 2008 zawiera sekcję opisującą „Shell and Utilities” . Ogólnie rzecz biorąc, jeśli trzymasz się tego, twoje skrypty powinny być dość przyszłościowe, z wyjątkiem ewentualnych wycofań, ale te prawie nie zdarzają się z dnia na dzień, więc powinieneś mieć dużo czasu na aktualizację swoich skryptów.

W niektórych przypadkach, gdy format wyjściowy dla jednego narzędzia różni się znacznie w zależności od platformy i wersji, standard POSIX może zawierać opcję zwykle wywoływaną -plub -Pokreślającą gwarantowany i przewidywalny format wyjściowy. Przykładem tego jest timenarzędzie , które ma bardzo różne implementacje. Jeśli potrzebujesz stabilnego formatu API / wyjściowego, skorzystałbyś time -p.

Jeśli potrzebujesz użyć narzędzia filtrującego, które nie jest objęte standardem POSIX, to w zasadzie jesteś na łasce programów pakujących / programistów, tak jak na zdalnych programistach internetowych, kiedy robisz skrobanie sieci.

jw013
źródło
12

Spróbuję odpowiedzieć na podstawie mojego doświadczenia.

  1. Polecenia tak naprawdę nie są zgodne z formalną specyfikacją, ale są zgodne z wymogiem zużycia i generowania tekstu zorientowanego liniowo.

  2. Tak oczywiście. Zanim narzędzia GNU stały się de facto standardem, wielu sprzedawców miało dziwaczne wyniki, szczególnie w odniesieniu do psi ls. To spowodowało wiele bólu. Dzisiaj tylko HP dostarcza super dziwaczne polecenia. Historycznie narzędzia Berkeley Software Distribution (BSD) stanowiły poważny przełom w przeszłości. Specyfikacja POSIX była zerwaniem z przeszłością, ale teraz jest powszechnie akceptowana.

  3. Z czasem polecenia uniksowe rzeczywiście dojrzewały. Nadal nie jest niemożliwe złamanie skryptu napisanego dla starszej wersji. Pomyśl o najnowszych trendach w kierunku UTF-8 jako kodowania plików tekstowych. Ta zmiana wymagała zmiany podstawowych narzędzi, takich jak tr. W przeszłości prosty tekst był prawie zawsze ASCII (lub czymś zbliżonym), więc wielkie litery tworzyły zakres liczbowy, podobnie jak małe litery. Nie jest to już prawdą w przypadku UTF-8, więc trmożna zaakceptować różne opcje wiersza poleceń, aby określić takie rzeczy, jak „wielkie litery” lub „alfanumeryczny”.

  4. Jednym z najlepszych sposobów „wzmocnienia” filtrów jest nie zależenie od konkretnego układu tekstu. Na przykład nie rób cut -c10-24, co zależy od pozycji linii. cut -f2Zamiast tego użyj , aby wyciąć drugie pole rozdzielone tabulatorami. awkdzieli dowolny wiersz wejściowy na 1 $, 2 $, 3 $ ... które są domyślnie oddzielone spacją. Zależy od koncepcji wyższego poziomu, takich jak „pola”, niż koncepcji niższego poziomu, takich jak pozycja kolumny. Ponadto używaj wyrażeń regularnych: sedi awkoba mogą robić rzeczy z wyrażeniami regularnymi, które nie dbają o pewną wariancję danych wejściowych. Inną sztuczką jest przetworzenie danych wejściowych na coś, czego formatem może być wybredny filtr. Służy tr -cs '[a-zA-z0-9]' '[\n]'do dzielenia tekstu na pojedyncze słowo w wierszu bez interpunkcji. Po prostu nie

Bruce Ediger
źródło
9

Po pierwsze, bardzo krótkie odpowiedzi na twoje pytania:

  1. Formalna standaryzacja konwencji wejścia / wyjścia: nie
  2. Awaria w przeszłości ze względu na zmieniającą się wydajność: tak
  3. Absolutnie niemożliwe jest złamanie przyszłych filtrów: nie
  4. Jak uchronić się przed zmianami: bądź konserwatywny

Kiedy mówisz „API”, używasz terminu, który (na dobre lub na złe) oznacza zbyt wiele formalności wokół konwencji filtrów wejścia / wyjścia. Bardzo (i mam na myśli „bardzo”) ogólnie, podstawowe konwencje dla danych, które można łatwo filtrować, to

  • każda linia wejściowa jest kompletnym zapisem
  • w każdym rekordzie pola są oddzielone znanym znakiem separatora

Klasycznym przykładem może być format / etc / passwd. Ale te domyślne konwencje są prawdopodobnie naruszane do pewnego stopnia częściej niż są przestrzegane w liście.

  • Istnieje wiele filtrów (często napisanych w awk lub perl), które analizują wielowierszowe formaty wejściowe.
  • Istnieje wiele wzorców wprowadzania (np. / Var / log / messages), w których nie ma dobrze zdefiniowanej struktury pola, i należy zastosować bardziej ogólne techniki oparte na wyrażeniach regularnych.

Twoje czwarte pytanie, jak uchronić się przed zmianami struktury wyjściowej, jest naprawdę jedynym, na które możesz cokolwiek poradzić.

  • Jak powiedział @ jw013 , spójrz na to, co mówią standardy POSIX . Oczywiście posix nie określa wszystkich poleceń, których chcesz użyć jako źródła danych wejściowych.
  • Jeśli chcesz, aby twoje skrypty były przenośne, staraj się unikać osobliwości dowolnej wersji polecenia, które zdarzyło się, że nie jest zainstalowane. Na przykład wiele wersji GNU standardowych poleceń unix ma niestandardowe rozszerzenia. Mogą być przydatne, ale należy unikać ich, jeśli chcesz mieć maksymalną przenośność.
  • Spróbuj dowiedzieć się, jakie podzbiory argumentów poleceń i formatów wyjściowych są stabilne na różnych platformach. Niestety wymaga to dostępu do wielu platform wraz z czasem, ponieważ różnice te nie zostaną nigdzie zapisane, nawet nieformalnie.

W końcu nie możesz w pełni zabezpieczyć się przed problemami, o które się martwisz, i nie ma jednego miejsca, w którym można by znaleźć „definitywne” stwierdzenie, co powinno zrobić określone polecenie. W przypadku wielu skryptów powłoki, zwłaszcza tych napisanych do użytku osobistego lub na małą skalę, po prostu nie stanowi to problemu

Dale Hagglund
źródło
5

Dotyczy tylko 1) Twojego pytania.

Oczywiście interfejsy API mogą zawsze zmieniać się zgodnie z wolą ich twórców, a tym samym psować zależne oprogramowanie w dowolnym języku. To powiedziawszy, świetnym pomysłem „interfejsów” I / O narzędzi uniksowych jest to, że praktycznie nie ma żadnych (być może 0x0ana końcu linii). Dobry skrypt filtruje dane za pomocą narzędzi uniksowych zamiast je tworzyć. Oznacza to, że skrypt może się zepsuć, ponieważ zmieniono specyfikację wejścia lub wyjścia, ale nie dlatego, że zmienił się format we / wy (ponownie tak naprawdę nie ma jednego) poszczególnych narzędzi używanych w skrypcie (ponieważ coś, co tak naprawdę nie istnieje) tak naprawdę nie mogę się zmienić).

Przeglądając listę podstawowych narzędzi, jest kilka, które przypisałbym również producentowi , a nie tylko filtrowaniu:

  • wc - wypisuje liczbę bajtów, słów, wierszy - bardzo prosty format, więc absolutnie mało prawdopodobne, aby się zmienił, a ponadto mało prawdopodobne, aby był używany w skrypcie.
  • diff - ewoluowały różne formaty wyjściowe, ale nie słyszałem o żadnych problemach. Zwykle również nie jest używane bez nadzoru.
  • data - teraz naprawdę musimy zadbać o to, co produkujemy, szczególnie w odniesieniu do ustawień regionalnych systemu. Ale w przeciwnym razie format wyjściowy jest RFC, ponieważ nie określasz go dokładnie sam.
  • cal - nie rozmawiajmy o tym, wiem, że format wyjściowy bardzo różni się w zależności od systemu.
  • ls , który , w , ostatni - nie mogę pomóc, jeśli chcesz analizować ls, to po prostu było nie tak miało być. Ponadto, którzy, w końcu, są bardziej interaktywnymi listerami; Jeśli używasz ich w skrypcie, musisz uważać na to, co robisz.
  • czas został wskazany w innym poście. Ale tak, to jest tak samo jak z ls. Więcej do użytku interaktywnego / lokalnego. A wbudowane bash bardzo różni się od wersji GNU, a wersja GNU ma naprawione błędy od wielu lat. Tylko nie polegaj na tym.

Oto narzędzia, które oczekują, że określony format wejściowy będzie bardziej szczegółowy niż strumień bajtów:

  • bc , dc - kalkulatory. Już po bardziej hackerskiej stronie (naprawdę nie używam ich w skryptach) i przypuszczalnie bardzo stabilnych formatach I / O.

Istnieje inny obszar o znacznie wyższym ryzyku uszkodzenia, mianowicie interfejs wiersza poleceń. Większość narzędzi ma różne funkcje zarówno w systemach, jak i na osi czasu. Przykładami są

  • Wszystkie narzędzia używające wyrażeń regularnych - wyrażenia regularne mogą zmieniać znaczenie w zależności od ustawień regionalnych systemu (na przykład LC_COLLATE) i istnieje wiele subtelności i osobliwości w różnych implementacjach wyrażeń regularnych.
  • Po prostu nie używaj fantazyjnych przełączników. Możesz łatwo użyć man 1p findna przykład do odczytania strony podręcznika POSIX zamiast strony systemowej. W moim systemie potrzebuję zainstalowanego manpages-posix.

Nawet podczas korzystania z takich przełączników normalnie błędy nie zostaną subtelnie wprowadzone i nie zatruwają danych. Większość programów po prostu odmawia pracy z nieznanym przełącznikiem.

Podsumowując, powiedziałbym, że powłoka ma potencjał bycia jednym z najbardziej przenośnych języków (przenośny, gdy skrypt jest przenośny). Porównaj z ulubionymi językami skryptowymi, w których występują subtelne błędy, lub z ulubionym skompilowanym programem, który zostanie skompilowany.

Ponadto w rzadkich miejscach, w których może wystąpić uszkodzenie z powodu niezgodności, prawdopodobnie nie spowodowałoby to czasu, ale różnorodność w różnych systemach (co oznacza, że ​​jeśli działa ono dla Ciebie, zrobiłoby to 20 lat wcześniej i będzie za 20 lat , zbyt). Jest to następstwem prostoty narzędzi.

Jo So
źródło
1

Istnieją tylko de facto standardy we / wy - białe znaki i rozdzielane wartościami zerowymi.

Jeśli chodzi o kompatybilność, zwykle powracamy do sprawdzania numerów wersji poszczególnych filtrów. Nie dlatego, że wiele się zmieniają, ale jeśli chcesz użyć zupełnie nowej funkcji i nadal chcesz, aby skrypt działał na starszych wersjach, musisz jakoś „ifdef”. Praktycznie nie ma mechanizmu raportowania zdolności, z wyjątkiem ręcznego pisania przypadków testowych.

lynxlynxlynx
źródło
0

Skrypty łamią się, niektóre częściej niż inne. Stare i znane oprogramowanie ma tendencję do pozostawania na tym samym poziomie i często ma flagi zgodności, gdy i tak się zmienia.

Skrypty napisane w jednym systemie zwykle działają, ale często psują inny.

Alex Chamberlain
źródło