Comment lire sur une entrée 4k sans nouvelles lignes sur un terminal?

25

J'ai donc beaucoup de données SANS NOUVELLES LIGNES dans le presse-papiers (c'est un gros fichier SVG sur une seule ligne). je suis allé

$ cat >file.svg

a ensuite essayé de coller (dans Gnome Terminal), mais seuls les 4 premiers caractères de 4 Ko ont été acceptés.

Je suppose que c'est une fonction / limitation de ligne de lecture.

Existe-t-il un moyen de lire à partir de STDIN qui permettrait d'éviter ce problème?

MODIFIER

Cas de test: créez un fichier de démonstration. Celui-ci aura ~ 4k "=" symboles suivis de "foo bar".

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

Copiez cela dans votre presse-papiers

xclip test.in

(si vous voulez cliquer avec le bouton du milieu pour insérer) ou

xclip -selection clipboard test.in

(si vous souhaitez utiliser Ctrl-Maj-Insert pour le coller)

Ensuite cat >test.out, collez (quelle que soit la façon). Appuyez sur Ctrl-D pour terminer le flux. cat test.out- voyez-vous "foo bar"?

Sur ma configuration (Ubuntu 12.04, Gnome Terminal, zsh) lorsque je colle, je ne vois que le =et je ne le vois pas foo bar. Pareil quand j'inspecte test.out.

artfulrobot
la source
Êtes-vous sûr que votre fichier SVG a été entièrement lu dans votre presse-papiers?
lgeorget
Quel est votre problème réel? Comment stocker le contenu du presse-papiers dans un fichier? Si c'est le cas, il existe un autre moyen que de coller dans le terminal.
lgeorget
combien coûte N dans votre cas? Je l'ai essayé avec 2 Ko de données xml (inc LF) sans problème.
fduff
1
@artfulrobot Un processus de premier plan interagit directement avec le tty / pty. Le shell n'est pas impliqué. Vous pouvez le voir parce que vous n'avez pas de fonctionnalités readline (commandes d'édition / saut, historique, ...) dans les programmes s'ils n'utilisent pas readline ou toute autre bibliothèque d'entrée eux-mêmes.
jofel
1
Ce n'est pas une limitation de readline - readline et bash ne sont pas impliqués ici. C'est une limitation de l'interface du terminal.
Gilles 'SO- arrête d'être méchant'

Réponses:

22

Si je comprends bien la source, sous Linux, le nombre maximum de caractères pouvant être lus en une seule fois sur un terminal est déterminé par N_TTY_BUF_SIZEdans la source du noyau. La valeur est 4096.

Il s'agit d'une limitation de l'interface du terminal, en particulier du mode canonique («cuit») qui fournit un éditeur de ligne extrêmement grossier (retour arrière, entrée, Ctrl+ Dau début d'une ligne pour la fin du fichier). Cela se passe entièrement en dehors du processus de lecture.

Vous pouvez basculer le terminal en mode brut, ce qui désactive le traitement en ligne. Il désactive également Ctrl+ Det d'autres subtilités, mettant un fardeau supplémentaire sur votre programme.

Il s'agit d'une ancienne limitation Unix qui n'a jamais été corrigée car il y a peu de motivation. Les humains n'entrent pas dans de si longues files d'attente. Si vous alimentiez l'entrée d'un programme, vous redirigeriez l'entrée de votre programme à partir d'un fichier ou d'un canal.

Par exemple, pour utiliser le contenu du presse-papiers X, du tuyau depuis xselou xclip. Dans ton cas:

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

Supprimez -bou -selection clipboardpour utiliser la sélection X (celle qui est définie en surlignant avec la souris) plutôt que le presse-papiers.

Sous OSX, utilisez pbpastepour coller le contenu du presse-papiers (et pbcopypour le définir).

Vous pouvez accéder au presse-papiers X via SSH si vous activez le transfert X11 ssh -X(ce que certains serveurs peuvent interdire). Si vous ne pouvez utiliser sshsans transfert X11, vous pouvez utiliser scp, sftpou sshfspour copier un fichier.

Si le collage est la seule solution parce que vous ne pouvez pas transférer le presse-papiers ou que vous ne collez pas, mais p. Base64 est bien adapté à cela: il transforme des données arbitraires en caractères imprimables et ignore les espaces lors du décodage. Cette approche présente l'avantage supplémentaire de prendre en charge des données arbitraires en entrée, même des caractères de contrôle que le terminal interpréterait lors du collage. Dans votre cas, vous pouvez encoder le contenu:

xsel -b | base64 | xsel -b

puis le décoder:

base64 -d
 Paste
Ctrl+D
Gilles 'SO- arrête d'être méchant'
la source
Notez qu'il y a un bogue de corruption de données vraiment désagréable lors de l'utilisation xselavec> 4k octets: github.com/kfish/xsel/issues/14
Patrick
14

La limite que vous utilisez en est la taille maximale d'une ligne en mode d'entrée canonique , MAX_CANON.

En mode d'entrée canonique, le pilote tty fournit des services d'édition de ligne de base afin que le programme de l'espace utilisateur n'en ait pas besoin. Il n'a pas autant de fonctionnalités que readline, mais il reconnaît quelques caractères spéciaux configurables comme l'effacement (généralement Backspace ou Delete) et kill (généralement Ctrl-U).

Plus important encore pour votre question, les tampons en mode canonique sont entrés jusqu'à ce que le caractère de fin de ligne soit visible. Parce que le tampon est dans le pilote tty, dans la mémoire du noyau, il n'est pas très grand.

Vous pouvez désactiver le mode canonique avec stty cbreakou stty -icanon, puis faire votre collage. Cela présente l'inconvénient majeur de ne pas pouvoir envoyer un EOF avec Ctrl-D. C'est une autre des choses dont le mode canonique est responsable. Vous pourrez toujours terminer le catavec Ctrl-C car les caractères générant le signal sont contrôlés par un drapeau séparé ( stty rawou stty -isig).

Le mystère pour moi est pourquoi, puisque vous avez déjà démontré que vous savez xclip, vous ne vous contentez pas d' utiliser au xclip -o > filelieu decat


la source
1
Le mystère peut être facilement résolu: il semble qu'artfulrobot veuille remplir rapidement un fichier sur des hôtes distants avec les données du presse-papiers. Dans le shell distant, il n'y a normalement pas d'accès direct au presse-papiers local via xclip.
jofel
3
Ah, bon vieux téléchargement par collage. Si je devais en faire un et que ce n'était pas du texte brut, je le coderais au lieu d'essayer de convaincre le pilote tty de le passer. Le texte brut avec d'énormes lignes pourrait également être traité de cette façon.
2

Si tu fais:

stty eol =

Et puis exécutez la démo suggérée dans votre EDIT , vous verrez foo bar dans l'impression de test.out . La discipline de ligne du terminal videra sa sortie vers son lecteur pendant qu'il lit chaque caractère EOL spécial dans votre entrée.

Un terminal en mode canonique Linux - tel qu'il peut être configuré avec stty icanonou probablement juste stty sane- gère les caractères d'entrée spéciaux suivants ...

  • eof
    • défaut: ^D
    • Termine une ligne d'entrée et vide la sortie vers le lecteur. Parce qu'il est supprimé de l'entrée, s'il est entré en tant que seul caractère sur une ligne, il est transmis en tant que lecture nulle - ou fin de fichier - au lecteur.
  • eol
    • par défaut: non attribué
    • Termine également une ligne d'entrée, mais n'est pas supprimé de l'entrée.
  • tuer
    • défaut: ^U
    • Efface toutes les entrées en mémoire tampon.
  • effacer
    • par défaut: ^H (ou éventuellement @ou ^?sur certains systèmes)
    • Efface le dernier caractère d'entrée mis en mémoire tampon.

Lorsque iexten est également défini - comme stty icanon iextenou, encore une fois, probablement juste stty sane, un terminal Linux canonique gérera également ...

  • eol2
    • par défaut: non affecté
    • En outre également se termine une ligne d'entrée, et est également pas retiré de l' entrée.
  • effacer
    • défaut: ^W
    • Efface le dernier mot d' entrée mis en mémoire tampon .
  • rprnt
    • défaut: ^R
    • Réimprime toutes les entrées mises en mémoire tampon.
  • lnext
    • défaut: ^V
    • Supprime toute signification particulière en ce qui concerne la discipline de ligne pour le caractère d'entrée immédiatement suivant.

Ces caractères sont gérés en les supprimant du flux d'entrée - à l'exception de eol et eol2 , c'est-à-dire - et en exécutant la fonction spéciale associée avant de transmettre le flux traité au lecteur - qui est généralement votre shell, mais pourrait être le groupe de processus de premier plan quel qu'il soit. .

D'autres caractères d'entrée spéciaux qui sont traités de la même manière mais qui peuvent être configurés indépendamment de tout paramètre icanon incluent le jeu isig - défini comme stty isiget probablement également inclus dans une configuration saine :

  • quitter
    • défaut: ^\
    • Vide toutes les entrées tamponnées (si noflsh n'est pas défini) et envoie SIGQUIT au groupe de processus de premier plan - générant probablement un vidage de mémoire .
  • susp
    • défaut: ^Z
    • Vide toutes les entrées en mémoire tampon (si noflsh n'est pas défini) et envoie SIGTSTP au groupe de processus de premier plan. Le groupe de processus suspendu peut probablement être repris avec kill -CONT "$!"ou simplement fgdans un shell contrôlé par le travail ( set -m) .
  • intr
    • défaut: ^C
    • Vide toutes les entrées tamponnées (si noflsh n'est pas défini) et envoie SIGINT au groupe de processus de premier plan.

Et l' ensemble ixon - configuré comme stty ixonet également généralement inclus dans une configuration saine :

  • Arrêtez
    • défaut: ^S
    • Arrête toutes les sorties vers le lecteur jusqu'à ce que le début soit lu en entrée ou - lorsque ixany est également défini - au moins un caractère supplémentaire soit lu.
  • début
    • défaut: ^Q
    • Redémarre la sortie si elle a déjà été arrêtée avec stop .
  • Les deux arrêt et commencer sont retirés de l' entrée lors d'un traitement, mais si la sortie est redémarré en raison d'un caractère en entrée lorsque ixany est réglé alors que le caractère est pas supprimé.

Les caractères spéciaux gérés sur d'autres systèmes non Linux peuvent inclure ...

  • affleurer
    • défaut: ^O
    • Bascule la suppression et le vidage de l'entrée en mémoire tampon et est supprimé de l'entrée.
  • dsusp
    • par défaut: non affecté
    • Vide toutes les entrées mises en mémoire tampon uniquement lorsque le lecteur lit le caractère d'entrée spécial affecté puis envoie SIGTSTP.

Et peut-être ...

  • swtch
    • par défaut ^@ (sens \0ou NUL)
    • Bascule les calques de premier plan. À utiliser avec l' application shl shell-couches sur certains systèmes.
    • Une implémentation shldont multiplexe ptys et est donc compatible avec le contrôle des tâches plutôt que le comportement dépendant de swtch de l'implémentation d' origine peut être librement disponible dans la heirloom-toolchestsuite d'outils.

Pour une image plus claire de comment et pourquoi (et peut-être pourquoi pas) ces fonctions d'entrée sont gérées, consultez man 3 termios.

Toutes les fonctions ci-dessus peuvent être attribuées (ou réaffectées) - le cas échéant - comme sttyfunction assigned-key. Pour désactiver une fonction unique, faites . Alternativement, comme diverses tentatives avec des affectations pour l'une des fonctions d'édition de ligne susmentionnées avec toutes les implémentations de GNU, AST ou de l'héritage semblent l'indiquer, vous pouvez également, comme l' affectation NUL pour n'importe quelle fonction semble équivaloir à la définir comme non affectée sur mon linux système.sttyfunction^-sttysttyfunction^@

Vous voyez probablement un écho de ces caractères lorsque vous les tapez (comme cela peut probablement être configuré avec [-] ctlecho ) , mais ce n'est qu'un marqueur pour vous montrer où vous en êtes - le programme recevant votre entrée n'a aucune idée que vous les a tapés (sauf eol [2] , c'est-à-dire) et ne reçoit qu'une copie de votre saisie à laquelle la discipline de ligne a appliqué leurs effets.

Une conséquence de la gestion par le terminal des diverses fonctions d'édition de ligne est qu'il doit dans une certaine mesure mettre en mémoire tampon l'entrée afin d'agir sur les fonctions que vous lui indiquez qu'il devrait - et donc il ne peut pas y avoir une fourniture illimitée d'entrée qui vous pourriez à tout moment tuer . Le tampon de ligne est plus précisément le tampon de mise à mort .

Si vous définissez les caractères eol ou eol2 sur un délimiteur qui se produit en entrée - même si ni un saut de ligne ni un caractère de retour, par exemple - alors vous ne pourrez tuer que jusqu'au dernier point et votre tampon de mise à mort s'étendra aussi loin que possible jusqu'à ce que la prochaine - ou une nouvelle ligne (ou retourne si icrnl est défini et igncr ne l'est pas) - se produit en entrée.

mikeserv
la source
1

catacceptera n'importe quel nombre de caractères, comme vous pouvez en témoigner par exemple cat /dev/random > test.bin(ne le faites pas à moins de savoir comment l'arrêter :). J'ai essayé de copier et coller un gros fichier dans cat > test.txt. Toutes les lignes se sont retrouvées dans le fichier, que j'aie annulé avec Ctrl- cou Ctrl- d, mais dans le premier cas, toutes les lignes n'ont pas été imprimées sur le terminal . Je pense que c'est parce que cattampon son impression, attendant un tampon complet de texte ou une entrée directe du terminal avant chaque impression.

Sur mon système, je pense que la taille du tampon est de 4096 (2 ^ 12) octets: créez un fichier de 4095 octets en utilisant (printf '1234567890%.0s' {1..409} && printf 12345) > test.in, chargez-le dans le tampon de copie en utilisant xclip test.in, démarrez cat > test.out, collez en utilisant Shift- Insertet terminez le flux en appuyant sur Ctrl- d. Ajoutez maintenant un octet à l'aide de printf '6' >> test.in, et le flux est imprimé deux fois : une fois dans la catsortie (tous les 4096 octets) et les 4095 derniers octets à nouveau sur le shell après la fin.

l0b0
la source
+1 Dans mon cas, cela dépendait également du presse-papiers utilisé. Si j'ai utilisé le tampon de sélection (pâte de clic du milieu), je n'ai vu que les 4542 premières lignes de mes données de test (mais toutes se sont retrouvées dans le fichier créé) mais en utilisant le presse-papiers X (Ctrl + C / Ctrl + V), j'ai vu tout. Dans les deux cas, toutes les données ont été imprimées dans le fichier résultant, mais dans le premier cas, seules des données partielles étaient affichées dans le terminal.
terdon
1
Je n'ai pas le même comportement. Voir la question modifiée
artfulrobot
0

Une solution consiste à le coller dans un éditeur qui prend en charge les longues lignes, par exemple vim.

Si vous utilisez vim, entrez d'abord en :pastemode icollage avec avant de passer en mode insertion avec et de coller le texte.

Hjulle
la source