bash可以写入自己的输入流吗?


39

是否可以在交互式bash shell中输入输出某些文本的命令,使其显示在下一个命令提示符下,就像用户在该提示符下键入了该文本一样?

我希望能够生成source一个脚本,该脚本将生成一个命令行并输出,以便在脚本结束后提示返回时显示它,以便用户可以选择在按enter执行前对其进行编辑。

这可以通过以下方式实现,xdotool但仅在终端位于X窗口中且仅在已安装时才有效。

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

可以仅使用bash吗?


我认为,如果您可以忍受并让它驱动子外壳,那么对Expect来说应该不难。但我记得不够多,无法发布实际答案。
三胞胎

Answers:


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终端设备的输入缓冲区,就像从终端接收到的一样。

@mike 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的行编辑器)具有禁用终端本地回声,则该字符串将显示在shell提示,稍搞乱了显示。

要解决此问题,您可以稍稍延迟将请求发送到终端,以确保当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(与相对zsh)不支持wait-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和我自己给出的两个解决方案进行了扩展,并提供了一些更详细的信息。


我想我bind '"\e[0n": "echo test"'; printf '\e[5n'可能正在寻找仅打击bash的答案。这个对我有用。但是,我^[[0n也会在提示之前打印出来。我发现这是在$PS1包含子外壳时引起的。您可以通过PS1='$(:)'在bind命令之前执行来重现它。为什么会发生这种情况,对此可以采取任何措施?
starfry

尽管此答案中的所有内容都是正确的,但问题是针对bash而非zsh。有时我们没有选择使用哪种外壳。
虚假名称

@Falsenames仅第一段用于zsh。其余的要么是不可知的,要么是针对bash的。问答不一定仅对bash用户有用。
斯特凡Chazelas

1
@starfry似乎好像可以\r把头放在头上$PS1?如果$PS1足够长,那应该可以工作。如果没有,那就放^[[M那里。
mikeserv

@mikeserv- r绝招。这当然不会阻止输出,只是在眼睛看到之前就将其覆盖。我猜^[[M会擦除该行以清除注入的文本,以防其长于提示符。是这样吗(我在ANSI转义列表中找不到它)?
starfry

24

提供此答案是为了阐明我自己的理解,并且受到@StéphaneChazelas和@mikeserv的启发。

TL; DR

  • bash没有外部帮助就不可能做到这一点;
  • 正确的方法是使用发送终端输入, ioctl但是
  • 最可行的bash解决方案使用bind

简单的解决方案

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

Bash有一个称为shell的内置bind函数,当接收到一个键序列时,它允许执行shell命令。本质上,shell命令的输出被写入到shell的输入缓冲区。

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

按键序列\e[0n<ESC>[0n)是终端发送的ANSI终端转义码,以指示其正常运行。它发送此消息以响应设备状态报告请求,该请求以形式发送<ESC>[5n

通过将响应绑定到echo输出要注入的文本的,我们可以在任何时候通过请求设备状态来注入文本,这可以通过发送<ESC>[5n转义序列来完成。

printf '\e[5n'

这有效,并且可能足以回答原始问题,因为不涉及其他工具。它是纯净的,bash但依赖行为良好的终端(实际上都是)。

它将回显的文本留在命令行上,使其可以像已被键入一样使用。可以附加,编辑和按下ENTER它来执行它。

添加\n到绑定命令以使其自动执行。

但是,此解决方案仅在当前终端中有效(这在原始问题的范围内)。它可以从交互式提示或脚本运行,但是如果从子shell中使用,则会引发错误:

bind: warning: line editing not enabled

接下来描述的正确解决方案更加灵活,但是它依赖于外部命令。

正确的解决方案

注入输入的正确方法是使用tty_ioctl,它是I / O Control的unix系统调用,具有TIOCSTI可用于注入输入的命令。

TIOC从“牛逼端子IOC TL ”和 STI从“小号结束牛逼端子 NPUT ”。

没有内置的命令bash。这样做需要外部命令。在典型的GNU / Linux发行版中没有这样的命令,但是通过少量编程就不难实现。这是一个使用的shell函数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):

佩尔 inject.pl

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

您可以生成sys/ioctl.ph定义TIOCSTI而不是使用数字值。看这里

蟒蛇 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做在子shell这样的作品。它也可以注入到其他终端,如下所述。

更进一步(控制其他终端)

这超出了原始问题的范围,但是可以在具有适当权限的情况下将字符注入另一个终端。通常,这意味着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

清洁输出

插入的文本显示在提示的前面,就像在提示出现之前键入(实际上是输入)一样,但是在提示之后再次出现。

隐藏提示前面出现的文本的一种方法是在提示前面加上回车符(\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是上述描述的解决方案中的一个,或取代printf '\e[5n'

替代方法

如果您的环境满足某些先决条件,则可以使用其他方法来注入输入。如果您在桌面环境中,则xdotool是一个X.Org实用程序,它可以模拟鼠标和键盘的活动,但是您的发行版可能默认不包含它。你可以试试:

$ xdotool type ls

如果使用终端复用器tmux,则可以执行以下操作:

$ tmux send-key -t session:pane ls

在此处-t选择要注入的会话窗格GNU Screen具有与其stuff命令类似的功能:

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

如果您的发行版包含console-tools软件包,那么您可能会writevt使用ioctl与我们的示例类似的命令。但是,大多数发行版不赞成使用此软件包,而推荐使用缺少此功能的kbd

可以使用编译writevt.c的更新副本gcc -o writevt writevt.c

可能更适合某些用例的其他选项包括ExpectEmpty,它们旨在允许编写交互式工具脚本。

您也可以使用支持终端注入的shell zsh来做到这一点print -z ls

“哇,这很聪明...”答案

这里描述的方法也在这里讨论并且以这里讨论的方法为基础。

从shell重定向将/dev/ptmx获得一个新的伪终端:

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

一个用C编写的小工具,用于解锁伪终端主设备(ptm),并将伪终端从设备的名称(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;
}

(另存为pts.c并使用编译gcc -o pts pts.c

在将程序的标准输入设置为ptm的情况下调用程序时,它将解锁相应的pts,并将其名称输出到标准输出。

$ ./pts </dev/ptmx
/dev/pts/20
  • 所述unlockpt() 函数解锁对应于由给定的文件描述符称为主伪终端从伪终端设备。程序将其作为零传递,这是程序的标准输入

  • 所述ptsname()使用 函数返回相应于主从伪终端设备的名称提到了给定的文件描述符,再次通过零的程序的标准输入。

可以将进程连接到pt。首先获得一个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。它的标准输入(fd 0)是已读/写打开的pt,将被重定向(实际上fd已复制- >&0)到其标准输出(fd 1)。

这将创建一个子外壳,其标准输入和输出连接到pts。可以通过写入ptm来发送输入,通过读取ptm可以看到其输出:

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

子shell执行以下命令:

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

bash(6527,6527)-i新会话中以交互()模式运行(setsid -c请注意,PID和PGID相同)。它的标准错误被重定向到其标准输出(2>&1),并通过管道进行tee(6528,6524)传递,因此将其写入log文件和pts中。这提供了另一种查看子shell输出的方法:

$ tail -f log

由于子外壳程序是bash交互式运行的,因此可以向其发送命令来执行,如显示子外壳程序的文件描述符的示例所示:

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

读取subshel​​l的输出(tail -f logcat <&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)是属于控制终端的点。

包起来

下面的脚本使用上述技术。它建立了一个交互式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'解决方案,对所有的输出后,ls -l^[[0n将在终端输出一次我打回车键从而运行ls -l。有什么想法如何“隐藏”它吗?谢谢。
阿里

1
我提出了一种解决方案,可以使您获得满意的效果-在我的答案的干净输出部分中,我建议在提示符后添加一个返回值以隐藏多余的文本。我在尝试PS1="\r\e[M$PS1"之前就尝试过bind '"\e[0n": "ls -l"'; printf '\e[5n',这给了您所描述的效果。
星空

谢谢!我完全错过了这一点。
阿里

20

这取决于你的意思是什么bash。如果您是指一个互动bash会话,那么答案几乎肯定是“ 否”。这是因为,即使您ls -l在任何规范终端上的命令行中输入了这样的命令,bash都还没有意识到- bash甚至在那时都没有涉及。

相反,到目前为止,发生的事情是内核的tty行规则已经缓冲stty echo了用户的输入并将其仅输入到屏幕。它将输入的内容刷新到它的阅读器(bash在您的示例中为逐行),并且通常在Unix系统上也\r\n回合转换为ewlines bash,因此也不会这样做,因此您的源脚本也无法知道直到用户按下该ENTER键为止。

现在,有一些解决方法。实际上,最健壮的解决方案根本不是解决方法,它涉及到使用多个进程或专门编写的程序来对输入进行排序,-echo向用户隐藏行纪律,并仅在解释输入时将认为适当的内容写到屏幕上特别是在必要时。这可能很难做好,因为这意味着编写解释规则,该解释规则可以按字符处理任意输入的char,并可以无误地同时写出来,以模拟普通用户在该场景中的期望。正是由于这个原因,交互式终端的I / O很少得到很好的理解-这种困难并不是大多数人都可以进一步研究的。

另一个解决方法可能涉及终端仿真器。您说您遇到的问题是对X和的依赖xdotool。在这种情况下,我将要提供的解决方法可能会遇到类似的问题,但我将以相同的方式继续进行。

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

这将在一个工作xterm瓦特/ allowwindowOps资源集。它首先将图标/窗口名称保存在堆栈中,然后设置终端的图标字符串,^Umy command然后请求终端将该名称注入到输入队列中,最后将其重置为保存的值。对于 在正确的配置下bash运行的交互式shell,它应该是不可见的,xterm但这可能不是一个好主意。请在下面查看Stéphane的评论。

不过,这是我在机器上运行printf带不同转义序列的位后,我对术语终端所拍摄的照片。对于每个新行printf我输入命令CTRL+V,然后CTRL+J和之后按下ENTER键。之后,我什么也没键入,但是,正如您所看到的,终端my command为我注入了行规的输入队列:

term_inject

真正的方法是使用嵌套的pty。它是如何screentmux和类似的工作-这两者,顺便说一下,可以使这个可能适合你。xterm实际上带有一个叫做的小程序luit,它也可以使这成为可能。但是,这并不容易。

您可以采用以下一种方法:

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的编译器)时,将写出一个名为的小型可执行二进制文件,该二进制文件ptsunlockpt()在其stdin上运行该函数并将其写入stdout刚解锁的pty设备的名称。我在工作时写了它。。。我该怎么办?我该怎么办?

无论如何,上面的代码所做的是bash在当前tty下方的pty中运行一个shell。bash被告知将所有输出写入从属pty,并且当前tty既不配置-echo为其输入也不对其进行缓冲,而是将其(主要是) 传递rawcat,然后将其复制到bash。同时,cat所有其他从属输出将所有从属输出复制到当前tty中。

在大多数情况下,上述配置将完全无用-基本上是多余的- 除了我们bash使用自己的pty master fd on的副本启动时<>9。这意味着bash可以通过简单的重定向自由地写入自己的输入流。所有bash要做的就是:

echo echo hey >&9

...自言自语

这是另一张图片:

在此处输入图片说明


2
您设法在哪些终端上工作?这种东西在过去很容易被滥用,现在应该默认禁用。使用xterm,您仍然可以使用来查询图标标题,\e[20t但前提是使用来查询allowWindowOps: true
斯特凡Chazelas


@StéphaneChazelas可用于术语,但我敢肯定它也可在gnome终端,kde终端中工作(我忘了它的名字,并且我认为还有一个不同的转义符),正如您所说,w / xtermw /正确配置。我认为,如果使用xterm适当的话,您可以读写复制/粘贴缓冲区,因此它变得更加简单。Xterm还具有转义序列,用于更改/影响术语描述本身。
mikeserv

除了术语之外,我无法使它起作用(顺便说一句,它还有其他几个类似的漏洞)。如果CVE已有12年历史,并且相对知名,那么如果任何主终端模拟器都具有相同的漏洞,我将感到惊讶。需要注意的是使用xterm,这\e[20t(不\e]1;?\a
斯特凡Chazelas


8

尽管 StéphaneChazelas 的ioctl(,TIOCSTI,) 答案当然是正确的答案,但有些人可能会对此部分但微不足道的答案感到满意:只需将命令推入历史记录堆栈,然后用户就可以将历史记录上移1行以查找命令。

$ 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-p提示,启用对输入的readline编辑。


这仅适用于shell函数,或者脚本是源代码(. foo.sh或`source foo.sh,而不是在子shell中运行。),但是有趣的方法。一个类似的需要修改调用Shell上下文的技巧是建立一个自定义补全,将空行扩展为某些内容,然后恢复旧的补全处理程序。
彼得·科德斯

@PeterCordes你是对的。我从字面上讲也是这个问题。但是我添加了一个可以运行的简单脚本的示例。
meuh 2015年

@mikeserv嘿,它只是一个简单的解决方案,可能对某些人有用。你甚至可以删除eval,如果你有简单的命令来编辑,无需管道和重定向等
meuh

1

哦,我的话,我们错过了bash内置的简单解决方案:该read命令具有一个option -i ...,当与一起使用时-e,该选项会将文本推入输入缓冲区。从手册页:

-i 文字

如果使用readline读取行,则在开始编辑之前将文本放入编辑缓冲区。

因此,创建一个小的bash函数或shell脚本,它将命令呈现给用户,然后运行或评估其答复:

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

毫无疑问,这使用了已经存在超过32年的ioctl(,TIOCSTI),因为它已经存在于2.9BSD ioctl.h中


1
有趣的是,它具有类似的效果,但不会注入到提示中。
starfry

在第二个想法上你是对的。bash不需要TIOCSTI,因为它自己在做所有的I / O。
meuh 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.