Как прочитать более 4k ввода без новых строк на терминале?


25

Поэтому у меня есть много данных без новых строк в буфере обмена (это большой SVG-файл в одну строку). я пошел

$ cat >file.svg

затем попытался вставить (в Gnome Terminal), но были приняты только первые 4 КБ символы.

Я предполагаю, что это функция / ограничение readline.

Есть ли способ прочитать из STDIN, что бы избежать этой проблемы?

РЕДАКТИРОВАТЬ

Тестовый пример: создайте демонстрационный файл. У этого будет ~ 4k символов "=", за которыми следует "foo bar".

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

Скопируйте это в буфер обмена

xclip test.in

(если вы хотите щелкнуть средней кнопкой мыши, чтобы вставить) или

xclip -selection clipboard test.in

(если вы хотите использовать Ctrl-Shift-Insert, чтобы вставить его в)

Затем cat >test.outвставьте (в любом случае). Нажмите Ctrl-D, чтобы закончить поток. cat test.out- Вы видите "Foo Bar"?

На моей установке (Ubuntu 12.04, Gnome Terminal, zsh) при вставке я вижу только то, =что не вижу foo bar. То же самое, когда я проверяю test.out.


Вы уверены, что ваш файл SVG был полностью прочитан в буфер обмена?
lgeorget

В чем ваша проблема? Как сохранить содержимое буфера обмена в файл? Если это так, есть другой способ, кроме вставки в терминале.
lgeorget

сколько N в вашем случае? Я пробовал это с 2 КБ XML-данных (вкл. НЧ) без проблем.
fduff

1
@artfulrobot Процесс переднего плана напрямую взаимодействует с tty / pty. Оболочка не участвует. Вы можете видеть это, потому что у вас нет функций readline (команд редактирования / перехода, истории, ...) в программах, если они сами не используют readline или любую другую входную библиотеку.
Джофель

1
Это не ограничение readline - readline и bash здесь не участвуют. Это ограничение интерфейса терминала.
Жиль "ТАК - перестань быть злым"

Ответы:


22

Если я правильно понимаю источник, в Linux максимальное количество символов, которое можно прочитать за один раз на терминале, определяется N_TTY_BUF_SIZEв исходном коде ядра. Значение 4096.

Это ограничение интерфейса терминала, в частности канонического («приготовленного») режима, который обеспечивает чрезвычайно грубый редактор строк (backspace, enter, Ctrl+ Dв начале строки для конца файла). Это происходит полностью вне процесса, который читает.

Вы можете переключить терминал в необработанный режим, который отключает обработку строк. Он также отключает Ctrl+ Dи другие тонкости, накладывая дополнительную нагрузку на вашу программу.

Это древнее ограничение Unix, которое никогда не было исправлено, потому что там мало мотивации. Люди не заходят в такие длинные очереди. Если бы вы вводили ввод из программы, вы бы перенаправили ввод вашей программы из файла или канала.

Например, чтобы использовать содержимое буфера обмена X, канал из xselили xclip. В твоем случае:

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

Удалите -bили -selection clipboardиспользуйте выбор X (тот, который установлен выделением мышью), а не буфер обмена.

В OSX используйте pbpasteдля вставки содержимого буфера обмена (и pbcopyдля его установки).

Вы можете получить доступ к буферу обмена X через SSH, если активируете переадресацию X11 с помощью ssh -X(которую некоторые серверы могут запретить). Если вы можете использовать только sshбез пересылки X11, вы можете использовать scp, sftpили sshfsскопировать файл.

Если вставка является единственным решением, потому что вы не можете переслать буфер обмена или не вставляете, а, например, имитируете ввод текста на виртуальной машине, альтернативный подход заключается в кодировании данных во что-то, что имеет символы новой строки. Base64 хорошо подходит для этого: он преобразует произвольные данные в печатные символы и игнорирует пробелы при декодировании. Этот подход имеет дополнительное преимущество, заключающееся в том, что он поддерживает произвольные данные на входе, даже управляющие символы, которые терминал будет интерпретировать при вставке. В вашем случае вы можете закодировать контент:

xsel -b | base64 | xsel -b

затем расшифруйте его:

base64 -d
 Paste
Ctrl+D

Обратите внимание, что при использовании xselс размером > 4 Кб существует очень неприятная ошибка, связанная с повреждением данных : github.com/kfish/xsel/issues/14
Патрик

14

Предельное вы бежите в это максимальный размер строки в режиме канонического ввода , MAX_CANON.

В режиме канонического ввода драйвер tty предоставляет базовые сервисы редактирования строк, поэтому программа пользовательского пространства не нуждается в этом. У него не так много функций, как у readline, но он распознает несколько настраиваемых специальных символов, таких как erase (обычно Backspace или Delete) и kill (обычно Ctrl-U).

Что наиболее важно для вашего вопроса, канонический режим буферизует ввод до тех пор, пока не появится символ конца строки. Поскольку буфер находится в драйвере tty, в памяти ядра он не очень велик.

Вы можете отключить канонический режим с помощью stty cbreakили stty -icanon, а затем выполнить вставку. Это имеет существенный недостаток: вы не сможете отправить EOF с помощью Ctrl-D. Это еще одна вещь, за которую отвечает канонический режим. Вы по-прежнему сможете завершать с catпомощью Ctrl-C, потому что генерирующие сигнал символы управляются отдельным флагом ( stty rawили stty -isig).

Для меня загадка заключается в том, почему, поскольку вы уже продемонстрировали, о чем знаете xclip, вы не просто используете xclip -o > fileвместоcat


2
Загадка может быть легко решена: кажется, что artfulrobot хочет быстро заполнить файл на удаленных хостах данными из буфера обмена. В удаленной оболочке обычно нет прямого доступа к локальному буферу обмена через xclip.
Джофель

3
Ах, старая добрая закачка. Если бы мне пришлось сделать один из них, а это был не простой текст, я бы запрограммировал его вместо попытки убедить tty-драйвер пропустить его. Обычный текст с огромными линиями может быть обработан таким же образом.

3

Если вы делаете:

stty eol =

А затем запустите демонстрацию, предложенную в вашем редакторе , вы увидите строку foo в распечатке test.out . Линейная дисциплина терминала будет сбрасывать свой вывод в читатель, когда он читает каждый специальный символ eol в вашем вводе.

Терминал Linux в каноническом режиме - который может быть настроен stty icanonили, возможно, просто stty sane- обрабатывает следующие специальные символы ввода ...

  • ВФ
    • дефолт: ^D
    • Завершает строку ввода и сбрасывает вывод в считыватель. Поскольку он удаляется из ввода, если он вводится как единственный символ в строке, он передается в считыватель как нулевое чтение или конец файла .
  • EOL
    • по умолчанию: неназначенный
    • Также завершает строку ввода, но не удаляется из ввода.
  • убийство
    • дефолт: ^U
    • Стирает весь буферизованный ввод.
  • стирание
    • по умолчанию: ^H (или возможно @или ^?в некоторых системах)
    • Стирает последний буферный символ ввода.

Когда iexten также установлен - как stty icanon iextenили, опять же, вероятно, просто stty sane, канонический терминал Linux также будет обрабатывать ...

  • EOL2
    • по умолчанию: не назначено
    • Также также завершает строку ввода и также не удаляется из ввода.
  • WERASE
    • дефолт: ^W
    • Стирает последнее буферизованное входное слово .
  • rprnt
    • дефолт: ^R
    • Перепечатывает весь буферизованный ввод.
  • СИМВОЛ
    • дефолт: ^V
    • Удаляет любое особое значение в отношении линейной дисциплины для непосредственно следующего входного символа.

Эти символы обрабатываются путем удаления их из входного потока - за исключением EOL и EOL2 , то есть - и выполнять соответствующую специальную функцию перед передачей обработанного потока для читателя - который, как правило , в оболочке, но может быть все , что группа процессов переднего плана ,

Другие специальные входные символы, которые обрабатываются аналогичным образом, но могут быть настроены независимо от любого параметра icanon, включают набор isig - set like stty isigи, вероятно, также включены в нормальную конфигурацию:

  • уволиться
    • дефолт: ^\
    • Сбрасывает весь буферизованный ввод (если noflsh не задан) и отправляет SIGQUIT в группу процессов переднего плана - вероятно, генерируя дамп ядра.
  • сусп
    • дефолт: ^Z
    • Очищает все буферизованные входные данные (если noflsh не установлен) и отправляет SIGTSTP в группу процессов переднего плана. Приостановленная группа процессов, вероятно, может быть возобновлена ​​с помощью kill -CONT "$!"или только fgв (- set -m) управляемой заданием оболочке.
  • ВВЕДЕНИ
    • дефолт: ^C
    • Очищает весь буферизованный ввод (если noflsh не установлен) и отправляет SIGINT в группу процессов переднего плана.

И набор ixon - настроен как stty ixonи также обычно включается в нормальный конфиг:

  • стоп
    • дефолт: ^S
    • Останавливает весь вывод в считыватель до тех пор, пока либо не будет прочитано начало при вводе, либо - когда также установлена ixany - будет прочитан хотя бы еще один символ.
  • Начало
    • дефолт: ^Q
    • Перезапускает вывод, если он был ранее остановлен с остановкой .
  • И обработчик останова, и запуск удаляются из ввода при обработке, но если выход перезапускается из-за какого-либо символа на входе, когда установлен ixany, то этот символ не удаляется.

Специальные символы, обрабатываемые в других системах, отличных от Linux, могут включать ...

  • румянец
    • дефолт: ^O
    • Переключает сброс и сброс буферизованного ввода и удаляется из ввода.
  • DSUSP
    • по умолчанию: не назначено
    • Очищает все буферизованные входные данные только тогда, когда считыватель читает назначенный специальный символ ввода и затем отправляет SIGTSTP.

И возможно ...

  • swtch
    • по умолчанию ^@ (значение \0или NUL)
    • Переключение переднего слоя оболочки. Для использования с приложением shl shell-layer в некоторых системах.
    • Реализация, shlкоторая мультиплексирует ptys и, следовательно, совместима с управлением заданиями, а не с зависимым от swtch поведением оригинальной реализации, может свободно использоваться в heirloom-toolchestнаборе инструментов.

Для более ясного представления о том, как и почему (и, возможно, почему нет) эти функции ввода обрабатываются, обратитесь к man 3 termios.

Все вышеперечисленные функции могут быть назначены (или переназначены) - если применимо - как sttyfunction assigned-key. Чтобы отключить любую функцию, сделайте . В качестве альтернативы, поскольку различные попытки с назначениями для любой из вышеупомянутых функций редактирования строк со всеми реализациями GNU, AST или семейной реликвии, по- видимому, указывают, вы также можете, поскольку назначение NUL для любой функции, по-видимому, равносильно установке его как неназначенного в моем linux система.sttyfunction^-sttysttyfunction^@

Возможно, вы видите эхо этих символов при их вводе (что, вероятно, можно настроить с помощью [ / ] ctlecho ) , но это всего лишь маркер, показывающий, где вы работали - программа, получающая ваши данные, не имеет представления о том, что вы набрал их (исключая eol [2] , то есть) и получил только копию вашего ввода, к которому дисциплина применяет свои эффекты.

Следствием того, что терминал обрабатывает различные функции редактирования строк, является то, что ему необходимо в некоторой степени буферизовать ввод для того, чтобы воздействовать на функции, которые вы указываете ему, что он должен, - и поэтому не может быть неограниченной подачи ввода, которая Вы можете в любое время убить . Линия буфер более точно убить буфера.

Если вы установите Eol или EOL2 символы в какой - то разделитель , который происходит на входе - даже если ни один не символ перевода строки или возврата символов, например , - тогда вы будете только в состоянии убить до такой степени , что это в последний раз произошло и ваш убить буфер будет расширяться, насколько это возможно, до тех пор, пока следующая из них - или новая строка (или возврат, если icrnl установлен, а igncr - нет) - не появится на входе.


1

catбудет принимать любое количество символов, как вы могли бы засвидетельствовать, например, cat /dev/random > test.bin(не делайте этого, если вы не знаете, как это остановить :). Я попытался скопировать и вставить большой файл в cat > test.txt. Все строки оказались в файле независимо от того, был ли я отменен с помощью Ctrl- cили Ctrl- d, но в первом случае не все строки были напечатаны на терминале . Я считаю, что это потому, что catбуферизует его печать, ожидая полного буфера текста или прямого ввода с терминала перед каждой печатью.

В моей системе я думаю, что размер буфера составляет 4096 (2 ^ 12) байт: создайте файл размером 4095 байт (printf '1234567890%.0s' {1..409} && printf 12345) > test.in, загрузите его в буфер копирования с помощью xclip test.in, запустите cat > test.out, вставьте с помощью Shift- Insertи завершите поток нажатием Ctrl- d. Теперь добавьте байт с помощью printf '6' >> test.in, и поток будет напечатан дважды : один раз на catвыходе (все 4096 байтов) и последние 4095 байтов снова на оболочке после завершения.


+1 В моем случае это также зависело от используемого буфера обмена. Если я использовал буфер выбора (вставка по среднему щелчку мыши), я видел только первые 4542 строки моих тестовых данных (но все они оказались в созданном файле), но используя буфер обмена X (Ctrl + C / Ctrl + V), я увидел все это. В обоих случаях все данные были напечатаны в результирующий файл, но в первом случае в терминале были показаны только частичные данные.
Тердон

1
Я не получаю такое же поведение. Посмотреть отредактированный вопрос
artfulrobot

0

Одно из решений - вставить его в редактор, который поддерживает длинные строки, например, vim.

Если вы используете vim, сначала войдите в режим вставки с, :pasteпрежде чем войти в режим iвставки с помощью и вставить текст.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.