完整版 Linux技巧:使用bash read命令实现一个简易shell( 二 )


在实际工作中,对这个例子进行扩展,就能模拟一个简易的 shell 效果,可以输入自定义的命令简写,来执行一长串的命令,非常方便
例如进行 Android 系统开发,经常用到 adb shell 的各种命令,有些命令带有很多参数,比较难输入,仿照这个例子,可以只输入一个字符、或者几个字符,然后执行对应的 adb shell 命令,减少很多输入 。
使用 -e 选项在交互式 shell 中获取到历史命令前面提到在 while 循环中不断执行 read -p 命令,可以模拟一个简易的 shell 效果 。
实际使用时遇到一个问题,那就是输入上光标键,会打印 ^[[A,输入下光标键,会打印 ^[[B,不能像 bash 那样通过上下光标键显示执行过的历史命令 。
具体执行结果如下:
$ ./tinyshell.shtinyshell> ^[[A^[[B这里打印的 ^[[A 是输入上光标键所显示,^[[B 是输入下光标键所显示 。
如果想要在执行 read 命令时,可以通过上下光标键来显示历史命令,需要加上 -e 选项,且在交互式 shell 中运行 。
查看 help read 对 -e 选项说明如下:

-e
use Readline to obtain the line in an interactive shell
即,在交互式 shell 中,read -e 会使用 readline 库来获取输入 。
readline 库支持很多强大的功能,上下光标键能够显示历史命令,就是因为默认把上下光标键绑定到 readline 库获取上下历史命令的函数 。
可以执行下面的命令来进行确认:
$ bind -p | grep -E "previous-history|next-history""C-n": next-history"eOB": next-history"e[B": next-history"C-p": previous-history"eOA": previous-history"e[A": previous-history这里的 "e[A" 就是对应上光标键,绑定到 previous-history 功能,也就是显示上一个历史命令 。
"e[B" 对应下光标键,绑定到 next_history 功能,也就是显示下一个历史命令 。
上面的 "C-p" 对应 CTRL-p,也就是同时按下 CTRL 键和 p 键,可以看到它也对应上一个历史命令 。
"C-n" 对应 CTRL-n,对应下一个历史命令 。
一般来说,直接在 bash shell 中执行 read 命令,就处于交互式 shell(interactive shell)之下 。
具体举例说明如下:
$ read^[[A^[[B$ read -eread -e这个例子先是直接执行 read 命令,然后输入上光标键,会打印 ^[[A,然后输入下光标键,又打印 ^[[B 。
之后,执行 read -e 命令,输入上光标键,会自动填充上一个历史命令,也就是正在执行的 “read -e” 命令 。
注意:这个 -e 选项只在交互式 shell 中才会生效 。一般来说,shell 脚本是在非交互式 shell 中执行 。
当在 shell 脚本中使用 read -e 时,输入上下光标键,不会再打印 ^[[A、^[[B,也不会显示历史命令,而是什么都没有打印 。
这跟 read -p 的效果有所不同,read -p 可以在输入上下光标键时,打印出 ^[[A、^[[B 。
让 shell 脚本运行在交互模式下我们可以使用下面几个方法来让 shell 脚本在交互模式下执行 。
通过 bash -i 选项指定运行在交互模式下在 bash 中,可以使用 bash 的 -i 选项来让 shell 脚本在交互模式下运行 。
查看 man bash 对 -i 选项说明如下:
-i
If the -i option is present, the shell is interactive.
即,在 shell 脚本开头,把脚本的解释器写为 #/bin/bash -i 。执行这个 shell 脚本时,就会运行在交互模式下 。
把前面的 tinyshell.sh 脚本修改成下面的内容来进行验证:
#!/bin/bash -iwhile read -ep "tinyshell> " input; doif [ "$input" == "l" ]; thenlselif [ "$input" == "quit" ]; thenbreakelseecho "Unknown input: $input"fidone相比于之前的脚本,这次的改动点是:
  • 把之前的 #!/bin/bash 改成 #!/bin/bash -i,添加 -i 选项指定运行在交互模式下 。
  • 把 read -p 改成 read -ep,添加 -e 选项指定在交互模式下用 readline 库读取用户输入 。
执行修改后的脚本,结果如下:
$ ./tinyshell.shtinyshell> #!/bin/bash -iUnknown input: #!/bin/bash -itinyshell>上面的在 “tinyshell>” 之后显示的 “#!/bin/bash -i” 是输入两次上光标键后显示出来的历史命令 。第一个输入光标键会显示脚本里面的整个 while 循环语句 。
注意:这个脚本在 Linux Debian 系统、 Linux Ubuntu 系统本地测试都能生效,可以通过上下光标键显示出历史命令 。
但是在 windows 下通过 ssh 远程登录到 Ubuntu 系统,在远程 Ununtu 系统下执行这个脚本不生效,即使把脚本开头的解释器写为 #!/bin/bash -i,read -e 命令也无法通过上下光标键读取到历史命令,输入上下光标键,什么都没有打印出来 。


推荐阅读