Como ler mais de 4k de entrada sem novas linhas em um terminal?

25

Portanto, tenho muitos dados SEM NOVAS LINHAS na área de transferência (é um arquivo SVG grande em uma linha). eu fui

$ cat >file.svg

depois tentou colar (no Gnome Terminal), mas apenas os primeiros caracteres de 4kB foram aceitos.

Presumo que este seja um recurso / limitação de readline.

Existe uma maneira de ler o STDIN que evite esse problema?

EDITAR

Caso de teste: Crie um arquivo de demonstração. Este terá ~ 4k "=" símbolos seguidos por "foo bar".

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

Copie isso para a área de transferência

xclip test.in

(se você quiser clicar com o botão do meio para inserir) ou

xclip -selection clipboard test.in

(se você quiser usar Ctrl-Shift-Insert para colar)

Em seguida cat >test.out, cole (de qualquer maneira). Pressione Ctrl-D para finalizar o fluxo. cat test.out- você vê "foo bar"?

Na minha instalação (Ubuntu 12.04, Gnome Terminal, zsh), quando colo, vejo apenas o =e não vejo foo bar. Mesmo quando inspeciono test.out.

artfulrobot
fonte
Tem certeza de que seu arquivo SVG foi totalmente lido na sua área de transferência?
Lgeorget 20/05
Qual é o seu problema real? Como armazenar o conteúdo da área de transferência em um arquivo? Nesse caso, existe outra maneira de colar no terminal.
Lgeorget 20/05
quanto é N no seu caso? Eu tentei com 2kB de dados xml (inc LF) não há problema.
Fduff
11
@artfulrobot Um processo em primeiro plano interage diretamente com o tty / pty. O shell não está envolvido. Você pode ver isso porque não possui recursos de linha de leitura (comandos de edição / salto, histórico, ...) em programas se eles não usarem a linha de leitura ou qualquer outra biblioteca de entrada.
Jofel
11
Esta não é uma limitação da linha de leitura - a linha de leitura e o bash não estão envolvidos aqui. É uma limitação da interface do terminal.
Gilles 'SO- stop be evil'

Respostas:

22

Se eu entendo a fonte corretamente, no Linux, o número máximo de caracteres que podem ser lidos de uma só vez em um terminal é determinado pela N_TTY_BUF_SIZEfonte do kernel. O valor é 4096.

Essa é uma limitação da interface do terminal, especificamente o modo canônico ("cozido"), que fornece um editor de linhas extremamente bruto (backspace, enter, Ctrl+ Dno início de uma linha para o final do arquivo). Isso acontece inteiramente fora do processo que está sendo lido.

Você pode alternar o terminal para o modo bruto, o que desativa o processamento de linha. Ele também desativa Ctrl+ De outras sutilezas, sobrecarregando seu programa.

Esta é uma antiga limitação do Unix que nunca foi corrigida porque há pouca motivação. Os seres humanos não entram nessas longas filas. Se você estivesse alimentando a entrada de um programa, redirecionaria a entrada do programa a partir de um arquivo ou canal.

Por exemplo, para usar o conteúdo da área de transferência do X, canalize de xselou xclip. No seu caso:

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

Remova -bou -selection clipboarduse a seleção X (aquela que é definida destacando com o mouse) em vez da área de transferência.

No OSX, use pbpastepara colar o conteúdo da área de transferência (e pbcopyconfigurá-lo).

Você pode acessar a área de transferência do X por SSH se ativar o encaminhamento do X11 ssh -X(o que alguns servidores podem proibir). Se você só pode usar sshsem encaminhamento X11, você pode usar scp, sftpou sshfspara copiar um arquivo.

Se colar é a única solução, porque você não pode encaminhar a área de transferência ou não está colando, mas, por exemplo, falsificando a digitação em uma máquina virtual, uma abordagem alternativa é codificar os dados em algo que tenha novas linhas. Base64 é adequado para isso: transforma dados arbitrários em caracteres imprimíveis e ignora os espaços em branco ao decodificar. Essa abordagem tem a vantagem adicional de suportar dados arbitrários na entrada, até controlar caracteres que o terminal interpretaria ao colar. No seu caso, você pode codificar o conteúdo:

xsel -b | base64 | xsel -b

então decodifique:

base64 -d
 Paste
Ctrl+D
Gilles 'SO- parar de ser mau'
fonte
Nota há um realmente desagradável corrupção de dados erro ao usar xselcom> 4K bytes: github.com/kfish/xsel/issues/14
Patrick
14

O limite você está correndo em é o tamanho máximo de uma linha no modo de entrada canônica , MAX_CANON.

No modo de entrada canônica, o driver tty fornece serviços básicos de edição de linha, para que o programa de espaço do usuário não precise. Ele não possui tantos recursos quanto a linha de leitura, mas reconhece alguns caracteres especiais configuráveis ​​como apagar (geralmente Backspace ou Delete) e kill (geralmente Ctrl-U).

Mais importante para sua pergunta, o modo canônico armazena em buffer até que o caractere de fim de linha seja visto. Como o buffer está no driver tty, na memória do kernel, não é muito grande.

Você pode desativar o modo canônico com stty cbreakou stty -icanone, em seguida, cole. Isso tem a desvantagem significativa de que você não poderá enviar um EOF com Ctrl-D. Essa é outra das coisas pelas quais o modo canônico é responsável. Você ainda poderá finalizar catcom Ctrl-C porque os caracteres geradores de sinal são controlados por um sinalizador separado ( stty rawou stty -isig).

O mistério para mim é por que, como você já demonstrou ter conhecimento xclip, não usa apenas xclip -o > fileocat


fonte
11
O mistério pode ser facilmente resolvido: parece que o artfulrobot deseja preencher rapidamente um arquivo em hosts remotos com dados da área de transferência. No shell remoto, normalmente não há acesso direto à área de transferência local via xclip.
Jofel
3
Ah, bom e velho upload por colar. Se eu tivesse que fazer um desses e não fosse texto simples, eu o codificaria em vez de tentar convencer o motorista tty a passar por ele. Texto simples com linhas enormes também poderia ser tratado dessa maneira.
2

Se você fizer:

stty eol =

E, em seguida, execute a demonstração sugerida na sua edição , você verá a barra foo na impressão de test.out . A disciplina de linha do terminal liberará sua saída para o leitor à medida que ele lê cada caractere eol especial em sua entrada.

Um terminal de modo canônico do Linux - como pode ser configurado com stty icanonou provavelmente apenas stty sane- lida com os seguintes caracteres de entrada especiais ...

  • eof
    • padrão: ^D
    • Encerra uma linha de entrada e libera a saída para o leitor. Como é removido da entrada, se for inserido como o único caractere em uma linha, é passado como uma leitura nula - ou final do arquivo - para o leitor.
  • eol
    • padrão: não atribuído
    • Também termina uma linha de entrada, mas não é removida da entrada.
  • mate
    • padrão: ^U
    • Apaga toda a entrada em buffer.
  • apagar
    • padrão: ^H (ou possivelmente @ou ^?em alguns sistemas)
    • Apaga o último caractere de entrada em buffer.

Quando o iexten também é configurado - como stty icanon iextenou, novamente, provavelmente apenas stty sane, um terminal canônico do Linux também manipula ...

  • eol2
    • padrão: não atribuído
    • Além disso , também termina uma linha de entrada, e é também não removido a partir da entrada.
  • werase
    • padrão: ^W
    • Apaga a última palavra de entrada em buffer .
  • rprnt
    • padrão: ^R
    • Reimprime todas as entradas em buffer.
  • proxima
    • padrão: ^V
    • Remove qualquer significado especial no que diz respeito à disciplina de linha para o caractere de entrada imediatamente seguinte.

Esses caracteres são manipulados removendo-os do fluxo de entrada - exceto eol e eol2 , ou seja - e executando a função especial associada antes de passar o fluxo processado para o leitor - que geralmente é seu shell, mas pode ser o grupo de processos em primeiro plano. .

Outros caracteres de entrada especiais que são tratados de maneira semelhante, mas podem ser configurados independentemente de qualquer configuração icanon , incluem o isig set - set like stty isige provavelmente também incluído em uma configuração :

  • Sair
    • padrão: ^\
    • Libera toda a entrada em buffer (se noflsh não estiver definido) e envia SIGQUIT para o grupo de processos em primeiro plano - provavelmente gerando um core-dump.
  • susp
    • padrão: ^Z
    • Libera toda a entrada em buffer (se noflsh não estiver definido) e envia o SIGTSTP ao grupo de processos em primeiro plano. O grupo de processos suspenso provavelmente pode ser retomado com um kill -CONT "$!"ou apenas fgem um ( set -m) shell controlado por tarefa.
  • intr
    • padrão: ^C
    • Limpa toda a entrada em buffer (se noflsh não estiver definido) e envia o SIGINT ao grupo de processos em primeiro plano.

E o conjunto ixon - configurado como stty ixone também geralmente incluído em uma configuração :

  • Pare
    • padrão: ^S
    • Interrompe toda a saída do leitor até que o início seja lido na entrada ou - quando ixany também está definido - pelo menos mais um caractere é lido.
  • começar
    • padrão: ^Q
    • Reinicia a saída se ela tiver sido parada anteriormente com parada .
  • Ambos parada e começar são removidos da entrada quando processado, mas se a saída for reiniciado devido a qualquer personagem na entrada quando ixany está definido então que o personagem não é removido.

Caracteres especiais manipulados em outros sistemas não Linux podem incluir ...

  • rubor
    • padrão: ^O
    • Alterna o descarte e a descarga da entrada em buffer e é removida da entrada.
  • dsusp
    • padrão: não atribuído
    • Liberta toda a entrada em buffer somente quando o leitor lê o caractere de entrada especial atribuído e envia o SIGTSTP.

E possivelmente...

  • swtch
    • padrão ^@ (significado \0ou NUL)
    • Alterna camadas de shell em primeiro plano. Para uso com o aplicativo shl shell-layers em alguns sistemas.
    • Uma implementação shlque multiplexa ptys e, portanto, é compatível com o controle de tarefas, em vez do comportamento dependente de swtch da implementação original, pode ser livremente realizada no heirloom-toolchestconjunto de ferramentas.

Para uma imagem mais clara de como e por que (e talvez por que não) essas funções de entrada são tratadas, consulte man 3 termios.

Todas as funções acima podem ser atribuídas (ou reatribuídas) - quando aplicável - como sttyfunction assigned-key. Para desativar qualquer função única, faça . Como alternativa, como várias tentativas com atribuições para qualquer uma das funções de edição de linha mencionadas acima, com todas as implementações do GNU, AST ou da herança parecem indicar, você também pode, pois a atribuição NUL para qualquer função parece equiparar a configuração não atribuída no meu linux sistema.sttyfunction^-sttysttyfunction^@

Provavelmente, você vê um eco desses caracteres ao digitá-los (como provavelmente pode ser configurado com [-] ctlecho ) , mas esse é apenas um marcador para mostrar a você o local onde você fez - o programa que recebe sua entrada não faz ideia de que você digitou-os (exceto eol [2] , isto é) e recebe apenas uma cópia de sua entrada na qual a disciplina de linha aplicou seus efeitos.

Uma conseqüência do manuseio do terminal das várias funções de edição de linha é que ele precisa proteger a entrada até certo ponto, a fim de atuar sobre as funções que você indicar que deveria - e, portanto, não pode haver um suprimento ilimitado de entrada que você pode a qualquer momento matar . A linha de tampão é mais precisamente a matança tampão.

Se você definir as EOL ou eol2 caracteres para alguns delimitador que ocorre na entrada - mesmo que nem é uma nova linha ou um caractere de retorno, por exemplo - então você só vai ser capaz de matar até o ponto que ocorreu pela última vez e sua matança tampão vai se estender o máximo possível até a próxima - ou uma nova linha (ou retornar se icrnl estiver definido e igncr não) - ocorrer na entrada.

mikeserv
fonte
1

cataceitará qualquer número de caracteres, como você pode testemunhar, por exemplo cat /dev/random > test.bin(não faça isso a menos que saiba como pará-lo :). Eu tentei copiar e colar um arquivo grande em cat > test.txt. Todas as linhas terminaram no arquivo se eu cancelei com Ctrl- cou Ctrl- d, mas no primeiro caso, nem todas as linhas foram impressas no terminal . Acredito que isso ocorre porque o catbuffer é impresso, aguardando um buffer completo de texto ou entrada direta do terminal antes de cada impressão.

No meu sistema, acho que o tamanho do buffer é 4096 (2 ^ 12) bytes: Crie um arquivo de 4095 bytes usando (printf '1234567890%.0s' {1..409} && printf 12345) > test.in, carregue-o no buffer de cópia usando xclip test.in, inicie cat > test.out, cole usando Shift- Inserte encerre o fluxo pressionando Ctrl- d. Agora adicione um byte usando printf '6' >> test.in, e o fluxo será impresso duas vezes : Uma vez na catsaída (todos os 4096 bytes) e os últimos 4095 bytes novamente no shell após o término.

l0b0
fonte
+1 No meu caso, também dependia da área de transferência usada. Se eu usei o buffer de seleção (pasta com o clique do meio), só vi as primeiras 4542 linhas dos meus dados de teste (mas todas acabaram no arquivo criado), mas usando a área de transferência X (Ctrl + C / Ctrl + V) vi tudo isso. Nos dois casos, todos os dados foram impressos no arquivo resultante, mas no primeiro apenas dados parciais foram mostrados no terminal.
terdon
11
Eu não tenho o mesmo comportamento. Veja pergunta editada
artfulrobot
0

Uma solução é colá-lo em um editor que suporte linhas longas, por exemplo, vim.

Se você usar o vim, primeiro entre no modo de colar com :pasteantes de entrar no modo de inserção ie colar o texto.

Hjulle
fonte