EnterキーでEOLが送信されないのはなぜですか?


19

Unix / Linux EOLはLF、改行、ASCII 10、エスケープシーケンス\nです。

キーを1回押すだけのPythonスニペットを次に示します。

import sys, tty, termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
    tty.setraw(sys.stdin.fileno())
    ch = sys.stdin.read(1)
finally:
    termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
    return ch

Enterこのスニペットに応答してキーボードを押すと\r、キャリッジリターン、ASCII 13が返されます。

のWindowsEnter送信しますCR LF == 13 10。* nixはWindowsではありません。なぜEnter10ではなく13を与えるのですか?


2バイトを読んでみてください。
マイケルハンプトン

@MichaelHamptonいや、1つのバイトが読み取られた後、そのファイルディスクリプタを待っているそこの何もない

回答:


11

一方でトーマス・ディッキーの答えは非常に正確である、ステファンChazelasは正しく変換が石に設定されていないことディッキーの回答にコメントで述べました。それはライン規律の一部です。

実際、翻訳は完全にプログラム可能です。

男3つのtermiosの manページには、基本的にすべての関連情報が含まれています。(リンクはLinuxのman-pagesプロジェクトに移動します。このプロジェクトでは、Linux専用の機能と、POSIXまたは他のシステムに共通の機能が記載されています。常に各ページのConforming toセクションを確認してください。)

iflag(端末属性old_settings[0]に質問に示すコードではPythonは)すべてPOSIXyシステムの3つの関連するフラグがあります。

  • INLCR:設定されている場合、入力時にNLをCRに変換します
  • ICRNL:設定されている場合(設定されてIGNCRいない場合)、入力時にCRをNLに変換します
  • IGNCR:入力時にCRを無視

同様に、関連する出力設定(old_settings[1])もあります。

  • OPOST:出力処理を有効にします。
  • OCRNL:出力でCRをNLにマップします。
  • ONLCR:出力時にNLをCRにマップします。(XSI。すべてのPOSIXまたはSingle-Unix-Specificationシステムで使用できるわけではありません。)
  • ONOCR:最初の列のCRをスキップします(出力しません)。
  • ONLRET:スキップ(出力しない)CR。

たとえば、ttyモジュールに依存しないようにすることができます。「makeraw」操作は、フラグのセットをクリアするだけです(そしてCS8oflag を設定します)。

import sys
import termios

fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
ch = None

try:
    new_settings = termios.tcgetattr(fd)
    new_settings[0] = new_settings[0] & ~termios.IGNBRK
    new_settings[0] = new_settings[0] & ~termios.BRKINT
    new_settings[0] = new_settings[0] & ~termios.PARMRK
    new_settings[0] = new_settings[0] & ~termios.ISTRIP
    new_settings[0] = new_settings[0] & ~termios.INLCR
    new_settings[0] = new_settings[0] & ~termios.IGNCR
    new_settings[0] = new_settings[0] & ~termios.ICRNL
    new_settings[0] = new_settings[0] & ~termios.IXON
    new_settings[1] = new_settings[1] & ~termios.OPOST
    new_settings[2] = new_settings[2] & ~termios.CSIZE
    new_settings[2] = new_settings[2] | termios.CS8
    new_settings[2] = new_settings[2] & ~termios.PARENB
    new_settings[3] = new_settings[3] & ~termios.ECHO
    new_settings[3] = new_settings[3] & ~termios.ECHONL
    new_settings[3] = new_settings[3] & ~termios.ICANON
    new_settings[3] = new_settings[3] & ~termios.ISIG
    new_settings[3] = new_settings[3] & ~termios.IEXTEN
    termios.tcsetattr(fd, termios.TCSANOW, new_settings)
finally:
    termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)

return ch

ただし、互換性のために、最初にtermiosモジュールにこれらの定数がすべて存在するかどうかを確認することをお勧めします(非POSIXシステムで実行する場合)。また、を使用new_settings[6][termios.VMIN]new_settings[6][termios.VTIME]て、保留中のデータがない場合に読み取りをブロックするかどうか、およびその長さ(整数デシ秒単位)を設定できます。(通常VMINは0に設定され、VTIME読み取りがすぐに返される場合は0に、読み取りが最大で待機する時間(10分の1秒)に正の数に設定されます。)

ご覧のとおり、上記の(および一般に "makeraw")入力時のすべての変換を無効にします。これにより、猫が見ている動作が説明されます。

    new_settings[0] = new_settings[0] & ~termios.INLCR
    new_settings[0] = new_settings[0] & ~termios.ICRNL
    new_settings[0] = new_settings[0] & ~termios.IGNCR

通常の動作を得るには、これら3行をクリアする行を省略するだけで、「生」の場合でも入力変換は変更されません。

このnew_settings[1] = new_settings[1] & ~termios.OPOST行は、他の出力フラグの内容に関係なく、すべての出力処理を無効にします。出力処理をそのまま維持するために、単に省略できます。これにより、rawモードでも出力が「通常」に維持されます。(入力が自動的にエコーされるかどうかには影響しません。それはのECHOcflag によって制御されnew_settings[3]ます。)

最後に、新しい属性が設定されると、新しい設定のいずれかが設定されていれば呼び出しは成功します。設定が重要な場合(たとえば、コマンドラインでパスワードを要求している場合)、新しい設定を取得し、重要なフラグが正しく設定/設定解除されていることを確認してください。

現在の端末設定を確認するには、次を実行します

stty -a

通常、入力フラグは4行目にあり、出力フラグは5行目にあり-、フラグが設定されていない場合はフラグ名の前に付きます。たとえば、出力は

speed 38400 baud; rows 58; columns 205; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?; swtch = M-^?; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts
-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc ixany imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke

疑似端末およびUSB TTYデバイスでは、ボーレートは無関係です。

パスワードなどの読み取りを希望するBashスクリプトを作成する場合は、次のイディオムを検討してください。

#!/bin/bash
trap 'stty sane ; stty '"$(stty -g)" EXIT
stty -echo -echonl -imaxbel -isig -icanon min 1 time 0

EXITシェルが終了したときにトラップが実行されます。stty -g現在の設定をする場合、スクリプトが終了すると、自動的に復元されているので、スクリプトの開始時に、端末の現在の設定を読み取ります。Ctrl+を使用してスクリプトを中断することもできCますが、それは正しいことをします。(信号があるいくつかのコーナーケースでは、端末が時々生/非標準設定で立ち往生することがわかりました(端末でタイプreset+ Enter盲目的に入力する必要があります)がstty sane、実際の元の設定を復元する前に実行すると、私です。だからそこにあるのです。一種の安全性です。)

readbashビルトインを使用して(端末に選択されていない)入力行を読み取ることができます。

IFS=$'\0'
input=""
while read -N 1 c ; do
    [[ "$c" == "" || "$c" == $'\n' || "$c" == $'\r' ]] && break
    input="$input$c"
done

IFSASCII NULに設定しない場合、readビルトインは区切り文字を消費するため、区切り文字はc空になります。若い選手のためのrap。


1
ああ、神々ために、何もありません今までシンプル:(

それは、他の1が大きいにも関わらず、PythonのDEVとして私に最も便利ですので、私はこの答えを受け入れるよ
猫は

2
@cat:これはあなたにとって最も役立つかもしれませんが、トーマス・ディッキーの答えはまだ正しいと思います。代わりに受け入れてください。
公称動物

4
あなたの+15の担当者を辞退する意欲はあなたに与えられますが、@ catはまったく正しいです。回答が受け入れられるかどうかは、それが投稿された回答の「最も正しい」ことを示すものではありません。それは、個人的な理由が何であれ、OPがそれを好んだことを意味するだけです。通常、「最も正しい」ものが最も高く支持されます。回答を受け入れるかどうかは個人の好み次第であり、OPがあなたのものを好む場合、受け入れない理由はありません。
テルドン

1
@terdon:わかりました、それでは修正します。
公称動物

30

基本的に「手動タイプライター以来、そのように行われているため」。本当に。

手動タイプライターには、用紙が送られるキャリッジがあり、入力すると前方に移動し(スプリングをロード)、キャリッジを解放し、スプリングがキャリッジを左マージンに戻すレバーまたはキーがありました。

電子データ入力(テレタイプなど)が導入されたとき、彼らはそれを進めました。それでEnter多くの端末のキーはラベルされるでしょうReturn

キャリッジを左マージンに戻した後に(手動プロセスで)改行が発生しました。繰り返しますが、電子デバイスは手動デバイスを模倣し、別個のline-feed操作を行いました。

どちらの操作もエンコードされているため(テレタイプが紙の種類を作成するスタンドアロンデバイス以上になるように)、CR(キャリッジリターン)とLF(改行)があります。ASR 33 Teletype Informationのこの画像は、キーボードReturnの右側とLine-Feed左側を示しています。上にある、それが主な鍵となりました。

ここに画像の説明を入力してください

Unixは後に登場しました。その開発者は、物事を短縮するのが好きでした(creat「作成」も含めて、すべての略語を見てください)。おそらく2つの部分から成るプロセスに直面して、改行はキャリッジリターンが先行する場合にのみ意味があると判断しました。そこで、それらはfilesから明示的な復帰を削除し、端末のReturnキーを変換して対応する改行を送信しました。混乱を避けるため、改行を「改行」と呼びました。

端末でテキストを書くとき、Unixは逆方向に変換します。改行はキャリッジリターン/改行になります。

(つまり、「通常」:いわゆる「調理モード」。これは、翻訳が行われない「生」モードとは対照的です)。

概要:

  • キャリッジリターン/ラインフィードはシーケンス13 10
  • デバイスは、(「永遠に」あなたの用語であるため)13を送信します
  • Unixライクなシステムはそれを13 10に変更します
  • 他のシステムは必ずしも10個だけを保存するわけではありません(Windowsは、互換性の重要度に応じて、10または13 10個のみを受け入れます)。

1
手動タイプライターのレバーを示す素敵な写真を探しましたが、低解像度の画像しか見つかりませんでした。
トーマスディッキー

3
それらのいずれかを入力する必要がある場合は、すべても省略します!
マイケルハンプトン

3
歴史の部分に関しては、私が使用した手動タイプライターには、これと同様にレバーが1 しかありませんでした。引っ張ると、最初にローラーをクランキングし(ラインフィード)、次にキャリッジを引っ張ります。そして、この負荷がバネに負荷をかけました。入力された各文字、またはタブが押されると、スプリングが多少解放され、キャリッジが「ロードされていない」位置に戻ります。
RealSkeptic

2
入力では、CRは(tty行規則によって)CR LFではなくLFに変換されます。に変換されるのは、出力(入力のエコーを含む)上LF にありCR LFます。あなたが入力するとfoo<Return>、調理モードでは、アプリケーションが読み込むfoo\nfoo\r\n、端末にエコー用のライン規律によって送り返されます。
ステファンシャゼラス

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.