Wiele osób korzysta z onelinerów i skryptów zawierających kod wzdłuż linii
cat "$MYFILE" | command1 | command2 > "$OUTPUT"
Pierwszy cat
jest często nazywany „bezużytecznym użyciem kota”, ponieważ technicznie wymaga on rozpoczęcia nowego procesu (często /usr/bin/cat
), w którym można by tego uniknąć, gdyby polecenie było
< "$MYFILE" command1 | command2 > "$OUTPUT"
ponieważ wtedy powłoka musi się tylko uruchomić command1
i po prostu wskazać stdin
na podany plik.
Dlaczego powłoka nie wykonuje tej konwersji automatycznie? Uważam, że składnia „bezużyteczne korzystanie z kota” jest łatwiejsza do odczytania, a powłoka powinna mieć wystarczającą ilość informacji, aby automatycznie pozbyć się bezużytecznego kota. Jest cat
on zdefiniowany w standardzie POSIX, więc powłoka powinna mieć możliwość implementacji go wewnętrznie zamiast używania ścieżki binarnej. Powłoka może nawet zawierać implementację tylko dla jednej wersji argumentu i wrócić do ścieżki binarnej.
źródło
lseek
jest nadal zdefiniowanym zachowaniem i może powodować inny wynik, różne zachowanie blokujące może mieć znaczenie semantyczne itp. Dopuszczalne byłoby dokonanie zmiany, gdybyś wiedział, jakie były inne polecenia i wiedziałeś, że ich to nie obchodzi, lub jeśli po prostu nie dbałeś o kompatybilność na tym poziomie, ale korzyść jest niewielka. Wyobrażam sobie, że brak korzyści napędza sytuację bardziej niż koszt zgodności.cat
lub użyć dowolnego innego narzędzia. Dozwolone jest również wiedzieć, jak działają inne narzędzia należące do systemu (np. Może wiedzieć, jak zachowuje się zewnętrznagrep
implementacja dostarczona z systemem ). Jest to całkowicie wykonalne, więc można się zastanawiać, dlaczego tak nie jest.grep
. Ised
… Iawk
… Idu
… A ile setek, jeśli nie tysiące innych narzędzi?Odpowiedzi:
2 polecenia nie są równoważne: rozważ obsługę błędów:
cat <file that doesn't exist> | less
utworzy pusty strumień, który zostanie przekazany do potokowego programu ... jako taki kończy się wyświetleniem niczego.< <file that doesn't exist> less
nie uda się otworzyć paska, a następnie w ogóle się nie otworzy.Próba zmiany tego pierwszego na drugi może spowodować uszkodzenie dowolnej liczby skryptów, które oczekują uruchomienia programu z potencjalnie pustym wejściem.
źródło
cat
zawsze wykona drugie polecenie w potoku, natomiast wariant z samym przekierowaniem wejścia w ogóle nie wykona polecenia, jeśli brakuje pliku wejściowego.<"missing-file" grep foo | echo 2
nie zostanie ono wykonane,grep
ale zostanie wykonaneecho
.„Bezużyteczne użycie
cat
” polega raczej na tym, jak piszesz kod, niż na tym, co faktycznie działa po uruchomieniu skryptu. Jest to swego rodzaju anty-wzór projektowy , sposób na osiągnięcie czegoś, co prawdopodobnie można by zrobić w bardziej wydajny sposób. Brak zrozumienia, jak najlepiej połączyć dane narzędzia, aby utworzyć nowe narzędzie. Twierdziłbym, że łączenie kilkused
i / lubawk
poleceń w potoku również czasami można uznać za objaw tego samego anty-wzorca.Naprawianie przypadków „bezużytecznego użycia
cat
” w skrypcie jest przede wszystkim kwestią ręcznej naprawy kodu źródłowego skryptu. Narzędzie takie jak ShellCheck może w tym pomóc, wskazując oczywiste przypadki:Uzyskiwanie powłoki automatycznie do wykonania tego zadania byłoby trudne ze względu na charakter skryptów powłoki. Sposób wykonania skryptu zależy od środowiska odziedziczonego po procesie nadrzędnym oraz od konkretnej implementacji dostępnych poleceń zewnętrznych.
Powłoka niekoniecznie wie, co
cat
jest. Może to być dowolne polecenie z dowolnego miejsca w Twojej$PATH
lub funkcja.Gdyby było to wbudowane polecenie (które może być w niektórych powłokach), miałoby możliwość reorganizacji potoku, tak jak wiedziałby o semantyce wbudowanego
cat
polecenia. Zanim to zrobi, będzie musiał dodatkowo założyć następne polecenie w potoku, po oryginalecat
.Zauważ, że odczyt ze standardowego wejścia zachowuje się nieco inaczej, gdy jest podłączony do potoku i gdy jest podłączony do pliku. Potok nie jest widoczny, więc w zależności od tego, co zrobi następne polecenie w potoku, może on zachowywać się inaczej, jeśli potok został zmieniony (może wykryć, czy wejście jest widoczne, i zdecydować o zrobieniu różnych rzeczy, jeśli tak jest lub jeśli tak nie jest, w każdym razie zachowałoby się inaczej).
To pytanie jest podobne (w bardzo ogólnym sensie) do „ Czy istnieją jakieś kompilatory, które próbują samodzielnie naprawić błędy składniowe? ” (W witrynie Software Engineering StackExchange), chociaż to pytanie oczywiście dotyczy błędów składniowych, a nie bezużytecznych wzorców projektowych . Pomysł automatycznej zmiany kodu na podstawie intencji jest jednak w dużej mierze taki sam.
źródło
cat
jest, a pozostałe polecenia w potoku (zasada as-if) i zachowują się odpowiednio, po prostu ich tu nie ma, ponieważ jest to bezcelowe i zbyt trudne.cat /dev/tty
jest to interesujący, z którym byłoby inaczej<
.cat
właściwie robi polecenie bez wykonania polecenia . Z tego co wiesz (i powłoka), OP ma nacat
swojej ścieżce polecenie, które jest interaktywną symulacją kota, „mój plik” jest tylko zapisanym stanem grycommand1
icommand2
przetwarza niektóre statystyki dotyczące bieżącej sesji gry ...Ponieważ to nie jest bezużyteczne.
W przypadku
cat file | cmd
fd0
(stdin)cmd
będzie potokiem, aw przypadkucmd <file
może być zwykłym plikiem, urządzeniem itp.Potok ma inną semantykę niż zwykły plik, a jego semantyka nie jest podzbiorem semantyki zwykłego pliku:
zwykły plik nie może być
select(2)
edytowany anipoll(2)
edytowany w znaczący sposób; aselect(2)
na nim zawsze zwróci „gotowy”. Zaawansowane interfejsy, takie jakepoll(2)
w systemie Linux, po prostu nie będą działać ze zwykłymi plikami.Linux nie ma połączenia systemowe (
splice(2)
,vmsplice(2)
,tee(2)
), które działają jedynie w rurach [1]Ponieważ
cat
jest tak często używany, można go zaimplementować jako wbudowaną powłokę, która pozwoli uniknąć dodatkowego procesu, ale kiedy zaczniesz na tej ścieżce, to samo można zrobić z większością poleceń - przekształcając powłokę w wolniejszą i clunkierperl
lubpython
. prawdopodobnie lepiej jest napisać inny język skryptowy z łatwą w użyciu składnią przypominającą potok dla kontynuacji ;-)[1] Jeśli chcesz prosty przykład nie składa się na tę okazję, można spojrzeć na mojego „exec binarny z stdin” git sens z kilku wyjaśnień w komentarzu tutaj . Wdrożenie
cat
w nim, aby działało bez UUoC, uczyniłoby go 2 lub 3 razy większym.źródło
cat
wewnętrznie.cat /dev/urandom | cpu_bound_program
uruchamiaread()
wywołania systemowe w osobnym procesie. Na przykład w systemie Linux rzeczywista praca procesora polegająca na generowaniu większej liczby liczb losowych (gdy pula jest pusta) odbywa się w tym wywołaniu systemowym, więc użycie osobnego procesu pozwala wykorzystać osobny rdzeń procesora do generowania losowych danych jako danych wejściowych. np. Jaki jest najszybszy sposób na wygenerowanie pliku tekstowego 1 GB zawierającego losowe cyfry?lseek
nie będzie działać.cat foo.mp4 | mpv -
będzie działać, ale nie możesz szukać dalej niż bufor bufora mpv lub mplayera. Ale z przekierowaniem danych wejściowych z pliku możesz.cat | mpv -
to jeden ze sposobów sprawdzenia, czy MP4 ma swójmoov
atom na początku pliku, więc można go odtwarzać bez szukania do końca i do tyłu (tj. czy nadaje się do przesyłania strumieniowego). Łatwo jest wyobrazić sobie inne przypadki, w których chcesz przetestować program pod kątem niewidzialnych plików, uruchamiając go/dev/stdin
zcat
przekierowaniem.xargs cat | somecmd
. Jeśli ścieżki plików wykraczają poza limit bufora poleceń,xargs
mogą być uruchamianecat
wiele razy, co powoduje ciągły strumień, axargs somecmd
bezpośrednie użycie często kończy się niepowodzeniem, ponieważsomecmd
nie można uruchomić ich w wielu miejscach, aby uzyskać płynny wynik.Ponieważ wykrycie bezużytecznego kota jest naprawdę bardzo trudne.
Miałem skrypt powłoki, w którym pisałem
Skrypt powłoki nie powiódł się w produkcji, jeśli
cat
został usunięty, ponieważ został wywołany przezsu -c 'script.sh' someuser
. Pozornie zbędnecat
spowodowało, że właściciel standardowego wejścia zmienił się na użytkownika, który działał skrypt, tak że ponowne otwarcie go/proc
działało.źródło
cat
po którym następuje dokładnie jeden parametr, więc powłoka powinna używać prawdziwegocat
pliku wykonywalnego zamiast zoptymalizowanego skrótu. Warto jednak zwrócić uwagę na możliwe inne dane uwierzytelniające lub niestandardowe standardowe wejście dla rzeczywistych procesów.tl; dr: Pociski nie robią tego automatycznie, ponieważ koszty przekraczają prawdopodobne korzyści.
Inne odpowiedzi wskazywały na różnicę techniczną między stdin będącym potokiem a byciem plikiem. Mając to na uwadze, powłoka może wykonać jedną z następujących czynności:
cat
jako wbudowane, nadal zachowując rozróżnienie pliku v. Potoku. Pozwoliłoby to zaoszczędzić koszty egzekucji i być może rozwidlenia.Następnie musisz wziąć pod uwagę koszty i korzyści każdego podejścia. Korzyści są dość proste:
cat
)Dzięki temu oszczędzasz trochę czasu procesora i pamięci, zwłaszcza jeśli możesz uniknąć rozwidlenia. Oczywiście oszczędzasz ten czas i pamięć tylko wtedy, gdy funkcja jest rzeczywiście używana. I tak naprawdę oszczędzasz tylko czas na rozwidlenie / wykonanie; w przypadku większych plików czas to głównie czas operacji we / wy (tzn. kot czyta plik z dysku). Musisz więc zapytać: jak często jest
cat
używany (bezużytecznie) w skryptach powłoki, w których wydajność ma znaczenie? Porównaj go z innymi popularnymi wbudowanymi powłokami, takimi jaktest
- trudno sobie wyobrazić, żecat
jest używany (bezużytecznie) nawet z jedną dziesiątą tak często, jaktest
w miejscach, które mają znaczenie. Zgaduję, że nie zmierzyłem, co chciałbyś zrobić przed każdą próbą wdrożenia. (Lub podobnie, prosząc kogoś innego o wdrożenie np. Żądania funkcji).Następnie pytasz: jakie są koszty. Dwa koszty, które przychodzą na myśl, to (a) dodatkowy kod w powłoce, który zwiększa jej rozmiar (a tym samym możliwe użycie pamięci), wymaga więcej prac konserwacyjnych, jest kolejnym miejscem na błędy itp .; oraz (b) zaskakuje kompatybilność wsteczna, POSIX
cat
pomija wiele funkcji np. GNU coreutilscat
, więc musisz uważać dokładnie, cocat
wbudowałoby.Dodatkowa wbudowana opcja prawdopodobnie nie jest taka zła - dodanie jeszcze jednego wbudowanego, w którym już istnieje kilka. Gdybyś miał dane profilujące, które by to pomogły, prawdopodobnie możesz przekonać autorów ulubionej powłoki, aby je dodali.
Jeśli chodzi o analizę potoku, nie sądzę, aby powłoki robiły obecnie coś takiego (kilka rozpoznaje koniec potoku i może uniknąć rozwidlenia). Zasadniczo dodajesz do powłoki (prymitywny) optymalizator; Optymalizatory często okazują się skomplikowanym kodem i źródłem wielu błędów. Te błędy mogą być zaskakujące - niewielkie zmiany w skrypcie powłoki mogą zakończyć się uniknięciem lub wywołaniem błędu.
Postscript: Możesz zastosować podobną analizę do swoich bezużytecznych zastosowań kota. Korzyści: łatwiejszy do odczytania (chociaż jeśli polecenie1 weźmie plik jako argument, prawdopodobnie nie). Koszty: dodatkowe rozwidlenie i exec (a jeśli polecenie1 może przyjąć plik jako argument, prawdopodobnie bardziej mylące komunikaty o błędach). Jeśli z analizy wynika, że bezużytecznie używasz kota, to śmiało.
źródło
cat
Komenda może zaakceptować-
jako marker stdin . ( POSIX , „ Jeśli plik ma wartość„ - ”, narzędzie cat odczytuje ze standardowego wejścia w tym punkcie sekwencji. ”) Pozwala to na prostą obsługę pliku lub standardowego wejścia, w przeciwnym razie byłoby to niedozwolone.Rozważ te dwie trywialne alternatywy, w których argumentem powłoki
$1
jest-
:Kolejny czas
cat
jest przydatny, gdy jest celowo używany jako brak operacji w celu utrzymania składni powłoki:Wreszcie, uważam, że jedyny raz, kiedy UUOC można naprawdę poprawnie wywołać, jest
cat
użycie nazwy pliku, która jest znana jako zwykły plik (tj. Nie jest urządzeniem ani nazwaną potokiem) i że komenda nie otrzymuje żadnych flag:W każdej innej sytuacji
cat
mogą być wymagane same właściwości .źródło
Polecenie cat może robić rzeczy, których muszla niekoniecznie musi robić (a przynajmniej nie może robić łatwo). Załóżmy na przykład, że chcesz wydrukować znaki, które w innym przypadku byłyby niewidoczne, takie jak tabulatory, znaki powrotu karetki lub znaki nowej linii. * Może * być na to sposób, używając tylko poleceń wbudowanych w powłokę, ale nie mogę wymyślić żadnego z góry. Wersja cat GNU może to robić za pomocą
-A
argumentów lub-v -E -T
argumentów (chociaż nie wiem o innych wersjach cat). Możesz również poprzedzić każdą linię numerem linii za pomocą-n
(ponownie IDK, jeśli wersje inne niż GNU mogą to zrobić).Kolejną zaletą cat jest to, że może on z łatwością czytać wiele plików. Aby to zrobić, wystarczy wpisać
cat file1 file2 file3
. Aby zrobić to samo z powłoką, sytuacja stałaby się trudna, chociaż starannie wykonana pętla najprawdopodobniej mogłaby osiągnąć ten sam wynik. To powiedziawszy, czy naprawdę chcesz poświęcić czas na napisanie takiej pętli, skoro istnieje taka prosta alternatywa? Ja nie!Czytanie plików za pomocą cat prawdopodobnie zużyłoby mniej procesora niż powłoka, ponieważ cat jest programem wstępnie skompilowanym (oczywistym wyjątkiem jest każda powłoka, która ma wbudowanego cat). Podczas czytania dużej grupy plików może to stać się oczywiste, ale nigdy tego nie zrobiłem na moich komputerach, więc nie mogę być tego pewien.
Polecenie cat może być również przydatne do zmuszania polecenia do zaakceptowania standardowego wejścia w przypadkach, w których może nie być. Rozważ następujące:
echo 8 | sleep
Liczba „8” nie zostanie zaakceptowana przez polecenie „uśpienia”, ponieważ tak naprawdę nigdy nie była przeznaczona do przyjęcia standardowego wejścia. Zatem sen zlekceważy te dane, narzeka na brak argumentów i wychodzi. Jednak jeśli jeden typ:
echo 8 | sleep $(cat)
Wiele pocisków rozwinie to do
sleep 8
i sen będzie czekał 8 sekund przed wyjściem. Możesz także zrobić coś podobnego z ssh:command | ssh 1.2.3.4 'cat >> example-file'
To polecenie z dołączonym plikiem przykładowym na komputerze o adresie 1.2.3.4 z dowolnym wyjściem z „polecenia”.
I to (prawdopodobnie) po prostu drapie powierzchnię. Jestem pewien, że mógłbym znaleźć więcej przykładów przydatności kota, gdybym chciał, ale ten post jest wystarczająco długi. Podsumowując, powiem tak: proszenie powłoki o przewidywanie wszystkich tych scenariuszy (i kilku innych) jest naprawdę niewykonalne.
źródło
Pamiętaj, że użytkownik może mieć
cat
w swoim$PATH
która nie jest dokładnie POSIXcat
(ale być może jakiś wariant, który mógłby coś gdzieś zalogować). W takim przypadku nie chcesz, aby powłoka go usunęła.PATH
Może zmienić się dynamicznie, a następniecat
nie jest to, co uważam, że jest. Trudno byłoby napisać powłokę wykonującą optymalizację, o jakiej marzysz.Ponadto w praktyce
cat
jest to dość szybki program. Jest kilka praktycznych powodów (oprócz estetyki), aby tego uniknąć.Zobacz także doskonałe przemówienie Piekła na temat POSIX [Y] Regina-Gianasa na FOSDEM2018. Daje to inne powody, aby unikać robienia tego, o czym marzysz w skorupce.
Gdyby wydajność była naprawdę problemem dla powłok, ktoś zaproponowałby powłokę, która wykorzystuje wyrafinowaną optymalizację kompilatora całego programu, statyczną analizę kodu źródłowego i techniki kompilacji just-in-time (wszystkie te trzy domeny mają dziesięciolecia postępu oraz publikacje naukowe i dedykowane konferencje, np. w ramach SIGPLAN ). Niestety, nawet jako interesujący temat badawczy, który nie jest obecnie finansowany przez agencje badawcze lub inwestorów venture capital, i dedukuję, że po prostu nie jest to warte wysiłku. Innymi słowy, prawdopodobnie nie ma znaczącego rynku na optymalizację pocisków . Jeśli masz pół miliona euro na takie badania, łatwo znajdziesz kogoś, kto to zrobi, i wierzę, że przyniosłoby to wartościowe wyniki.
Z praktycznego punktu widzenia, przepisywania, w celu poprawy wydajności, często wykonuje się mały (sto wierszy) skrypt powłoki w dowolnym lepszym języku skryptowym (Python, AWK, Guile, ...). I nie jest rozsądne (z wielu powodów inżynierii oprogramowania) pisanie dużych skryptów powłoki: gdy piszesz skrypt powłoki przekraczający sto wierszy, musisz rozważyć jego przepisanie (nawet ze względu na czytelność i konserwację) w bardziej odpowiednim języku : jako język programowania powłoka jest bardzo słaba. Istnieje jednak wiele generowanych skryptów powłoki i nie bez powodu (np.
configure
Skrypty generowane przez autoconf GNU ).Jeśli chodzi o ogromne pliki tekstowe, przekazywanie ich
cat
jako pojedynczego argumentu nie jest dobrą praktyką, a większość sysadminów wie o tym (gdy uruchomienie dowolnego skryptu powłoki zajmuje więcej niż minutę, zaczynasz rozważać jego optymalizację). W przypadku dużych plików gigabajtów nigdy niecat
jest dobrym narzędziem do ich przetwarzania.źródło
cat some-huge-log | tail -n 5
bieg (gdzietail -n 5 some-huge-log
mógłby skoczyć prosto do końca, podczas gdycat
czyta tylko od przodu do tyłu) nie zgodziłby się.cat
dużego pliku tekstowego w zakresie kilkudziesięciu GB (który został stworzony do testowania) zajmuje trochę czasu. Nie poleciłbym.Dodając do odpowiedzi @Kusalananda (i komentarza @alephzero), cat może być dowolny:
lub
Nie ma powodu, dla którego cat (sam) lub / usr / bin / cat w systemie faktycznie jest narzędziem cat konkatenate.
źródło
cat
jest zdefiniowane przez POSIX i dlatego nie powinno być zupełnie inne.PATH=/home/Joshua/bin:$PATH cat ...
Czy na pewno wiesz, cocat
teraz?cat
można to zmienić, ale oboje wiemy, że nie należy bezmyślnie zastępować go czymś innym. W moim komentarzu wskazano, że POSIX nakazuje określone (podzbiór) zachowanie, które można zasadnie oczekiwać. Czasami napisałem skrypt powłoki, który rozszerza działanie standardowego narzędzia. W tym przypadku skrypt powłoki zachowywał się i zachowywał tak jak narzędzie, które zastąpił, z tym wyjątkiem, że miał dodatkowe możliwości./bin/cat
. (I uczyniłbyś to opcją, którą możesz wyłączyć.) Lub zrobiłbyścat
wbudowaną powłokę (do której może się/bin/cat
przydać wiele argumentów?), Aby użytkownicy mogli kontrolować, czy chcą, aby wersja zewnętrzna była normalna sposób, zenable cat
. Jak dlakill
. (Myślałem, że bashcommand cat
zadziała, ale to nie pomija wbudowanych)cat
w tym środowisku nie odnosi się już do zwykłegocat
. Oczywiście optymalizację należy wdrożyć po przetworzeniu aliasów. Uważam, że wbudowane powłoki reprezentują polecenia w katalogu wirtualnym, który zawsze jest poprzedzony twoją ścieżką. Jeśli chcesz uniknąć wbudowanej w powłokę wersji dowolnego polecenia (np.test
), Musisz użyć wariantu ze ścieżką.Dwa „bezużyteczne” zastosowania dla kota:
... tutaj
cat
służy do miksowania danych wejściowych pliku i potoku.... tutaj
xargs
możesz zaakceptować praktycznie nieskończoną liczbę nazw plików i uruchomićcat
tyle razy, ile potrzeba, jednocześnie zachowując się jak jeden strumień. Działa to więc w przypadku dużych list plików, w przypadku których bezpośrednie użyciexargs sort
nie.źródło
cat
wywoływana jest z dokładnie jednym argumentem. Zwłaszcza w przypadku, gdysh
przekazany jest ciąg znaków ixargs
zadzwonicat
bezpośrednio, nie ma możliwości, aby powłoka mogła użyć wbudowanej implementacji.Oprócz innych rzeczy,
cat
-check spowodowałoby dodatkowe obciążenie wydajności i zamieszanie co do tego, które użyciecat
jest faktycznie bezużyteczne, IMHO, ponieważ takie kontrole mogą być nieefektywne i powodować problemy z legalnymcat
użyciem.Gdy komendy dotyczą standardowych strumieni, muszą tylko dbać o odczyt / zapis do standardowych deskryptorów plików. Polecenia mogą wiedzieć, czy standardowe wejście można zobaczyć / zobaczyć, czy nie, co wskazuje na potok lub plik.
Jeśli dodamy do miksu sprawdzanie, jaki proces faktycznie zapewnia tę zawartość standardową, będziemy musieli znaleźć proces po drugiej stronie potoku i zastosować odpowiednią optymalizację. Można to zrobić pod względem samej powłoki, jak pokazano w poście SuperUser autorstwa Kyle'a Jonesa, oraz pod względem powłoki, która
jak pokazano w połączonym poście. To jeszcze 3 polecenia (tak dodatkowe
fork()
s iexec()
s) i rekurencyjne przejścia (tak dużoreaddir()
połączeń).Jeśli chodzi o C i kod źródłowy powłoki, powłoka zna już proces potomny, więc nie ma potrzeby rekurencji, ale skąd wiemy, kiedy zoptymalizować, a kiedy w
cat
rzeczywistości jest bezużyteczny? W rzeczywistości istnieją użyteczne zastosowania kota , takie jakDodanie takiej optymalizacji do powłoki byłoby prawdopodobnie marnotrawstwem i niepotrzebnym narzutem. Jak już wspomniano w odpowiedzi Kusalandy, w UUOC chodzi raczej o to, że użytkownik nie rozumie, jak najlepiej łączyć polecenia, aby uzyskać najlepsze wyniki.
źródło