Nagroda nie była mi znana do dziś, kiedy jakiś debiutant próbował przypiąć mi UUOC za jedną z moich odpowiedzi. To było cat file.txt | grep foo | cut ... | cut ...
. Dałem mu trochę do myślenia i dopiero po tym, jak odwiedziłem ten link, podał mi, odnosząc się do pochodzenia nagrody i praktyki. Dalsze poszukiwania doprowadziły mnie do tego pytania. Niestety, pomimo świadomego przemyślenia, żadna z odpowiedzi nie zawierała mojego uzasadnienia.
Nie chciałem być defensywny, kiedy go wychowywałem. W końcu w młodszych latach pisałbym to polecenie, grep foo file.txt | cut ... | cut ...
ponieważ za każdym razem, gdy robisz częste single grep
, uczysz się umiejscowienia argumentu pliku i wiadomo, że pierwszy to wzorzec, a później nazwy plików.
Był to świadomy wybór, kiedy odpowiedziałem na pytanie z cat
prefiksem częściowo z powodu „dobrego smaku” (słowami Linusa Torvaldsa), ale głównie z ważnej przyczyny.
Ten drugi powód jest ważniejszy, dlatego przedstawię go na pierwszym miejscu. Kiedy oferuję rurociąg jako rozwiązanie, spodziewam się, że będzie można go ponownie wykorzystać. Jest całkiem prawdopodobne, że rurociąg zostanie dodany na końcu lub połączony z innym rurociągiem. W takim przypadku posiadanie argumentu pliku do grep psuje możliwość ponownego użycia i całkiem możliwe, że robi to po cichu bez komunikatu o błędzie, jeśli argument pliku istnieje. I. e. grep foo xyz | grep bar xyz | wc
da, ile w linie xyz
zawierają bar
natomiast spodziewasz liczbę wierszy, które zawierają zarówno foo
i bar
. Konieczność zmiany argumentów na polecenie w potoku przed jego użyciem jest podatna na błędy. Dodaj do tego możliwość cichych niepowodzeń, a stanie się to szczególnie podstępną praktyką.
Ten pierwszy powód nie jest również nieistotny, ponieważ wiele „dobrego smaku” jest jedynie intuicyjnym podświadomym uzasadnieniem rzeczy takich jak ciche niepowodzenia powyżej, o których nie można pomyśleć w chwili, gdy ktoś potrzebujący edukacji mówi „ale nie jest ten kot bezużyteczny ”.
Spróbuję jednak również uświadomić dawny powód „dobrego smaku”, o którym wspomniałem. Ten powód ma związek z duchem ortogonalnego projektowania Uniksa. grep
nie robi cut
i ls
nie robi grep
. Dlatego przynajmniej grep foo file1 file2 file3
stoi w sprzeczności z duchem designu. Ortogonalnym sposobem na to jest cat file1 file2 file3 | grep foo
. Teraz grep foo file1
jest to tylko szczególny przypadek grep foo file1 file2 file3
, a jeśli nie traktujesz go tak samo, przynajmniej zużywasz cykle zegara mózgowego, próbując uniknąć niepotrzebnej nagrody kota.
To prowadzi nas do argumentu, który grep foo file1 file2 file3
jest konkatenacją i cat
konkatenacją, więc jest to słuszne, cat file1 file2 file3
ale ponieważ cat
nie jest konkatenacja, cat file1 | grep foo
dlatego naruszamy ducha zarówno cat
wszechmogącego, jak i wszechmogącego. Cóż, gdyby tak było, Unix potrzebowałby innego polecenia, aby odczytać wyjście jednego pliku i wypluć go na standardowe wyjście (nie dzieląc go na strony ani nic innego, jak czystą pracę na standardowe wyjście). Tak więc miałbyś sytuację, w której mówisz cat file1 file2
lub mówisz dog file1
i sumiennie pamiętasz, aby unikać cat file1
unikania nagrody, jednocześnie unikając, dog file1 file2
ponieważ, miejmy nadzieję, projekt dog
będzie powodował błąd, jeśli podano wiele plików.
Mam nadzieję, że w tym momencie sympatyzujesz z projektantami uniksowymi, ponieważ nie zawarli osobnego polecenia wypluwania pliku na standardowe wyjście, jednocześnie nadając nazwę cat
konkatenacji zamiast nadawać jej inną nazwę. <edit>
jest taki pies, niefortunny <
operator. Niefortunne jest jego umieszczenie na końcu rurociągu, uniemożliwiając łatwą kompozycję. Nie ma syntaktycznie ani estetycznie czystego sposobu umieszczenia go na początku. Niestety nie jest wystarczająco ogólny, więc zaczynasz od psa, ale po prostu dodajesz inną nazwę pliku, jeśli chcesz, aby była przetwarzana po poprzedniej. (Z >
drugiej strony nie jest w połowie tak źle. Ma prawie idealne położenie na końcu. Zazwyczaj nie jest to część rurociągu wielokrotnego użytku, a zatem wyróżnia się symbolicznie.)</edit>
Kolejne pytanie brzmi: dlaczego ważne jest, aby mieć komendy, które jedynie plują na plik lub łączą kilka plików na standardowe wyjście, bez dalszego przetwarzania? Jednym z powodów jest uniknięcie posiadania przez każdą pojedynczą komendę Uniksa, która działa na standardowym wejściu, aby wiedzieć, jak parsować przynajmniej jeden argument pliku wiersza poleceń i używać go jako danych wejściowych, jeśli istnieje. Drugim powodem jest uniknięcie przez użytkowników konieczności pamiętania: (a) gdzie idą argumenty nazwy pliku; oraz (b) unikać cichego błędu potoku, jak wspomniano powyżej.
To prowadzi nas do tego, dlaczego grep
ma dodatkową logikę. Uzasadnieniem jest umożliwienie płynnej obsługi poleceń często używanych i autonomicznych (a nie potokowych). Jest to niewielki kompromis ortogonalności dla znacznego wzrostu użyteczności. Nie wszystkie polecenia powinny być zaprojektowane w ten sposób, a polecenia, które nie są często używane, powinny całkowicie unikać dodatkowej logiki argumentów pliku (pamiętaj, że dodatkowa logika prowadzi do niepotrzebnej niestabilności (możliwość błędu)). Wyjątkiem jest dopuszczenie argumentów pliku, tak jak w przypadku grep
. (nawiasem mówiąc, ls
ma zupełnie inny powód, aby nie tylko akceptować, ale wymagać argumentów pliku)
Wreszcie, lepiej byłoby, gdyby tak wyjątkowe polecenia, jak grep
(ale niekoniecznie ls
) generowały błąd, jeśli standardowe wejście jest dostępne. Jest to uzasadnione, ponieważ polecenia zawierają logikę, która narusza ortogonalnego ducha wszechmogącego Uniksa dla wygody użytkownika. Aby zwiększyć wygodę użytkownika, tj. Aby zapobiec cierpieniu spowodowanemu przez cichą awarię, takie polecenia nie powinny wahać się naruszyć własnego naruszenia poprzez powiadomienie użytkownika, jeśli istnieje możliwość cichej awarii.