bashは独自の入力ストリームに書き込むことができますか?


39

インタラクティブbashシェルで、ユーザーがそのプロンプトでそのテキストを入力したかのように、次のコマンドプロンプトに表示されるようにテキストを出力するコマンドを入力することは可能ですか?

私のことができるようにしたいsourceユーザーは、必要に応じて押す前に、それを編集できるように、それはそれは、スクリプトが終了した後ときプロンプトに戻りますが表示されていることを、コマンドラインと出力を生成するスクリプトenterを実行することを。

これはxdotool、端末でXウィンドウを使用し、インストールされている場合にのみ機能します。

[me@mybox] 100 $ xdotool type "ls -l"
[me@mybox] 101 $ ls -l  <--- cursor appears here!

これはbashのみを使用して実行できますか?


あなたがそれを容認することができ、サブシェルを駆動することができれば、これはExpectでは難しいことではないと思っています。しかし、実際の答えを投稿するのに十分なことを覚えていません。
トリプリー

回答:


40

ではzsh、あなたが使用することができますprint -z次のプロンプトのためのラインエディタバッファにテキストを配置します:

print -z echo test

echo test次のプロンプトで編集できるラインエディタを準備します。

bash同様の機能はないと思いますが、多くのシステムでは、端末デバイスの入力バッファーを次のようにプライミングできますTIOCSTI ioctl()

perl -e 'require "sys/ioctl.ph"; ioctl(STDIN, &TIOCSTI, $_)
  for split "", join " ", @ARGV' echo test

echo test端末から受信したかのように、端末デバイスの入力バッファに挿入します。

よりポータブルに変化マイクさん@ Terminologyアプローチそれは、セキュリティを犠牲にしないと、端末エミュレータにかなり標準送ることであろうquery status reportエスケープシーケンス:<ESC>[5n端子は必ず(その入力として)返信として<ESC>[0n、バインド文字列に挿入したいこと:

bind '"\e[0n": "echo test"'; printf '\e[5n'

GNU内であればscreen、次のこともできます。

screen -X stuff 'echo test'

現在、TIOCSTI ioctlアプローチを除いて、ターミナルエミュレーターに、入力されたかのように文字列を送信するように求めています。その文字列がreadlinebashのラインエディタ)がターミナルローカルエコーを無効にする前に来る場合、その文字列はシェルプロンプトではなく表示され、表示が少し乱れます。

これを回避するには、ターミナルへのリクエストの送信を少し遅らせて、readlineによってエコーが無効になったときに応答が到着するようにします。

bind '"\e[0n": "echo test"'; ((sleep 0.05;  printf '\e[5n') &)

(ここでsleepは、サブ秒の解像度をサポートすると仮定しています)。

理想的には、次のようなことをしたいでしょう:

bind '"\e[0n": "echo test"'
stty -echo
printf '\e[5n'
wait-until-the-response-arrives
stty echo

ただし、bash(とは反対にzshwait-until-the-response-arrivesレスポンスを読み取らないようなものはサポートしていません。

ただし、次のhas-the-response-arrived-yet機能がありますread -t0

bind '"\e[0n": "echo test"'
saved_settings=$(stty -g)
stty -echo -icanon min 1 time 0
printf '\e[5n'
until read -t0; do
  sleep 0.02
done
stty "$saved_settings"

参考文献

@starfryの回答を参照してください。これは、@ mikeservと私が提供する2つのソリューションをさらに詳細に説明したものです。


bind '"\e[0n": "echo test"'; printf '\e[5n'おそらく私が探しているのはbashのみの答えだと思います。わたしにはできる。ただし、^[[0nプロンプトの前にも印刷されます。これは$PS1、サブシェルが含まれている場合に発生することを発見しました。PS1='$(:)'bindコマンドの前に実行することで再現できます。なぜそれが起こり、それについて何かできるのでしょうか?
15

この答えのすべてが正しいものの、質問はzshではなくbashに対するものでした。使用するシェルを選択できない場合があります。
偽名

@Falsenames最初の段落のみがzsh用です。残りは、シェルに依存しないか、bash固有です。Q&Aは、bashユーザーだけに役立つ必要はありません。
ステファンシャゼル

1
@starfryは、たぶんあなたが\r頭に戻ることができるように思えます$PS1か?$PS1十分に長ければそれは機能するはずです。そうでない場合は、そこに置き^[[Mます。
mikeserv

@mikeserv- rトリックを行います。もちろん、これは出力を妨げるものではなく、目に見える前に上書きされるだけです。^[[Mプロンプトよりも長い場合、挿入されたテキストをクリアするために行を消去すると思います。それは正しいですか(私が持っているANSIエスケープリストでそれを見つけることができませんでした)?
15

24

この回答は、私自身の理解の明確化として提供されており、私の前の@StéphaneChazelasと@mikeservに触発されています。

TL; DR

  • bash外部からの支援なしにこれを行うことはできません。
  • これを行う正しい方法は、端末入力を送信 することですioctlが、
  • 最も簡単な実行可能なbashソリューションはを使用しbindます。

簡単な解決策

bind '"\e[0n": "ls -l"'; printf '\e[5n'

Bashにはシェルビルトインがありbind、キーシーケンスを受け取ったときにシェルコマンドを実行できます。本質的に、シェルコマンドの出力はシェルの入力バッファに書き込まれます。

$ bind '"\e[0n": "ls -l"'

キーシーケンス\e[0n<ESC>[0n)は、端末が正常に機能していることを示すために送信するANSI端末エスケープコードです。として送信されるデバイスステータスレポート要求への応答でこれを送信し<ESC>[5nます。

echo挿入するテキストを出力するに応答をバインドすることにより、デバイスステータスを要求することにより、必要なときにいつでもテキストを挿入でき<ESC>[5nます。

printf '\e[5n'

これは機能し、おそらく他のツールが関与しないため、元の質問に答えるのにおそらく十分です。これは純粋ですbashが、正常に動作する端末に依存しています(実際にはすべてがそうです)。

コマンドラインにエコーされたテキストが入力されたかのように使用できる状態になります。追加、編集、押すENTERことができ、実行されます。

\nバインドされたコマンドに追加して、自動的に実行されるようにします。

ただし、このソリューションは現在の端末でのみ機能します(元の質問の範囲内です)。対話型プロンプトまたはソーススクリプトから機能しますが、サブシェルから使用するとエラーが発生します。

bind: warning: line editing not enabled

次に説明する正しいソリューションはより柔軟ですが、外部コマンドに依存しています。

正しい解決策

入力をインジェクトする適切な方法は、tty_ioctlを使用します。これは、入力をインジェクトするためTIOCSTI使用できるコマンドを持つI / O Controlの UNIXシステムコールです。

TIOC "から T erminal IOCの TL "と STI "から Sの終わりT erminal I NPUT "。

このためのコマンドは組み込まれbashていません。そのためには、外部コマンドが必要です。典型的なGNU / Linuxディストリビューションにはこのようなコマンドはありませんが、少しのプログラミングで達成することは難しくありません。以下が使用するシェル関数ですperl

function inject() {
  perl -e 'ioctl(STDIN, 0x5412, $_) for split "", join " ", @ARGV' "$@"
}

これ0x5412TIOCSTIコマンドのコードです。

TIOCSTIは、標準Cヘッダーファイルで値が定義されている定数です0x5412。試してgrep -r TIOCSTI /usr/include、または見てください/usr/include/asm-generic/ioctls.h。によって間接的にCプログラムに含まれてい#include <sys/ioctl.h>ます。

その後、次のことができます。

$ inject ls -l
ls -l$ ls -l <- cursor here

他のいくつかの言語での実装を以下に示します(ファイルに保存してchmod +xから):

Perl inject.pl

#!/usr/bin/perl
ioctl(STDIN, 0x5412, $_) for split "", join " ", @ARGV

数値を使用sys/ioctl.phするTIOCSTI代わりに、どちらを定義するかを生成できます。こちらをご覧ください

Python inject.py

#!/usr/bin/python
import fcntl, sys, termios
del sys.argv[0]
for c in ' '.join(sys.argv):
  fcntl.ioctl(sys.stdin, termios.TIOCSTI, c)

ルビー inject.rb

#!/usr/bin/ruby
ARGV.join(' ').split('').each { |c| $stdin.ioctl(0x5412,c) }

C inject.c

でコンパイルする gcc -o inject inject.c

#include <sys/ioctl.h>
int main(int argc, char *argv[])
{
  int a,c;
  for (a=1, c=0; a< argc; c=0 )
    {
      while (argv[a][c])
        ioctl(0, TIOCSTI, &argv[a][c++]);
      if (++a < argc) ioctl(0, TIOCSTI," ");
    }
  return 0;
}

**!**ここにはさらに例があります

これioctlを行うために使用すると、サブシェルで機能します。次に説明するように、他の端末に注入することもできます。

さらに進める(他の端末を制御する)

元の質問の範囲を超えていますが、適切な権限を持っていることを条件に、別の端末に文字を注入することは可能です。通常、これはを意味しますがroot、他の方法については以下を参照してください。

上記のCプログラムを拡張して、別の端末のttyを指定するコマンドライン引数を受け入れると、その端末に注入できます。

#include <stdlib.h>
#include <argp.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>

const char *argp_program_version ="inject - see https://unix.stackexchange.com/q/213799";
static char doc[] = "inject - write to terminal input stream";
static struct argp_option options[] = {
  { "tty",  't', "TTY", 0, "target tty (defaults to current)"},
  { "nonl", 'n', 0,     0, "do not output the trailing newline"},
  { 0 }
};

struct arguments
{
  int fd, nl, next;
};

static error_t parse_opt(int key, char *arg, struct argp_state *state) {
    struct arguments *arguments = state->input;
    switch (key)
      {
        case 't': arguments->fd = open(arg, O_WRONLY|O_NONBLOCK);
                  if (arguments->fd > 0)
                    break;
                  else
                    return EINVAL;
        case 'n': arguments->nl = 0; break;
        case ARGP_KEY_ARGS: arguments->next = state->next; return 0;
        default: return ARGP_ERR_UNKNOWN;
      }
    return 0;
}

static struct argp argp = { options, parse_opt, 0, doc };
static struct arguments arguments;

static void inject(char c)
{
  ioctl(arguments.fd, TIOCSTI, &c);
}

int main(int argc, char *argv[])
{
  arguments.fd=0;
  arguments.nl='\n';
  if (argp_parse (&argp, argc, argv, 0, 0, &arguments))
    {
      perror("Error");
      exit(errno);
    }

  int a,c;
  for (a=arguments.next, c=0; a< argc; c=0 )
    {
      while (argv[a][c])
        inject (argv[a][c++]);
      if (++a < argc) inject(' ');
    }
  if (arguments.nl) inject(arguments.nl);

  return 0;
}  

また、デフォルトで改行を送信しますが、と同様にecho、それ-nを抑制するオプションを提供します。--tまたは--ttyオプションの引数が必要です- tty端末の注入されます。この値は、そのターミナルで取得できます。

$ tty
/dev/pts/20

でコンパイルしgcc -o inject inject.cます。--引数パーサーがコマンドラインオプションを誤って解釈するのを防ぐために、ハイフンが含まれている場合に挿入するテキストの前に付けます。をご覧ください./inject --help。次のように使用します。

$ inject --tty /dev/pts/22 -- ls -lrt

あるいは単に

$ inject  -- ls -lrt

現在の端末を挿入します。

別の端末に注入するには、次の方法で取得できる管理者権限が必要です。

  • 以下のようなコマンドを発行しroot
  • を使用してsudo
  • CAP_SYS_ADMIN能力を持っているか
  • 実行可能ファイルの設定 setuid

割り当てるにはCAP_SYS_ADMIN

$  sudo setcap cap_sys_admin+ep inject

割り当てるにはsetuid

$ sudo chown root:root inject
$ sudo chmod u+s inject

きれいな出力

挿入されたテキストは、プロンプトが表示される前に入力されたかのようにプロンプ​​トの前に表示されます(実際にはそうでした)が、プロンプトの後に再び表示されます。

プロンプトの前に表示されるテキストを非表示にする1つの方法は、プロンプトにキャリッジリターン(\r改行ではなく)を追加し、現在の行をクリアすることです(<ESC>[M)。

$ PS1="\r\e[M$PS1"

ただし、これはプロンプトが表示されている行のみをクリアします。挿入されたテキストに改行が含まれている場合、意図したとおりに機能しません。

別の解決策は、挿入された文字のエコーを無効にします。ラッパーはsttyこれを行うために使用します:

saved_settings=$(stty -g)
stty -echo -icanon min 1 time 0
inject echo line one
inject echo line two
until read -t0; do
  sleep 0.02
done
stty "$saved_settings"

ここinjectで、上記のソリューションの1つ、またはに置き換えられprintf '\e[5n'ます。

代替アプローチ

ご使用の環境が特定の前提条件を満たしている場合、入力を注入するために使用できる他の方法を使用できる場合があります。デスクトップ環境の場合、xdotoolはマウスとキーボードの動作をシミュレートするX.Orgユーティリティですが、ディストリビューションにはデフォルトで含まれていない場合があります。あなたが試すことができます:

$ xdotool type ls

端末マルチプレクサであるtmuxを使用する場合、これを行うことができます。

$ tmux send-key -t session:pane ls

whereは、注入-tするセッションペインを選択します。GNU Screenには、そのstuffコマンドで同様の機能があります。

$ screen -S session -p pane -X stuff ls

ディストリビューションにconsole-toolsパッケージが含まれている場合、例のようなwritevtコマンドを使用できますioctl。ただし、ほとんどのディストリビューションは、この機能を欠いているkbdを支持して、このパッケージを廃止しました。

writevt.cの更新されたコピーは、を使用してコンパイルできますgcc -o writevt writevt.c

いくつかのユースケースに適した他のオプションには、インタラクティブツールをスクリプト化できるように設計されたexpectおよびemptyがあります。

また、可能性のある端末インジェクションをサポートするシェルを使用することもzshできますprint -z ls

「うわー、それは賢い...」答え

ここで説明されたメソッドもここで議論されてここで議論されたメソッドに基づいています

からのシェルリダイレクト/dev/ptmxは、新しい擬似端末を取得します。

$ $ ls /dev/pts; ls /dev/pts </dev/ptmx
0  1  2  ptmx
0  1  2  3  ptmx

擬似端末マスター(ptm)のロックを解除し、擬似端末スレーブ(pts)の名前を標準出力に出力する、Cで書かれた小さなツール。

#include <stdio.h>
int main(int argc, char *argv[]) {
    if(unlockpt(0)) return 2;
    char *ptsname(int fd);
    printf("%s\n",ptsname(0));
    return argc - 1;
}

(名前を付けて保存しpts.cてコンパイルgcc -o pts pts.c

標準入力をptmに設定してプログラムを呼び出すと、対応するptsのロックが解除され、その名前が標準出力に出力されます。

$ ./pts </dev/ptmx
/dev/pts/20
  • unlockpt() 関数は、指定されたファイル記述子によって参照されるマスタ擬似端末に対応するスレーブ擬似端末デバイスのロックを解除します。プログラムは、これをプログラムの標準入力であるゼロとして渡します

  • ptsname() 関数は、マスタに対応するスレーブ擬似端末デバイスの名前を戻し、再度プログラムの標準入力に対してゼロを通過する、与えられたファイル記述子によって参照されます。

プロセスをPTSに接続できます。最初にptmを取得します(ここでは、ファイル記述子3に割り当てられ、<>リダイレクトによって読み取りと書き込みが開かれます)。

 exec 3<>/dev/ptmx

次に、プロセスを開始します。

$ (setsid -c bash -i 2>&1 | tee log) <>"$(./pts <&3)" 3>&- >&0 &

このコマンドラインによって生成されるプロセスは、次の方法で最もわかりやすく説明されていpstreeます。

$ pstree -pg -H $(jobs -p %+) $$
bash(5203,5203)─┬─bash(6524,6524)─┬─bash(6527,6527)
                             └─tee(6528,6524)
            └─pstree(6815,6815)

出力は現在のshell($$)に相対しており、各プロセスのPID(-p)とPGID(-g)はカッコ内に表示されます(PID,PGID)

ツリーの先頭にあるのはbash(5203,5203)、コマンドを入力する対話型シェルであり、そのファイル記述子は、対話するために使用しているターミナルアプリケーション(xterm、または同様のもの)に接続します。

$ ls -l /dev/fd/
lrwx------ 0 -> /dev/pts/3
lrwx------ 1 -> /dev/pts/3
lrwx------ 2 -> /dev/pts/3

もう一度コマンドを見ると、カッコの最初のセットがサブシェルを開始しましたbash(6524,6524))、そのファイル記述子0(標準入力)が、ロックを解除するために<>実行された別のサブシェルによって返されるpts(読み取り/書き込みで開かれます)に割り当てられます./pts <&3ファイル記述子3に関連付けられたPTS(前のステップで作成されたexec 3<>/dev/ptmx)。

サブシェルのファイル記述子3は閉じられている(3>&-)ため、ptmにはアクセスできません。読み取り/書き込みで開かれたPTSである標準入力(fd 0)は、>&0標準出力(fd 1)にリダイレクトされます(実際にはfdがコピーされます- )。

これにより、標準入力と出力がPTSに接続されたサブシェルが作成されます。ptmに書き込むことで入力を送信でき、ptmから読み取ることで出力を確認できます。

$ echo 'some input' >&3 # write to subshell
$ cat <&3               # read from subshell

サブシェルは次のコマンドを実行します。

setsid -c bash -i 2>&1 | tee log

新しいセッションbash(6527,6527)でインタラクティブ(-i)モードで実行されます(setsid -c、PIDとPGIDは同じであることに注意してください)。その標準エラーはその標準出力(2>&1)にリダイレクトされ、経由でパイプされるtee(6528,6524)ためlog、ptsだけでなくファイルにも書き込まれます。これにより、サブシェルの出力を確認する別の方法が提供されます。

$ tail -f log

サブシェルはbash対話的に実行されているため、サブシェルのファイル記述子を表示する次の例のように、実行するコマンドを送信できます。

$ echo 'ls -l /dev/fd/' >&3

サブシェルの出力(tail -f logまたはcat <&3)を読み取ると、次のことがわかります。

lrwx------ 0 -> /dev/pts/17
l-wx------ 1 -> pipe:[116261]
l-wx------ 2 -> pipe:[116261]

標準入力(fd 0)はptsに接続され、標準出力(fd 1)とエラー(fd 2)は同じパイプに接続されteeます。

$ (find /proc -type l | xargs ls -l | fgrep 'pipe:[116261]') 2>/dev/null
l-wx------ /proc/6527/fd/1 -> pipe:[116261]
l-wx------ /proc/6527/fd/2 -> pipe:[116261]
lr-x------ /proc/6528/fd/0 -> pipe:[116261]

そして、のファイル記述子を見て tee

$ ls -l /proc/6528/fd/
lr-x------ 0 -> pipe:[116261]
lrwx------ 1 -> /dev/pts/17
lrwx------ 2 -> /dev/pts/3
l-wx------ 3 -> /home/myuser/work/log

標準出力(fd 1)はPTSです。「tee」がその標準出力に書き込むものはすべて、ptmに送り返されます。標準エラー(fd 2)は、制御端末に属するPTSです。

まとめます

次のスクリプトは、上記の手法を使用しています。bashファイル記述子に書き込むことで挿入できる対話型セッションを設定します。それは利用可能だここと説明して文書化。

sh -cm 'cat <&9 &cat >&9|(             ### copy to/from host/slave
        trap "  stty $(stty -g         ### save/restore stty settings on exit
                stty -echo raw)        ### host: no echo and raw-mode
                kill -1 0" EXIT        ### send a -HUP to host pgrp on EXIT
        <>"$($pts <&9)" >&0 2>&1\
        setsid -wc -- bash) <&1        ### point bash <0,1,2> at slave and setsid bash
' --    9<>/dev/ptmx 2>/dev/null       ### open pty master on <>9

最も簡単なbind '"\e[0n": "ls -l"'; printf '\e[5n'解決策では、Enterキーを押すと、すべての出力ls -l^[[0nターミナルに出力され、実行されls -lます。それを「隠す」方法はありますか?ありがとうございました。
アリ

1
あなたが望んでいる効果を与える1つの解決策を提示しました- 私の答えのきれいな出力セクションで、私はプロンプトにリターンを追加して、余計なテキストを隠すことをお勧めします。私はPS1="\r\e[M$PS1"やる前に試しましたがbind '"\e[0n": "ls -l"'; printf '\e[5n'、それはあなたが説明した効果を与えました。
スターフライ

ありがとうございました!私はその点を見逃しました。
アリ

20

それはあなたの意味bashだけに依存します。単一の対話型bashセッションを意味する場合、答えはほぼ間違いなくnoです。そして、これはls -l、正規の端末のコマンドラインのようなコマンドを入力しbashても、それをまだ認識してbashいないためであり、その時点では関与していません。

むしろ、その時点までに起こったことは、カーネルのtty行規律がバッファーに入れstty echoられ、画面へのユーザーの入力のみが行われたことです。その入力をそのリーダーにフラッシュします- bashあなたの例では-行\rごとに-そして一般的に\n同様にUnixシステム上でewlinesに変換します-そしてそうでbashはありません-そしてあなたのソーススクリプトも-ユーザーがENTERキーを押すまで入力します。

現在、いくつかの回避策があります。最も堅牢なのは実際には回避策ではなく、複数のプロセスまたは特別に作成されたプログラムを使用して入力をシーケンス-echoし、ユーザーから行規律を隠し、入力の解釈中に適切と判断されたものだけを画面に書き込むことを含みます特に必要な場合。これはうまくいくのが難しい可能性があります。なぜなら、それは到着した文字ごとに任意の入力文字を処理できる解釈ルールを記述し、そのシナリオで平均的なユーザーが期待することをシミュレートするために間違いなく同時に書き込むことを意味するためです。おそらく、対話型端末のI / Oがあまりよく理解されていないのはこのためでしょう-困難な見通しは、ほとんどの場合、さらなる調査に役立つものではありません。

別の回避策には、ターミナルエミュレータが含まれる場合があります。あなたは、あなたにとっての問題はXとに依存していると言いますxdotool。その場合、私が提供しようとしているこのような回避策は同様の問題を抱えているかもしれませんが、私は同じことを進めていきます。

printf  '\33[22;1t\33]1;%b\33\\\33[20t\33[23;0t' \
        '\025my command'

これはxtermw / allowwindowOpsリソースセットで機能します。最初にアイコン/ウィンドウ名をスタックに保存し、次に端末のアイコン文字列を設定してから^Umy command、端末がその名前を入力キューに挿入することを要求し、最後に保存された値にリセットします。 適切な設定でbash実行される対話型シェルでは目に見えないように動作するはずですxtermが、おそらく悪い考えです。以下のステファンのコメントをご覧ください。

ただし、ここではprintf、マシンで別のエスケープシーケンスを使用してビットを実行した後、用語集端末で撮影した写真を示します。各改行のためにprintf私が入力したコマンドCTRL+V、その後CTRL+J、その後押されたENTERキーを。その後は何も入力しませんでしたが、ご覧のmy commandとおり、端末はline-disciplineの入力キューに挿入されました。

term_inject

これを行う実際の方法は、ネストされたptyです。それはどのようにscreentmuxそして同様の仕事です-両方とも、ところで、あなたのためにこれを可能にすることができます。xterm実際には、luitこれを可能にする小さなプログラムが付属しています。しかし、それは簡単ではありません。

方法の1つを次に示します。

sh -cm 'cat <&9 &cat >&9|(             ### copy to/from host/slave
        trap "  stty $(stty -g         ### save/restore stty settings on exit
                stty -echo raw)        ### host: no echo and raw-mode
                kill -1 0" EXIT        ### send a -HUP to host pgrp on EXIT
        <>"$(pts <&9)" >&0 2>&1\       
        setsid -wc -- bash) <&1        ### point bash <0,1,2> at slave and setsid bash
' --    9<>/dev/ptmx 2>/dev/null       ### open pty master on <>9

これは決してポータブルではありませんが、開くための適切な許可が与えられているほとんどのLinuxシステムで動作するはず/dev/ptmxです。ユーザーはtty、システム上で十分なグループに属します。あなたも必要になります...

<<\C cc -xc - -o pts
#include <stdio.h>
int main(int argc, char *argv[]) {
        if(unlockpt(0)) return 2;
        char *ptsname(int fd);
        printf("%s\n",ptsname(0));
        return argc - 1;
}
C

... GNUシステム(またはstdinから読み取ることのできる標準Cコンパイラを使用する他のシステムpts実行するunlockpt()と、stdinで関数を実行し、stdoutに書き込むという名前の小さな実行可能バイナリを書き出しますロック解除したptyデバイスの名前。作業中にそれを書きました... このptyをどうやって実現し、それで何ができますか?

とにかく、上記のコードはbash、現在のttyの下のpty aレイヤーでシェルを実行します。bashすべての出力をスレーブptyに書き込むように指示され、現在のttyは-echo入力にもバッファにも設定されず、代わりに(大部分) rawに渡されcat、にコピーされbashます。そして、バックグラウンド化された別のcatすべてのスレーブ出力は、現在のttyにコピーされます。

ほとんどの場合、上記の構成は完全に無駄になります-基本的には冗長です- 独自のptyマスターfd onのコピーで起動することを除きます。つまり、単純なリダイレクトを使用して、独自の入力ストリームに自由に書き込むことができます。しなければならないことは次のとおりです。bash<>9bashbash

echo echo hey >&9

...自分自身と話す。

別の写真を次に示します。

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


2
どの端末で動作するように管理しましたか?そのようなことは昔は乱用されていたので、最近ではデフォルトで無効にされるべきです。を使用するとxterm、で\e[20t設定しallowWindowOps: trueた場合にのみ、アイコンのタイトルを照会できます。
ステファンシャゼル

それはCVE-
2003-0063

用語で動作する@StéphaneChazelasですが、gnomeターミナル、kdeターミナルでも動作すると確信しています(名前を忘れてしまい、別のエスケープがあると思います)そして、あなたが言うように、w / xtermw /適切構成 ただし、xtermが適切であれば、コピー/貼り付けバッファーの読み取りと書き込みができるため、さらにシンプルになります。Xtermには、用語の説明自体を変更または変更するためのエスケープシーケンスもあります。
mikeserv

用語(これには他にも同様の脆弱性がいくつかあります)以外では機能しません。そのCVEが12年以上前のものであり、比較的よく知られていることですが、メインターミナルエミュレータのいずれかに同じ脆弱性があった場合、私は驚くでしょう。xtermのと、だと注意\e[20t(ない\e]1;?\a
ステファンChazelas


8

StéphaneChazelas のioctl(,TIOCSTI,) 答えはもちろん正しい答えですが、一部の人々はこの部分的ではあるが些細な答えに十分満足しているかもしれません。コマンド。

$ history -s "ls -l"
$ echo "move up 1 line in history to get command to run"

これは、独自の1行の履歴を持つ単純なスクリプトになります。

#!/bin/bash
history -s "ls -l"
read -e -p "move up 1 line: "
eval "$REPLY"

read -e入力のreadline編集を有効に-pします。プロンプトです。


これは、シェル関数でのみ機能します。または、スクリプトがソースになっている場合(. foo.shまたはサブシェルで実行する代わりに「source foo.sh」)、興味深いアプローチです。呼び出しシェルのコンテキストを変更する必要がある同様のハックは、空の行を何かに拡張し、古い完了ハンドラーを復元するカスタム完了を設定することです。
ピーターコーデス

@PeterCordesあなたは正しいです。私も質問を文字通り受けていました。しかし、動作する簡単なスクリプトの例を追加しました。
-meuh

@mikeservちょっと、それは一部の人々に役立つかもしれない単純なソリューションです。あなたも、削除することができevalますが、編集する簡単なコマンドを持っている場合など、パイプとリダイレクトなしで、
meuh

1

私の言葉では、bashに組み込まれた簡単なソリューションを逃しまし。このreadコマンドにはoption -i ...があり、と共に使用すると-e、テキストを入力バッファーにプッシュします。manページから:

-i テキスト

readlineが行の読み取りに使用されている場合、テキストは編集が始まる前に編集バッファーに配置されます。

そのため、コマンドを受け取ってユーザーに提示し、応答を実行または評価する小さなbash関数またはシェルスクリプトを作成します。

domycmd(){ read -e -i "$*"; eval "$REPLY"; }

これは間違いなくioBSD(、TIOCSTI、)を使用しています。これは2.9BSD ioctl.hにすでに存在していたため、32年以上前から存在しています。


1
同様の効果を持つ興味深いものですが、プロンプトには表示されません。
15

考え直して、あなたは正しい。すべてのI / O自体を実行しているため、bashはTIOCSTIを行う必要がありません。
-meuh
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.