¿Cómo leer más de 4k de entrada sin nuevas líneas en un terminal?

25

Así que tengo muchos datos SIN NUEVAS LÍNEAS en el portapapeles (es un archivo SVG grande en una línea). Yo fuí

$ cat >file.svg

Luego traté de pegar (en Gnome Terminal), pero solo se aceptaron los primeros 4kB caracteres.

Supongo que esta es una característica / limitación de readline.

¿Hay alguna manera de leer de STDIN que evite este problema?

EDITAR

Caso de prueba: cree un archivo de demostración. Este tendrá ~ 4k "=" símbolos seguidos de "foo bar".

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

Copia eso en tu portapapeles

xclip test.in

(si desea hacer clic con el botón central para insertar) o

xclip -selection clipboard test.in

(si quieres usar Ctrl-Shift-Insert para pegarlo)

Luego cat >test.out, pegue (de cualquier manera). Presione Ctrl-D para finalizar la transmisión. cat test.out- ¿Ves "foo bar"?

En mi configuración (Ubuntu 12.04, Terminal Gnome, zsh) cuando pego solo veo =y no veo foo bar. Lo mismo cuando inspecciono test.out.

artfulrobot
fuente
¿Estás seguro de que tu archivo SVG se leyó por completo en tu portapapeles?
lgeorget
¿Cuál es tu problema real? ¿Cómo almacenar el contenido del portapapeles en un archivo? Si es así, hay otra manera que pegar en la terminal.
lgeorget
¿Cuánto es N en tu caso? Lo he intentado con 2kB de datos xml (inc LF) sin problema.
fduff
1
@artfulrobot Un proceso en primer plano interactúa directamente con tty / pty. El caparazón no está involucrado. Puede ver esto porque no tiene características de readline (comandos de edición / salto, historial, ...) en los programas si no usan readline ni ninguna otra biblioteca de entrada.
jofel
1
Esto no es una limitación de readline: readline y bash no están involucrados aquí. Es una limitación de la interfaz del terminal.
Gilles 'SO- deja de ser malvado'

Respuestas:

22

Si entiendo la fuente correctamente, en Linux, la cantidad máxima de caracteres que se pueden leer de una vez en un terminal está determinada por N_TTY_BUF_SIZEla fuente del núcleo. El valor es 4096.

Esta es una limitación de la interfaz del terminal, específicamente el modo canónico ("cocinado") que proporciona un editor de líneas extremadamente crudo (retroceso, enter, Ctrl+ Dal comienzo de una línea para el final del archivo). Sucede completamente fuera del proceso que está leyendo.

Puede cambiar el terminal al modo sin procesar, que deshabilita el procesamiento de línea. También deshabilita Ctrl+ Dy otras sutilezas, lo que supone una carga adicional para su programa.

Esta es una antigua limitación de Unix que nunca se ha solucionado porque hay poca motivación. Los humanos no entran en filas tan largas. Si alimentara la entrada de un programa, redirigiría la entrada de su programa desde un archivo o una tubería.

Por ejemplo, para usar el contenido del portapapeles X, canalizar desde xselo xclip. En tu caso:

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

Elimine -bo -selection clipboarduse la selección X (la que se establece resaltando con el mouse) en lugar del portapapeles.

En OSX, use pbpastepara pegar el contenido del portapapeles (y pbcopypara configurarlo).

Puede acceder al portapapeles X a través de SSH si activa el reenvío X11 con ssh -X(que algunos servidores pueden prohibir). Si sólo se puede utilizar sshsin el reenvío por X11, puede utilizar scp, sftpo sshfspara copiar un archivo.

Si pegar es la única solución porque no puede reenviar el portapapeles o no está pegando pero, por ejemplo, falsificando la escritura en una máquina virtual, un enfoque alternativo es codificar los datos en algo que tenga nuevas líneas. Base64 es adecuado para esto: transforma datos arbitrarios en caracteres imprimibles e ignora los espacios en blanco al decodificar. Este enfoque tiene la ventaja adicional de que admite datos arbitrarios en la entrada, incluso caracteres de control que el terminal interpretaría al pegar. En su caso, puede codificar el contenido:

xsel -b | base64 | xsel -b

luego descifrarlo:

base64 -d
 Paste
Ctrl+D
Gilles 'SO- deja de ser malvado'
fuente
Tenga en cuenta que hay un error de corrupción de datos realmente desagradable cuando se usa xselcon> 4k bytes: github.com/kfish/xsel/issues/14
Patrick
14

El límite que se está ejecutando en es el tamaño máximo de una línea en modo de entrada canónica , MAX_CANON.

En el modo de entrada canónica, el controlador tty proporciona servicios básicos de edición de línea para que el programa de espacio de usuario no lo necesite. No tiene casi tantas características como readline, pero reconoce algunos caracteres especiales configurables como borrar (generalmente Retroceso o Eliminar) y matar (generalmente Ctrl-U).

Lo más importante para su pregunta, el modo canónico amortigua la entrada hasta que se ve el carácter de fin de línea. Debido a que el búfer está en el controlador tty, en la memoria del kernel, no es muy grande.

Puede desactivar el modo canónico con stty cbreako stty -icanon, y luego pegar. Esto tiene la desventaja significativa de que no podrá enviar un EOF con Ctrl-D. Esa es otra de las cosas de las que es responsable el modo canónico. Aún podrá terminar catcon Ctrl-C porque los caracteres generadores de señal están controlados por una bandera separada ( stty rawo stty -isig).

El misterio para mí es por qué, dado que ya has demostrado que sabes xclip, no solo usas xclip -o > fileelcat


fuente
1
El misterio se puede resolver fácilmente: parece que artfulrobot quiere llenar rápidamente un archivo en un host remoto con datos del portapapeles. En el shell remoto, normalmente no hay acceso directo al portapapeles local a través de xclip.
jofel
3
Ah, buen viejo subir-pegar. Si tuviera que hacer uno de esos y no fuera texto simple, lo codificaría en lugar de tratar de convencer al controlador tty para que lo pasara. El texto sin formato con líneas enormes también podría manejarse de esa manera.
2

Si lo haces:

stty eol =

Y luego ejecute la demostración sugerida en su EDIT , verá la barra de información en la impresión de test.out . La disciplina de línea del terminal vaciará su salida a su lector a medida que lea cada charol especial en su entrada.

Un terminal de modo canónico de Linux, como se puede configurar con stty icanono probablemente solo stty sane, maneja los siguientes caracteres de entrada especiales ...

  • eof
    • defecto: ^D
    • Termina una línea de entrada y descarga la salida al lector. Debido a que se elimina de la entrada, si se ingresa como el único carácter en una línea, se pasa como una lectura nula , o al final del archivo , al lector.
  • eol
    • predeterminado: sin asignar
    • También termina una línea de entrada, pero no se elimina de la entrada.
  • matar
    • defecto: ^U
    • Borra todas las entradas almacenadas en el búfer.
  • borrar
    • predeterminado: ^H (o posiblemente @o ^?en algunos sistemas)
    • Borra el último carácter de entrada almacenado en el búfer.

Cuando iexten también está configurado, como stty icanon iexten, o nuevamente, probablemente solo stty sane, un terminal canónico de Linux también manejará ...

  • eol2
    • predeterminado: sin asignar
    • También también termina una línea de entrada y tampoco se elimina de la entrada.
  • borrar
    • defecto: ^W
    • Borra la última palabra de entrada almacenada en el búfer .
  • rprnt
    • defecto: ^R
    • Reimprime todas las entradas almacenadas en búfer.
  • a continuación
    • defecto: ^V
    • Elimina cualquier significado especial en lo que respecta a la disciplina de línea para el carácter de entrada inmediatamente siguiente.

Estos caracteres se manejan eliminándolos de la secuencia de entrada, a excepción de eol y eol2 , es decir, y realizando la función especial asociada antes de pasar la secuencia procesada al lector, que generalmente es su shell, pero podría ser cualquier grupo de procesos en primer plano .

Otros caracteres de entrada especiales que se manejan de manera similar pero que se pueden configurar independientemente de cualquier configuración de icanon incluyen el conjunto de isig , configurado como stty isigy probablemente también incluido en una configuración sensata :

  • dejar
    • defecto: ^\
    • Vacía toda la entrada almacenada en el búfer (si noflsh no está configurado) y envía SIGQUIT al grupo de procesos en primer plano, lo que probablemente genere un volcado del núcleo.
  • susp
    • defecto: ^Z
    • Vacía toda la entrada almacenada en el búfer (si noflsh no está configurado) y envía SIGTSTP al grupo de procesos en primer plano. El grupo de procesos suspendido probablemente se puede reanudar con cualquiera de kill -CONT "$!"o fgen un ( set -m) shell controlado por trabajo.
  • intr
    • defecto: ^C
    • Vacía toda la entrada almacenada en el búfer (si noflsh no está configurado) y envía SIGINT al grupo de procesos en primer plano.

Y el conjunto ixon , configurado como stty ixony también generalmente incluido en una configuración sensata :

  • detener
    • defecto: ^S
    • Detiene toda la salida al lector hasta que se lee el inicio en la entrada o, cuando ixany también está configurado, se lee al menos un carácter más.
  • comienzo
    • defecto: ^Q
    • Reinicia la salida si se ha detenido previamente con stop .
  • Tanto la detención como el inicio se eliminan de la entrada cuando se procesan, pero si la salida se reinicia debido a cualquier carácter en la entrada cuando se establece ixany , ese carácter no se elimina.

Los caracteres especiales manejados en otros sistemas que no son Linux pueden incluir ...

  • rubor
    • defecto: ^O
    • Alterna el descarte y el vaciado de la entrada almacenada en búfer y se elimina de la entrada.
  • dsusp
    • predeterminado: sin asignar
    • Vacía todas las entradas almacenadas solo cuando el lector lee el carácter de entrada especial asignado y luego envía SIGTSTP.

Y posiblemente...

  • swtch
    • predeterminado ^@ (significado \0o NUL)
    • Cambia las capas de concha en primer plano. Para usar con la aplicación de shl capas de shell en algunos sistemas.
    • Una implementación shlque multiplexa ptys y, por lo tanto, es compatible con el control del trabajo en lugar del comportamiento dependiente de swtch de la implementación original se puede tener libremente en el heirloom-toolchestconjunto de herramientas.

Para obtener una imagen más clara de cómo y por qué (y quizás por qué no) se manejan estas funciones de entrada, consulte man 3 termios.

Todas las funciones anteriores se pueden asignar (o reasignar) , cuando corresponda, como sttyfunction assigned-key. Para deshabilitar cualquier función individual, haga . Alternativamente, como varios intentos con asignaciones para cualquiera de las funciones de edición de líneas mencionadas anteriormente con todas las implementaciones de GNU, AST o reliquia parecen indicar, también puede, como la asignación NUL para cualquier función parece ser equivalente a configurarla como no asignada en mi Linux sistema.sttyfunction^-sttysttyfunction^@

Probablemente ve un eco de estos caracteres cuando los escribe (como se puede configurar probablemente con [-] ctlecho ) , pero esto es solo un marcador que le muestra dónde lo hizo: el programa que recibe su entrada no tiene noción de que usted los escribió (excepto eol [2] , es decir) y recibe solo una copia de su entrada a la que la disciplina de línea ha aplicado sus efectos.

Una consecuencia del manejo por parte del terminal de las diversas funciones de edición de línea es que debe necesitar un buffer de entrada hasta cierto punto para actuar sobre las funciones que le indique que debería, y por lo tanto no puede haber un suministro ilimitado de entrada que podrías matar en cualquier momento . El buffer de línea es más precisamente el buffer de eliminación .

Si establece los caracteres eol o eol2 en algún delimitador que se produce en la entrada, incluso si ninguno es una línea nueva o un carácter de retorno, por ejemplo, solo podrá matar hasta el punto en que ocurrió por última vez y su búfer de muerte se extenderá tanto como sea posible hasta que se produzca el siguiente de estos, o una nueva línea (o regrese si icrnl está configurado y no está activado igncr ) en la entrada.

mikeserv
fuente
1

cataceptará cualquier número de caracteres, como puede observar haciendo, por ejemplo cat /dev/random > test.bin(no lo haga a menos que sepa cómo detenerlo :). Intenté copiar y pegar un archivo grande en cat > test.txt. Todas las líneas terminaron en el archivo si cancelé con Ctrl- co Ctrl- d, pero en el primer caso no todas las líneas se imprimieron en el terminal . Creo que esto se debe a que catamortigua su impresión, esperando un buffer completo de texto o entrada directa desde el terminal antes de cada impresión.

En mi sistema, creo que el tamaño del búfer es 4096 (2 ^ 12) bytes: cree un archivo de 4095 bytes (printf '1234567890%.0s' {1..409} && printf 12345) > test.in, cárguelo en el búfer de copia usando xclip test.in, inicie cat > test.out, pegue usando Shift- Inserty finalice la secuencia presionando Ctrl- d. Ahora agregue un byte usando printf '6' >> test.in, y la secuencia se imprime dos veces : una vez en la catsalida (todos los 4096 bytes), y los últimos 4095 bytes nuevamente en el shell después de terminar.

l0b0
fuente
+1 En mi caso, también dependía del portapapeles utilizado. Si usé el búfer de selección (pegar con un clic central) solo vi las primeras 4542 líneas de mis datos de prueba (pero todas terminaron en el archivo creado) pero usando el portapapeles X (Ctrl + C / Ctrl + V) vi todo ello. En ambos casos, todos los datos se imprimieron en el archivo resultante, pero en el primero solo se mostraron datos parciales en el terminal.
terdon
1
No tengo el mismo comportamiento. Ver pregunta editada
artfulrobot
0

Una solución es pegarlo en un editor que admita líneas largas, por ejemplo vim.

Si usa vim, primero ingrese al modo pegar con :pasteantes de ingresar al modo insertar con iy pegue el texto.

Hjulle
fuente