Jak czytać ponad 4k danych wejściowych bez nowych wierszy na terminalu?


25

Mam więc dużo danych BEZ NOWYCH LINII w schowku (jest to duży plik SVG w jednej linii). poszedłem

$ cat >file.svg

następnie próbował wkleić (w Gnome Terminal), ale zaakceptowano tylko pierwsze znaki 4kB.

Zakładam, że jest to funkcja / ograniczenie readline.

Czy istnieje sposób na czytanie ze STDIN, który uniknąłby tego problemu?

EDYTOWAĆ

Przypadek testowy: Utwórz plik demonstracyjny. Ten będzie miał ~ 4k "=" symbole, a następnie "foo bar".

{ printf '=%.0s' {1..4095} ; echo "foo bar" ; } > test.in

Skopiuj to do schowka

xclip test.in

(jeśli chcesz kliknąć środkowy przycisk, aby wstawić) lub

xclip -selection clipboard test.in

(jeśli chcesz użyć Ctrl-Shift-Insert, aby go wkleić)

Następnie cat >test.outwklej (w dowolny sposób). Naciśnij Ctrl-D, aby zakończyć strumień. cat test.out- Widzisz „foo bar”?

W mojej konfiguracji (Ubuntu 12.04, Gnome Terminal, zsh) po wklejeniu widzę tylko =i nie widzę foo bar. To samo, kiedy sprawdzam test.out.


Czy na pewno plik SVG został w całości wczytany do schowka?
lgeorget

Jaki jest twój rzeczywisty problem? Jak przechowywać zawartość schowka do pliku? Jeśli tak, istnieje inny sposób niż wklejanie w terminalu.
lgeorget

ile wynosi N w twoim przypadku? Próbowałem z 2kB danych xml (włącznie z LF) bez problemu.
fduff

1
@artfulrobot Proces pierwszego planu współdziała bezpośrednio z tty / pty. Shell nie jest zaangażowany. Możesz to zobaczyć, ponieważ nie masz funkcji readline (edytowanie / przeskakiwanie poleceń, historia, ...) w programach, jeśli same nie używają readline ani żadnej innej biblioteki wprowadzania.
jofel

1
To nie jest ograniczenie readline - readline i bash nie są tutaj zaangażowane. Jest to ograniczenie interfejsu terminala.
Gilles „SO- przestań być zły”

Odpowiedzi:


22

Jeśli dobrze rozumiem źródło, w Linuksie maksymalna liczba znaków, które można odczytać za jednym razem na terminalu, jest określona przez N_TTY_BUF_SIZEźródło jądra. Wartość wynosi 4096.

Jest to ograniczenie interfejsu terminala, w szczególności trybu kanonicznego („gotowanego”), który zapewnia niezwykle prymitywny edytor linii (backspace, enter, Ctrl+ Dna początku linii dla końca pliku). Dzieje się to całkowicie poza procesem czytania.

Możesz przełączyć terminal do trybu surowego, który wyłącza przetwarzanie linii. Wyłącza również Ctrl+ Di inne drobiazgi, co stanowi dodatkowe obciążenie dla twojego programu.

Jest to starożytne ograniczenie Uniksa, które nigdy nie zostało naprawione, ponieważ motywacja jest niewielka. Ludzie nie wchodzą w tak długie linie. Jeśli podajesz dane wejściowe z programu, przekierujesz dane wejściowe programu z pliku lub potoku.

Na przykład, aby użyć zawartości schowka X, potoku z xsellub xclip. W Twoim przypadku:

xsel -b >file.svg
xclip -selection clipboard >file.svg

Usuń -blub -selection clipboardużyj zaznaczenia X (tego, które ustawia się poprzez podświetlenie myszą) zamiast schowka.

W systemie OSX użyj przycisku, pbpasteaby wkleić zawartość schowka (i pbcopyustawić ją).

Możesz uzyskać dostęp do schowka X przez SSH, jeśli aktywujesz przekazywanie X11 za pomocą ssh -X(którego niektóre serwery mogą zabronić). Jeśli można użyć tylko sshbez spedycji X11 można użyć scp, sftplub sshfsskopiować plik.

Jeśli wklejanie jest jedynym rozwiązaniem, ponieważ nie możesz przesłać schowka lub nie wklejasz, ale np. Fałszujesz pisanie na maszynie wirtualnej, alternatywnym podejściem jest zakodowanie danych w coś, co ma nowe znaki. Base64 jest do tego odpowiedni: przekształca dowolne dane w znaki drukowalne i ignoruje białe znaki podczas dekodowania. Podejście to ma tę dodatkową zaletę, że obsługuje dowolne dane na wejściu, a nawet znaki sterujące, które terminal interpretuje podczas wklejania. W twoim przypadku możesz zakodować treść:

xsel -b | base64 | xsel -b

następnie dekoduj:

base64 -d
 Paste
Ctrl+D

Zauważ, że przy użyciu xsel> 4k bajtów występuje naprawdę paskudny błąd powodujący
Patrick

14

Limit używasz na to rozmiar maksymalny linii w trybie wejściowym kanonicznej , MAX_CANON.

W trybie wprowadzania kanonicznego sterownik tty zapewnia podstawowe usługi edycji linii, więc program przestrzeni użytkownika nie musi tego robić. Nie ma prawie tyle funkcji, co readline, ale rozpoznaje kilka konfigurowalnych znaków specjalnych, takich jak wymazywanie (zwykle Backspace lub Delete) i zabijanie (zwykle Ctrl-U).

Najważniejsze dla twojego pytania, buforuje tryb kanoniczny, dopóki nie pojawi się znak końca linii. Ponieważ bufor znajduje się w sterowniku tty, w pamięci jądra nie jest bardzo duży.

Możesz wyłączyć tryb kanoniczny za pomocą stty cbreaklub stty -icanon, a następnie wkleić. Ma to tę istotną wadę, że nie będziesz w stanie wysłać EOF za pomocą Ctrl-D. To kolejna z rzeczy, za które odpowiada tryb kanoniczny. Nadal będziesz mógł zakończyć catCtrl-C, ponieważ znaki generujące sygnał są kontrolowane przez oddzielną flagę ( stty rawlub stty -isig).

Tajemnicą dla mnie jest to, dlaczego skoro już wykazałeś, że wiesz o tym xclip, nie używasz tylko xclip -o > filezamiastcat


1
Tajemnicę można łatwo rozwiązać: wydaje się, że artfulrobot chce szybko wypełnić plik na zdalnych hostach danymi ze schowka. W zdalnej powłoce zwykle nie ma bezpośredniego dostępu do lokalnego schowka przez xclip.
jofel

3
Ach, stare dobre przesyłanie przez wklejenie. Gdybym musiał zrobić jeden z nich i nie byłby to zwykły tekst, uuencodowałbym go zamiast próbować przekonać sterownik tty do przekazania go. W ten sposób można również obsługiwać zwykły tekst z ogromnymi liniami.

2

Jeśli zrobisz:

stty eol =

A następnie uruchom demo sugerowane w EDIT , zobaczysz pasek foo na wydruku test.out . Dyscyplina liniowa terminala opróżni swoje wyjście do swojego czytnika, gdy odczytuje każdy specjalny znak eol z twojego wejścia.

Terminal trybu kanonicznego w systemie Linux - jak można skonfigurować za pomocą stty icanonlub prawdopodobnie po prostu stty sane- obsługuje następujące specjalne znaki wejściowe ...

  • eof
    • domyślna: ^D
    • Kończy linię wejściową i opróżnia wyjście do czytnika. Ponieważ jest usuwany z wejścia, jeśli jest wprowadzany jako jedyny znak w linii, jest przekazywany do czytnika jako zerowy odczyt lub koniec pliku .
  • eol
    • domyślnie: nieprzypisany
    • Kończy również linię wejściową, ale nie jest usuwana z wejścia.
  • zabić
    • domyślna: ^U
    • Usuwa wszystkie buforowane dane wejściowe.
  • wymazać
    • domyślnie: ^H (lub ewentualnie @lub ^?w niektórych systemach)
    • Usuwa ostatni buforowany znak wejściowy.

Gdy iexten jest również ustawiony - podobnie jak stty icanon iexten, a może znowu tylko stty sanekanoniczny terminal Linux, będzie obsługiwał ...

  • eol2
    • domyślnie: nieprzypisany
    • Także też kończy przewód wejściowy i jest również nie usuwa z wejścia.
  • werase
    • domyślna: ^W
    • Usuwa ostatnie buforowane słowo wejściowe .
  • rprnt
    • domyślna: ^R
    • Przedrukowuje wszystkie buforowane dane wejściowe.
  • następny
    • domyślna: ^V
    • Usuwa wszelkie szczególne znaczenie, jeśli chodzi o dyscyplinę liniową dla bezpośrednio następującego znaku wejściowego.

Te znaki są obsługiwane przez usunięcie ich ze strumienia wejściowego - z wyjątkiem eol i eol2 , czyli - i wykonanie powiązanej funkcji specjalnej przed przekazaniem przetworzonego strumienia do czytnika - co zwykle jest twoją powłoką, ale może być niezależnie od grupy procesów pierwszego planu. .

Inne specjalne znaki wejściowe, które są obsługiwane podobnie, ale mogą być konfigurowane niezależnie od dowolnego ustawienia icanon , obejmują zestaw isig - zestaw podobny stty isigi prawdopodobnie również zawarty w zdrowej konfiguracji:

  • porzucić
    • domyślna: ^\
    • Opróżnia wszystkie buforowane dane wejściowe (jeśli nie jest ustawiony noflsh ) i wysyła SIGQUIT do grupy procesów na pierwszym planie - prawdopodobnie generując zrzut rdzenia.
  • susp
    • domyślna: ^Z
    • Opróżnia wszystkie buforowane dane wejściowe (jeśli nie jest ustawiony noflsh ) i wysyła SIGTSTP do grupy procesów pierwszego planu. Zawieszoną grupę procesów można prawdopodobnie wznowić za pomocą jednej kill -CONT "$!"lub tylko fgw ( set -m) kontrolowanej przez powłokę powłoce.
  • intr
    • domyślna: ^C
    • Opróżnia wszystkie buforowane dane wejściowe (jeśli nie jest ustawiony noflsh ) i wysyła SIGINT do grupy procesów pierwszego planu.

I zestaw ixon - skonfigurowany jak stty ixoni zwykle zawarty w zdrowej konfiguracji:

  • zatrzymać
    • domyślna: ^S
    • Zatrzymuje wszystkie dane wyjściowe do czytnika, dopóki albo start nie zostanie odczytany na wejściu, albo - gdy ixany jest również ustawiony - zostanie odczytany co najmniej jeden znak
  • początek
    • domyślna: ^Q
    • Ponownie uruchamia wyjście, jeśli zostało wcześniej zatrzymane za pomocą stop .
  • Zarówno stop, jak i start są usuwane z wejścia podczas przetwarzania, ale jeśli wyjście zostanie ponownie uruchomione z powodu dowolnego znaku na wejściu, gdy ustawiono ixany , znak ten nie zostanie usunięty.

Znaki specjalne obsługiwane w innych systemach innych niż Linux mogą obejmować ...

  • spłukać
    • domyślna: ^O
    • Przełącza odrzucanie i opróżnianie buforowanego wejścia i jest usuwane z wejścia.
  • dsusp
    • domyślnie: nieprzypisany
    • Opróżnia wszystkie buforowane dane wejściowe tylko wtedy, gdy czytnik odczytuje przypisany specjalny znak wejściowy, a następnie wysyła SIGTSTP.

I ewentualnie...

  • swtch
    • domyślne ^@ (znaczenie \0lub NUL)
    • Przełącza warstwy powłoki pierwszego planu. Do użytku z aplikacją shl warstw powłoki w niektórych systemach.
    • Implementacja, shlktóra multipleksuje ptys i dlatego jest kompatybilna z kontrolą zadań, a nie z zachowaniem zależnym od switch oryginalnej implementacji, może być swobodnie dostępna w heirloom-toolchestpakiecie narzędzi.

Aby uzyskać wyraźniejszy obraz tego, jak i dlaczego (a może dlaczego nie) obsługiwane są te funkcje wprowadzania, zapoznaj się z man 3 termios.

Wszystkie powyższe funkcje można przypisać (lub ponownie przypisać) - w stosownych przypadkach - podobnie sttyfunction assigned-key. Aby wyłączyć dowolną pojedynczą funkcję, wykonaj . Alternatywnie, ponieważ różne próby przypisania dowolnej z wyżej wymienionych funkcji edycji linii ze wszystkimi implementacjami GNU, AST lub dziedzicami wydają się wskazywać, możesz również, ponieważ przypisanie NUL dla dowolnej funkcji wydaje się równoznaczne z ustawieniem jej jako nieprzypisanej w moim systemie Linux system.sttyfunction^-sttysttyfunction^@

Prawdopodobnie widzisz echo tych znaków podczas ich wpisywania (co można prawdopodobnie skonfigurować w / [-] ctlecho ) , ale jest to tylko znacznik pokazujący, gdzie zrobiłeś - program otrzymujący twoje dane wejściowe nie ma pojęcia, że ​​ty wpisałem je (z wyjątkiem eol [2] , to jest) i otrzymuje tylko kopię twojego wkładu, do którego dyscyplina liniowa zastosowała swoje efekty.

Konsekwencją obsługi różnych funkcji edycji linii przez terminal jest to, że musi on w pewnym stopniu buforować dane wejściowe, aby działać zgodnie z funkcjami, które mu wskażesz - i dlatego nie może istnieć nieograniczona ilość danych wejściowych, które możesz w dowolnym momencie zabić . Linia bufor jest dokładniej kill bufor.

Jeśli ustawisz znaki eol lub eol2 na jakiś separator występujący na wejściu - nawet jeśli na przykład nie jest on znakiem nowej linii ani znakiem powrotu - będziesz w stanie zabić tylko do momentu, w którym ostatnio wystąpił i bufor zabijania będzie rozciągać się tak daleko, jak to możliwe, dopóki następna z nich - lub nowa linia (lub powróci, jeśli icrnl jest ustawione, a igncr nie jest ustawione ) - pojawi się na wejściu.


1

catzaakceptuje dowolną liczbę znaków, jak można to zrobić na przykład cat /dev/random > test.bin(nie rób tego, chyba że wiesz, jak to zatrzymać :). Próbowałem skopiować i wkleić duży plik do cat > test.txt. Wszystkie linie znalazły się w pliku, niezależnie od tego, czy anulowałem za pomocą Ctrl- cczy Ctrl- d, ale w pierwszym przypadku nie wszystkie linie zostały wydrukowane na terminalu . Uważam, że catdzieje się tak, ponieważ buforuje drukowanie, czekając na pełny bufor tekstu lub bezpośrednie wejście z terminala przed każdym drukowaniem.

W moim systemie rozmiar bufora wynosi 4096 (2 ^ 12) bajtów: utwórz plik 4095 bajtów za pomocą (printf '1234567890%.0s' {1..409} && printf 12345) > test.in, załaduj go do bufora kopii za pomocą xclip test.in, uruchom cat > test.out, wklej za pomocą Shift- Inserti zakończ strumień, naciskając Ctrl- d. Teraz dodaj bajt za pomocą printf '6' >> test.in, a strumień zostanie wydrukowany dwukrotnie : Raz w catwyjściu (wszystkie 4096 bajtów), a ostatnie 4095 bajtów ponownie w powłoce po zakończeniu.


+1 W moim przypadku zależało to również od użytego schowka. Gdybym użył bufora wyboru (wklejanie środkowego kliknięcia), zobaczyłem tylko pierwsze 4542 wiersze moich danych testowych (ale wszystkie znalazły się w utworzonym pliku), ale używając schowka X (Ctrl + C / Ctrl + V) zobaczyłem wszystko. W obu przypadkach wszystkie dane zostały wydrukowane w wynikowym pliku, ale w poprzednim tylko częściowe dane były wyświetlane w terminalu.
terdon

1
Nie dostaję takiego samego zachowania. Zobacz zredagowane pytanie
artfulrobot

0

Jednym z rozwiązań jest wklejenie go do edytora, który obsługuje długie linie, na przykład vim.

Jeśli używasz vima, najpierw wejdź w tryb wklejania, :pastezanim przejdziesz do trybu wstawiania izi wklejania tekstu.

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.