Linuxシェルコマンドの貼り付けの問題

Linuxシェルコマンドの貼り付けの問題

私はしばしばスクリプトを実行し、ここにSTDINの数行を入力する必要があります。これを行うときは通常、通話を準備してから、1つの端末ウィンドウから別の端末ウィンドウにコピーして貼り付けます。理想的には、次のようなテキストブロックを貼り付けることができることを願っています。

<script name> <script args>
<STDIN line 1>
<STDIN line 2>
...

次にCtrl-dを押して、STDINが入力の終わりに達したことをスクリプトに表示します。

bashで(単一貼り付けとして)これを行うと、セッションでSTDINエコーなしでスクリプトが実行され、<cr>^ dが処理される前に追加の項目がヒットされ、スクリプトSTDINの最初の行が失われます。そのため、通常、スクリプト呼び出しを自己貼り付けとして貼り付けてから、STDINのすべての行を2番目の貼り付けとして貼り付けます。これにより、行を貼り付けてエコーでき、Ctrl-dを押して入力の終わりを示します。

zshでこれを行うと、ほとんどのSTDIN行はエコーされません(STDIN行が多い場合は最後の行の一部がエコーされ、最初の行は行の末尾の部分だけをエコーできます)。 、<cr>^d の前に追加の必要性を識別した後。

問題のスクリプトがPythonかPerlかは問題ではありません。どちらも同じように動作するので、これがシェルの問題であると信じています。

問題は、問題がどこにあり、各実行を単一の貼り付けで実行できるソリューションがありますか?

実行されるスクリプトの内容は実際には重要ではありません。私のテストでは、whileループと同じ単純なスクリプトを使用してSTDINを1行ずつバッファに読み込み、whileループの外側にバッファを印刷しました。私が言うのは、<cr>キーボードのキーに応じてEnterまたはReturnキーです。

答え1

heredocを試してみてください。切り取り、貼り付けが簡単です。

script.sh arg arg arg <<'END_INPUT'
line1
line2
line3
END_INPUT

答え2

おそらく観察内容は次のように異なります。ライン編集そしてシェルは端末のモードを設定します。

テキストを貼り付けるとき

cat
a
b

(「b」の後の改行を含む)をインタラクティブBashセッションのコマンドラインに追加すると、デフォルトで次の設定が見つかります。オリジナルモード- 行を編集するときと同じです。GNUリードラインライブラリは、何よりも入力が1行ずつ文字ごとにシェルに送信され、キャリッジリターン( 、 を\r押したときに端末が表示する内容Enterまたはコマンドに貼り付けたときに改行を変換することを意味します。 行)改行(\n)は無効になります入力された文字は、端末ではなくシェル自体によってコマンドラインにエコーされます。
その後、シェルは最初の行を読み取り、それが完全な単純なコマンド(cat)であることを確認して実行します。これを行うと、独自の行編集も無効になり、端末を「クッキー」モードに設定して\r\n変換を有効にします。ただし、この時点で入力バッファにはすでに画面からa\rb\r読み取られ印刷された内容が含まれています。次に+()をcat押すと終了し、新しいプロンプトが印刷され(唯一の)印刷された行が上書きされます(キャリッジリターンで終わるため)。CtrlD^Dcat

気づく:

  • 代わりに、catループから標準入力を読み取るプログラムを呼び出すと、スクリプトと同じです。

    while IFS= read -r foo
    do
      # Accumulate the content of foo
    done
    

    Enter入力バッファに文字がないため、クリックするまで何も読みません\n(貼り付けたデータが端末の入力バッファサイズを超えない限り、以下を参照)。

  • b貼り付け後にコマンドの後に表示されるテキスト(この場合)は、貼り付けた内容のエコーではありませんcat。たとえば、貼り付けで簡単に確認できます。

    od -An -tc
    a
    b
    

    シェルの実行に貼り付けることもstrace -f -e read,write bash何が起こっているのかを理解するのに役立ちます。

  • 標準入力で読み取られたテキストが「十分」である場合(私のシステムでは4095文字以上、「正規モードと非正規モード」を参照)man 3 termios)、上記の内容は最初の4095バイトサイズの読み取りにのみ適用されます。これは、端末が生モードで入力バッファに入れる最大量です。その後、読み取りはベーキングモードで行われ、\r\n変換が行われ、貼り付けたテキストの対応する部分がコマンドラインに反映されます(コマンドの出力と混在する可能性があります)。

原則として、Bashの行編集を無効にすると、この動作を変更できます。

$ set +o emacs
$ set +o vi
$ cat         # Pasting the above snippet here
a
b
a
b
$             # ^D makes cat exit and brings the prompt back

ただし、「角かっこ貼り付け」が有効になっていない限り(以下を参照)、処理を開始する前に貼り付け操作が終了するのを待つように端末でシェルに指示する方法がないため、長い入力は依然として出力と混在します。


zsh少し簡単なようです。唯一の問題:「かっこ内に貼り付け」デフォルトでは有効にできます。これにより、複数行コマンドをコマンドラインに貼り付け、特殊文字(タブ、改行など)が正しく処理されていることを確認しながら、スクリプトのように実行できます。これも与えられたとき

cat
a
b

zsh3行を読み取って実行し、cat終了時に端末から新しい入力を待ち、catシェルが実行しようaとします(そのコマンドがシステムに存在しない場合は失敗します)b

角かっこ貼り付けを無効にすると、目的の動作が達成されます(ただし、行bashのない編集とは異なり、zsh貼り付けた内容は最初の4095バイトを超える部分を除いてデフォルトではエコーされません(リストの上の箇条書きを参照))。

% unset zle_bracketed_paste
% cat         # Pasting the above snippet here
a             # cat prints these two lines
b
%             # ^D makes cat exit and brings the prompt back

関連情報