Polecenie wyjścia zawartości pliku na standardowe wyjście?


28

Wiem, że catmogę to zrobić, ale jego głównym celem jest konkatenacja, a nie tylko wyświetlanie treści.

Wiem też, o less, a more, ale szukam czegoś prostego ( nie pager ), które po prostu wyprowadza zawartość pliku do terminalu i jest zrobiona specjalnie dla tego, jeśli nie ma czegoś takiego.


1
W rzeczywistości 99% razy używasz cat, aby wyświetlać pliki zamiast łączyć wszystko.
LatinSuD

1
@LatinSuD - w 100% przypadków - catkonkatenuje.
mikeserv

@mikeserv: Jest to kwestia tego, co masz na myśli przez wykonanie operacji; normalne użycie języka oznaczałoby, że zostało to wykonane przynajmniej raz. „Drukowanie” pustego ciągu nie wymaga drukowania żadnych znaków; można śmiało powiedzieć, że nic nie drukuje. Teraz przetwarzanie a+b+cwymaga 2 dodawania, a przetwarzanie anie wymaga dodawania niczego. Podobnie wykonywanie cat fnie wiąże się z niczym (nawet jeśli jest to jedyna rzecz, którą może oznaczać „łączenie sekwencji jednego pliku”).
Marc van Leeuwen,

2
@mikeserv: Nie rozumiem, co masz na myśli przez in + out. Pewnie catczyta plik i zapisuje inny plik (strumień), ale to nie znaczy, że wszystko łączy. Jeśli masz na myśli, że cat ftak naprawdę jest cat - f, to po prostu nieprawda.
Marc van Leeuwen,

1
@ confused00: catnaprawdę miał na celu konkatenację ( cat file1 file2połączy oba pliki do standardowego wyjścia). Ale jego efektem ubocznym jest to, że mając tylko 1 plik jako argument, wysyła ten jeden plik do standardowego wyjścia (gdziekolwiek to się dzieje, albo terminal, albo przekierowuje do czegoś). Nie było więc żadnego innego polecenia stworzonego tylko do wyjścia na standardowe wyjście, ponieważ catistniało i pozwalało na to po prostu. Dlatego chcesz użyć cat.
Olivier Dulac,

Odpowiedzi:


37

Najbardziej oczywistym z nich jest cat. Ale spójrz także na headi tail. Istnieją także inne utillities powłoki do wydrukowania pliku linia po linii: sed, awk, grep. Ale mają one na przemian zawartość pliku lub wyszukiwanie w pliku.

Zrobiłem kilka testów, aby oszacować, który jest najbardziej skuteczny. Przeglądam wszystkie koryta, straceaby zobaczyć, które wywołały najmniej wywołań systemowych. Mój plik ma 1275 linii.

  • awk: 1355 wywołań systemowych
  • cat: 51 wywołań systemowych
  • grep: 1337 wywołań systemowych
  • head: 93 wywołań systemowych
  • tail: 130 wywołań systemowych
  • sed: 1378 wywołań systemowych

Jak widać, nawet jeśli catzostał zaprojektowany do łączenia plików, jest najszybszy i najskuteczniejszy. sed, awkA grepwydrukowane pliku wiersz po wierszu, to dlatego, że mają więcej niż 1275 wywołań systemowych.


8
Dobry pomysł na policzenie wywołań systemowych!
stycznia

1
+1, odpowiedź jest dokładniejsza (o znaczeniu kota), bardziej kompletna (alternatywy) i zbadana (systemowe)
Olivier Dulac

23

Wiem, że catmogę to zrobić, ale jego głównym celem jest konkatenacja, a nie tylko wyświetlanie treści.

Celem catjest właśnie to, odczytanie pliku i wyjście na standardowe wyjście.


1
Ale cat --helpmówi „Połącz pliki lub standardowe wejście do standardowego wyjścia”. Nie chcę niczego konkatenować
zdezorientowany 00

18
Wierzcie lub nie, kot jest dokładnie tym, czego szukasz.
stycznia

5
Nie, @ confused00, Jan ma rację. Chodzi o to - terminal jest stdout - rozumiesz? zrobić readlink /dev/fd/1na przykład - powinieneś dostać tam nazwę swojego tty, jeśli uruchamiasz się w standardowym monicie. Więc łączenie danych wejściowych i wyjściowych jest tym, o co prosisz.
mikeserv

2
@mikeserv Tak, rozumiem, o co mi chodzi, chyba byłem zbyt skupiony na znaczeniu „konkatenacji”.
zmieszany 00

3
Logika polega na tym, że drukowanie zawartości jednego pliku jest tylko specjalnym przypadkiem drukowania zawartości jednego lub więcej plików po kolei.
zwolnić

10

Najpierw catzapisuje na standardowe wyjście, które niekoniecznie jest terminalem, nawet jeśli catzostało wpisane jako część polecenia do interaktywnej powłoki. Jeśli naprawdę potrzebujesz czegoś do zapisania na terminalu, nawet gdy przekierowane jest standardowe wyjście, nie jest to takie łatwe (musisz określić, który terminal, a może nawet nie być, jeśli polecenie zostanie wykonane ze skryptu), chociaż jeden mógłby (ab) użyć standardowego wyjścia błędu, jeśli polecenie jest tylko częścią potoku. Ale skoro wskazałeś, że to catrzeczywiście działa, przypuszczam, że nie pytałeś o taką sytuację.

Jeśli Twoim celem byłoby przesłanie tego, co zapisano na standardowe wyjście do potoku, wówczas użycie catbędzie kwalifikować się do nagrody Bezużyteczne użycie kota , ponieważ cat file | pipeline(gdzie pipelineoznacza dowolny potok) można to zrobić bardziej efektywnie <file pipeline. Ale znowu, z twoich sformułowań wywnioskuję, że to nie była twoja intencja.

Więc nie jest tak jasne, o co się martwisz. Jeśli nie możesz catpisać, możesz zdefiniować alias jedno- lub dwuznakowy (wciąż istnieje kilka takich nazw, które nie są używane w standardowym Uniksie). Jeśli jednak martwisz się, że catspędzasz bezużyteczne cykle, nie powinieneś.

Gdyby istniał program, nullktóry nie przyjmuje żadnych argumentów i po prostu kopiuje standardowe wejście na standardowe wyjście (obiekt neutralny dla potoków), możesz zrobić to, co chcesz <file null. Nie ma takiego programu, chociaż napisanie go byłoby łatwe (program C z mainfunkcją tylko jednego wiersza może wykonać zadanie), ale wywoływanie catbez argumentów (lub cat -jeśli chcesz być jawny) robi właśnie to.

Jeśli istniał nocatprogram, który pobiera dokładnie jeden argument nazwy pliku, próbuje otworzyć plik, narzeka, jeśli nie może, a w przeciwnym razie kontynuuje kopiowanie z pliku na standardowe wyjście, to właśnie o to prosisz. Jest to tylko nieco trudniejsze do napisania niż null, ponieważ główną pracą jest otwieranie pliku, testowanie i być może narzekanie (jeśli jesteś skrupulatny, możesz również chcieć dołączyć test, który zawiera dokładnie jeden argument, i narzekać inaczej). Ale znowu cat, teraz wyposażony w pojedynczy argument, robi właśnie to, więc nie ma potrzeby żadnego nocatprogramu.

Po napisaniu nocatprogramu, po co zatrzymywać się na jednym argumencie? Zawijanie kodu w pętlę for(;*argp!=NULL;++argp)wcale nie stanowi żadnego wysiłku, dodaje do pliku binarnego co najwyżej kilka instrukcji maszynowych i pozwala uniknąć narzekania na niewłaściwą liczbę argumentów (co oszczędza znacznie więcej instrukcji). Voilà prymitywna wersja catłączenia plików. (Szczerze mówiąc, musisz go trochę ulepszyć, aby bez argumentów zachowywał się jak null.)

Oczywiście w prawdziwym catprogramie dodali kilka dzwonków i gwizdków, ponieważ zawsze tak robią. Ale istotą jest to, że aspekt „konkatenacji” catkosztów naprawdę nie wymaga żadnego wysiłku, ani dla programisty, ani dla maszyny, która go wykonuje. Fakt, który catpochłania nulli nocatwyjaśnia nieistnienie takich programów. Unikaj używania catz jednym argumentem, jeśli wynik trafia do potoku, ale jeśli jest on używany tylko do wyświetlania zawartości pliku w terminalu, nawet strona, do której dowiązałem, przyznaje, że jest to użyteczne zastosowanie cat, więc nie wahaj się.


Możesz przetestować, który catjest naprawdę zaimplementowany przez prostą pętlę wokół hipotetycznej nocatfunkcjonalności, wywołując catkilka nazw plików, wśród których jedna jest niepoprawna, a nie na pierwszej pozycji: zamiast narzekać od razu, że ten plik nie istnieje, catnajpierw zrzuca poprzedzające go prawidłowe pliki, a następnie narzeka na nieprawidłowy plik (przynajmniej tak zachowuje się mój kot).


7

W trakcie zshpróby

<file

Uważam, że to najkrótszy sposób na wydrukowanie pliku. Używa „ukrytego” cat(lub morejeśli stdout jest terminalem), ale polecenie użyte do drukowania jest kontrolowane przez READNULLCMDzmienną, którą można bezpiecznie zastąpić bezpośrednio nazwą polecenia lub nawet jakąś funkcją. Na przykład, aby wydrukować pliki z numeracją linii:

numcat() { nl -s'> ' -w2 - }
READNULLCMD=numcat
<file

5

POSIX definiuje cat jako:

IMIĘ

cat - konkatenuje i drukuje pliki

STRESZCZENIE

cat [-u] [plik ...]

OPIS

Narzędzie cat odczyta pliki w kolejności i zapisze ich zawartość na standardowym wyjściu w tej samej kolejności.

Więc myślę, że konkatenacja tutaj oznacza odczytywanie plików w sekwencji .


5

Wiem, że to jest pytanie z przeszłości. Technicznie, ponieważ drukowanie zawartości pliku stdoutjest formą konkatenacji, catjest semantycznie właściwe. Nie zapominaj, że printfsemantycznie ma na celu formatowanie i drukowanie danych. Bash zapewnia również składnię przekierowującą dane wejściowe i wyjściowe z plików. Ich połączenie może powodować:

printf '%s' "$(<file.txt)"

4
Oprócz tego, że jest szczególnie ronda, wyświetlane polecenie nie jest równoważne cat file.txt, ponieważ usuwa wszelkie końcowe znaki nowej linii ( $(...)robi to).
Marc van Leeuwen

+1, niezły chwyt. Nie wiedział tego.
James M. Lay

3

Korzystanie z bashwbudowanych funkcji i unikanie tworzenia podprocesów:

{ while IFS='' read -rd '' _bcat_; do printf '%s\0' "${_bcat_}"; done; printf '%s' "${_bcat_}"; unset _bcat_; } <'/path/to/file'

IFSzostaną zastosowane tylko do readpoleceń, więc nie martw się o swoje globalne IFSzmiany.

Pętla wymagana do obsługi znaków zerowych (dzięki Stéphane Chazelas).

Ten sposób nie jest odpowiedni dla dużych plików, ponieważ zawartość pliku najpierw odczytuje zmienną (czyli pamięć). BTW Próbowałem wydrukować plik tekstowy 39M w ten sposób, a użycie pamięci bash nie przekroczyło 5M, więc nie jestem pewien co do tego przypadku.

Jest również cholernie wolny i nieefektywny procesorowo: dla tego samego pliku 39M zajęło ~ 3 minuty przy 100% wykorzystaniu pojedynczego rdzenia.

W przypadku dużych plików lub plików binarnych lepiej użyć cat '/path/to/file'lub nawet, dd if='/path/to/file' bs=1Mjeśli to możliwe.


1
Zobacz także, pv -qktóre w systemie Linux mogą używać, splice()które dla niektórych typów stdin / stdout poprawią wydajność.
Stéphane Chazelas

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.