Come leggere oltre 4k input senza nuove righe su un terminale?


25

Quindi ho molti dati SENZA NUOVE LINEE negli Appunti (è un file SVG di grandi dimensioni su una riga). sono andato

$ cat >file.svg

poi ha provato a incollare (nel Terminale Gnome), ma sono stati accettati solo i primi caratteri da 4kB.

Presumo che questa sia una caratteristica / limitazione readline.

C'è un modo per leggere da STDIN che eviterebbe questo problema?

MODIFICARE

Caso di prova: crea un file demo. Questo avrà ~ 4k "=" simboli seguiti da "foo bar".

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

Copialo negli appunti

xclip test.in

(se si desidera fare clic con il pulsante centrale per inserire) o

xclip -selection clipboard test.in

(se vuoi usare Ctrl-Shift-Insert per incollarlo)

Quindi cat >test.out, incolla (in qualsiasi modo). Premi Ctrl-D per terminare il flusso. cat test.out- vedi "foo bar"?

Sul mio set-up (Ubuntu 12.04, Gnome Terminal, zsh) quando incollo vedo solo il =e non vedo foo bar. Lo stesso quando ispeziono test.out.


Sei sicuro che il tuo file SVG sia stato letto interamente negli appunti?
lgeorget,

Qual è il tuo vero problema? Come archiviare il contenuto degli Appunti in un file? In tal caso, ci sono altri modi oltre a incollare nel terminale.
lgeorget,

quanto costa N nel tuo caso? L'ho provato con 2kB di dati xml (inc LF) nessun problema.
fduff,

1
@artfulrobot Un processo in primo piano interagisce direttamente con tty / pty. La shell non è coinvolta. Puoi vederlo perché non hai funzioni readline (comandi di modifica / salto, cronologia, ...) nei programmi se non usano essi stessi readline o altre librerie di input.
jofel,

1
Questo non è un limite di readline - readline e bash non sono coinvolti qui. È una limitazione dell'interfaccia del terminale.
Gilles 'SO- smetti di essere malvagio'

Risposte:


22

Se capisco correttamente la fonte, sotto Linux, il numero massimo di caratteri che possono essere letti in una volta sola su un terminale è determinato dalla N_TTY_BUF_SIZEfonte del kernel. Il valore è 4096.

Questa è una limitazione dell'interfaccia del terminale, in particolare la modalità canonica ("cotta") che fornisce un editor di linee estremamente grezzo (backspace, invio, Ctrl+ Dall'inizio di una riga per la fine del file). Succede completamente al di fuori del processo che sta leggendo.

È possibile passare al terminale in modalità raw, che disabilita l'elaborazione della linea. Disabilita anche Ctrl+ De altre cose, aggiungendo un onere extra al tuo programma.

Questa è un'antica limitazione di Unix che non è mai stata risolta perché c'è poca motivazione. Gli umani non entrano in file così lunghe. Se alimentassi l'input da un programma, reindirizzeresti l'input del tuo programma da un file o una pipe.

Ad esempio, per utilizzare il contenuto degli Appunti X, pipe da xselo xclip. Nel tuo caso:

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

Rimuovere -bo -selection clipboardutilizzare la selezione X (quella impostata evidenziando con il mouse) anziché gli Appunti.

Su OSX, utilizzare pbpasteper incollare il contenuto degli appunti (e pbcopyper impostarlo).

Puoi accedere agli Appunti X su SSH se attivi l'inoltro X11 con ssh -X(che alcuni server potrebbero vietare). Se è possibile utilizzare solo sshsenza l'inoltro X11, è possibile utilizzare scp, sftpo sshfsper copiare un file.

Se incollare è l'unica soluzione perché non è possibile inoltrare gli Appunti o non si sta incollando, ma ad esempio falsificare la digitazione in una macchina virtuale, un approccio alternativo consiste nel codificare i dati in qualcosa che ha nuove righe. Base64 è adatto per questo: trasforma i dati arbitrari in caratteri stampabili e ignora gli spazi bianchi durante la decodifica. Questo approccio ha l'ulteriore vantaggio di supportare dati arbitrari nell'input, persino di controllare i caratteri che il terminale interpreterebbe quando si incolla. Nel tuo caso, puoi codificare il contenuto:

xsel -b | base64 | xsel -b

quindi decodificalo:

base64 -d
 Paste
Ctrl+D

Si noti che esiste un bug di danneggiamento dei dati davvero brutto quando si utilizza xselcon> 4k byte: github.com/kfish/xsel/issues/14
Patrick

14

Il limite si sta eseguendo in è la dimensione massima di una linea in modalità di ingresso canonica , MAX_CANON.

In modalità di input canonica, il driver tty fornisce servizi di modifica di linea di base, quindi non è necessario che il programma userspace. Non ha quasi tutte le funzionalità di readline, ma riconosce alcuni caratteri speciali configurabili come cancella (di solito Backspace o Elimina) e uccidi (di solito Ctrl-U).

Soprattutto per la tua domanda, i buffer della modalità canonica vengono inseriti fino a quando non viene visualizzato il carattere di fine riga. Poiché il buffer si trova nel driver tty, nella memoria del kernel, non è molto grande.

Puoi disattivare la modalità canonica con stty cbreako stty -icanon, quindi incollare. Questo ha lo svantaggio significativo che non sarai in grado di inviare un EOF con Ctrl-D. Questa è un'altra delle cose di cui è responsabile la modalità canonica. Sarai comunque in grado di terminare catcon Ctrl-C perché i caratteri che generano il segnale sono controllati da un flag separato ( stty rawo stty -isig).

Il mistero per me è perché, dal momento che hai già dimostrato di conoscere xclip, non solo usi xclip -o > fileinvece dicat


1
Il mistero può essere facilmente risolto: sembra che artfulrobot voglia riempire rapidamente un file su un host remoto con i dati degli appunti. Nella shell remota, normalmente non c'è accesso diretto agli appunti locali tramite xclip.
jofel

3
Ah, buon vecchio upload-by-paste. Se dovessi fare uno di questi e non fosse un semplice testo, lo codificherei invece di cercare di convincere il conducente tty a passarlo. Anche il testo semplice con righe enormi potrebbe essere gestito in questo modo.

2

Se fate:

stty eol =

E quindi esegui la demo suggerita nel tuo EDIT , vedrai la barra di foo nella stampa di test.out . La disciplina di linea del terminale scaricherà il suo output sul suo lettore mentre legge ogni carattere eol speciale nel tuo input.

Un terminale in modalità canonica Linux - come può essere configurato con stty icanono probabilmente solo stty sane- gestisce i seguenti caratteri di input speciali ...

  • eof
    • predefinito: ^D
    • Termina una linea di input e scarica l'output sul lettore. Poiché viene rimosso dall'input, se viene immesso come unico carattere su una riga, viene passato al lettore come lettura null o fine del file .
  • EOL
    • impostazione predefinita: non assegnato
    • Termina anche una riga di input, ma non viene rimosso dall'input.
  • uccidere
    • predefinito: ^U
    • Cancella tutti gli input nel buffer.
  • cancellare
    • impostazione predefinita: ^H (o eventualmente @o ^?su alcuni sistemi)
    • Cancella l'ultimo carattere di input nel buffer.

Quando anche iexten è impostato - come stty icanon iexteno, di nuovo, probabilmente solo stty sane, un terminale canonico Linux gestirà anche ...

  • eol2
    • impostazione predefinita: non assegnato
    • Inoltre anche termina una linea di ingresso, ed è anche non rimosso dall'ingresso.
  • werase
    • predefinito: ^W
    • Cancella l'ultima parola di input memorizzata .
  • rprnt
    • predefinito: ^R
    • Ristampa di tutti gli input bufferizzati.
  • lnext
    • predefinito: ^V
    • Rimuove qualsiasi significato speciale per quanto riguarda la disciplina di linea per il carattere di input immediatamente successivo.

Questi caratteri vengono gestiti rimuovendoli dal flusso di input - ad eccezione di eol ed eol2 , ovvero - ed eseguendo la funzione speciale associata prima di passare il flusso elaborato al lettore - che di solito è la shell, ma potrebbe essere qualunque sia il gruppo di processi in primo piano .

Altri caratteri di input speciali che sono gestiti in modo simile ma che possono essere configurati indipendentemente da qualsiasi impostazione icanon includono il set isig - set like stty isige probabilmente incluso anche in una configurazione sana :

  • smettere
    • predefinito: ^\
    • Svuota tutti gli input buffer (se non è impostato noflsh ) e invia SIGQUIT al gruppo di processi in primo piano, generando probabilmente un core-dump.
  • sosp
    • predefinito: ^Z
    • Svuota tutti gli input buffer (se noflsh non è impostato) e invia SIGTSTP al gruppo di processi in primo piano. Il gruppo di processo sospeso può probabilmente essere ripreso con uno kill -CONT "$!"o solo fgin una ( set -m) shell controllata dal lavoro.
  • intr
    • predefinito: ^C
    • Svuota tutti gli input buffer (se noflsh non è impostato) e invia SIGINT al gruppo di processi in primo piano.

E il set ixon - configurato come stty ixone anche solitamente incluso in una configurazione sana :

  • fermare
    • predefinito: ^S
    • Interrompe tutto l'output al lettore fino a quando avvio viene letto in ingresso o - quando ixany è anche impostato - almeno un altro carattere viene letto.
  • inizio
    • predefinito: ^Q
    • Riavvia l'uscita se è stata precedentemente arrestata con stop .
  • Sia stop che start vengono rimossi dall'input durante l'elaborazione, ma se l'output viene riavviato a causa di un carattere in input quando ixany è impostato, quel carattere non viene rimosso.

I caratteri speciali gestiti su altri sistemi non Linux potrebbero includere ...

  • rossore
    • predefinito: ^O
    • Attiva / disattiva l'eliminazione e lo svuotamento dell'input buffer e viene rimosso dall'input.
  • dsusp
    • impostazione predefinita: non assegnato
    • Svuota tutti gli input nel buffer solo quando il lettore legge il carattere di input speciale assegnato quindi invia SIGTSTP.

E possibilmente...

  • swtch
    • impostazione predefinita ^@ (significato \0o NUL)
    • Commuta i layer shell in primo piano. Da utilizzare con l' applicazione shl shell-layer su alcuni sistemi.
    • Un'implementazione di shlcui multiplex ptys ed è quindi compatibile con il controllo del lavoro piuttosto che con il comportamento dipendente dall'implementazione originale dell'implementazione può essere liberamente inclusa nella heirloom-toolchestsuite di strumenti.

Per un quadro più chiaro di come e perché (e forse perché no) vengono gestite queste funzioni di input consultare man 3 termios.

Tutte le funzioni di cui sopra possono essere assegnate (o riassegnate) - quando applicabile - come sttyfunction assigned-key. Per disabilitare qualsiasi singola funzione fare . In alternativa, come sembrano indicare vari tentativi con assegnazioni per una qualsiasi delle funzioni di modifica della linea sopra menzionate con tutte le implementazioni di GNU, AST o heirloom , puoi anche come l' assegnazione NUL per qualsiasi funzione sembra equivalere a impostarla come non assegnata sul mio Linux sistema.sttyfunction^-sttysttyfunction^@

Probabilmente vedi un'eco di questi caratteri quando li digiti (come può essere probabilmente configurato con w / [-] ctlecho ) , ma questo è solo un indicatore per mostrarti dove sei stato - il programma che riceve i tuoi input non ha idea che tu li ha digitati (tranne eol [2] , cioè) e riceve solo una copia del tuo input a cui la disciplina di linea ha applicato i loro effetti.

Una conseguenza della gestione da parte del terminale delle varie funzioni di modifica della linea è che deve in qualche modo bufferizzare l'input per poter agire sulle funzioni che gli indichi che dovrebbe - e quindi non può esserci una fornitura illimitata di input che potresti uccidere in qualsiasi momento . Il buffer di linea è più precisamente il kill buffer.

Se imposti i caratteri eol o eol2 su un delimitatore che si verifica nell'input - anche se nessuno dei due è una nuova riga o un carattere di ritorno, per esempio - allora sarai in grado di uccidere fino al punto in cui si è verificato l'ultima volta e il tuo kill buffer si estenderà il più possibile fino al successivo di questi - o una nuova riga (o restituirà se icrnl è impostato e igncr non lo è) - si verifica nell'input.


1

cataccetterà un numero qualsiasi di personaggi, come potresti vedere facendo ad esempio cat /dev/random > test.bin(non farlo a meno che tu non sappia come fermarlo :). Ho provato a copiare e incollare un file di grandi dimensioni in cat > test.txt. Tutte le linee sono finite nel file se ho annullato con Ctrl- co Ctrl- d, ma nel primo caso non tutte le linee sono state stampate sul terminale . Questo credo sia perché catbufferizza la sua stampa, in attesa di un buffer completo di testo o input diretto dal terminale prima di ogni stampa.

Sul mio sistema, penso che la dimensione del buffer sia di 4096 (2 ^ 12) byte: crea un file di 4095 byte usando (printf '1234567890%.0s' {1..409} && printf 12345) > test.in, carica quello nel buffer di copia usando xclip test.in, avvia cat > test.out, incolla usando Shift- Inserte termina il flusso premendo Ctrl- d. Ora aggiungi un byte usando printf '6' >> test.ine lo stream viene stampato due volte : una volta catnell'output (tutti i 4096 byte) e gli ultimi 4095 byte di nuovo sulla shell dopo aver terminato.


+1 Nel mio caso, dipendeva anche dagli appunti utilizzati. Se avessi usato il buffer di selezione (clic centrale incollato) avrei visto solo le prime 4542 righe dei miei dati di test (ma tutte sono finite nel file creato) ma usando gli Appunti X (Ctrl + C / Ctrl + V) ho visto tutto. In entrambi i casi tutti i dati sono stati stampati nel file risultante ma nel primo sono stati mostrati solo i dati parziali nel terminale.
terdon

1
Non ho lo stesso comportamento. Vedi domanda modificata
artfulrobot

0

Una soluzione è incollarla in un editor che supporti le linee lunghe, ad esempio vim.

Se usi vim, prima inserisci la modalità incolla con :pasteprima di entrare nella modalità inserimento ie incollare il testo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.