PHP的命令行扩展Readline相关函数学习
PHP 作为一个 Web 开发语言,相对来说,命令行程序并不是它的主战场。所以很多年轻的 PHP 开发者可能连命令行脚本都没有写过,更别提交互式的命令操作了。而今天,我们带来的这个扩展就是针对 PHP 的交互式命令行操作的。
readline 扩展函数实现了访问 GNU Readline 库的接口。这些函数提供了可编辑的命令行。一个例子是在 Bash 中允许你使用箭头按键来插入字符或者翻看历史命令。因为这个库的交互特性,这个功能在你写的 Web 程序中没多大用处,但是当你写的脚本被用在命令行中时非常有用。
Readline 扩展的安装
Readline 扩展已经加入了 PHP 的官方安装包中,如果是新的 PHP 环境,那么在编译的时候加上 --with-readline 即可。另外,我们还需要安装操作系统的 Readline 库。当然,如果已经是正常运行的 PHP ,也可以重新编译一下。
# yum install -y readline-devel
# ./congiure xxxx --with-readline
默认情况下,如果没有在编译时增加 --whit-readline ,Readline 的一些函数也是可以使用的,不过它们调用的是系统的 libedit 库。有一些函数,比如 readline_list_history() 这种函数是无法使用的。要想完整的使用 Readline 扩展的能力,那么还是需要安装操作系统的 libreadline 库(上面 yum 安装的那个 readline-devel )并在 PHP 中进行相应参数的编译安装。
基本函数操作
Readline 扩展提供的函数不多,也非常的简单易用。
读取一行
$line = readline("请输入命令:"); // 读取命令行交互信息
echo $line, PHP_EOL; // aaa
运行 PHP 代码后,我们就进入了命令提示符等待状态,并且会提示“请输入命令:”,当我们输入了 aaa 并回车之后,输入的内容就保存到了 $line 变量中。
命令历史列表相关操作
Readline 很强大的一个功能就是它自带一套命令历史记录的功能。不过这个需要我们自己手动地将命令加入到命令历史中。
$line = readline("请输入命令:"); // 读取命令行交互信息
if (!empty($line)) {
readline_add_history($line); // 需要手动加入到命令历史记录中
}
echo $line, PHP_EOL; // aaa
$line = readline("请输入命令:");
if (!empty($line)) {
readline_add_history($line);
}
// 命令历史记录列表
print_r(readline_list_history());
// Array
// (
// [0] => aaa
// [1] => bbb
// )
使用 readline_add_history() 函数,就可以将一条命令加入到命令历史记录中,然后使用 readline_list_history() 就能够打印出我们之前在交互式环境中发送过的命令记录。当然,如果只是这样简单的保存再打印那就没意思了,它还能将这些历史信息保存到外部文件进行存储。
// 将命令历史记录写入到一个文件中
readline_write_history('./readline_history');
// ./readline_history中
// _HiStOrY_V2_
// aaa
// bbb
// 清理命令历史记录
readline_clear_history();
print_r(readline_list_history());
// Array
// (
// )
// 从文件中读取命令历史记录
readline_read_history('./readline_history');
print_r(readline_list_history());
// Array
// (
// [0] => bbb
// [1] => bbb
// )
我们使用 readline_write_history() 函数将当前的命令历史记录保存到一个文件中,然后使用 readline_clear_history() 清理掉目前命令历史记录列表中的内容,这个时候打印 readline_list_history() 的话里面已经没有任何东西了。接着,我们再使用 readline_read_history() 将命令的历史记录从文件中加载回来进行还原。这一套功能是不是就非常有意思了,我们可以记录客户的所有命令操作,不管是安全审查还是事件回放,都非常有用。
查看 Readline 状态
// 当前命令行内部的变量信息
print_r(readline_info());
// Array
// (
// [line_buffer] => bbb
// [point] => 3
// [end] => 3
// [mark] => 0
// [done] => 1
// [pending_input] => 0
// [prompt] => 请输入命令:
// [terminal_name] => xterm-256color
// [completion_append_character] =>
// [completion_suppress_append] =>
// [library_version] => 7.0
// [readline_name] => other
// [attempted_completion_over] => 0
// )
readline_info() 函数就比较简单了,我们可以看到最后一条交互式命令的信息,里面包括了命令输入的内容 line_buffer ,内容长度 point ,提示信息 prompt 等内容。
命令提示效果
在 Linux 等操作系统上,我们想不起一个命令的全拼没关系,只需要记住它的前几个字符然后按两个 Tab 键就可以得到相关的命令提示了。Readline 扩展库当然也为我们准备了这样的功能。
// 类似于命令行中按 Tab 键的提示效果
readline_completion_function(function ($input, $index) {
$commands = ['next', 'exit', 'quit'];
$matches = [];
if ($input) {
// 如果关键字包含在命令中,提示命令信息
foreach ($commands as $c) {
if (strpos($c, $input) !== false) {
$matches[] = $c;
}
}
}else{
$matches = $commands;
}
return $matches;
});
// 使用 Tab 键测试一下吧
$line = trim(readline("请输入命令:"));
if (!empty($line)) {
readline_add_history($line);
}
echo $line, PHP_EOL; // 当前输入的命令信息
// 如果命令是 exit 或者 quit ,就退出程序执行
if($line == 'exit' || $line == 'quit'){
exit;
}
readline_completion_function() 函数会接收一个回调函数,当在交互式命令行模式下,也就是 readline 函数调用时,按下 Tab 键的时候,就会进入到这个函数的回调函数中。\$input 是当前已经输入内容的值,$index 是第几个字符。我们在这个回调函数中定义了几个默认的命令,当你键入一个 n 时直接按 Tab 键,程序就是提示出完整的 next 命令出来。当然,多个相同的字母开头的都是可以通过这个 $matches 数组返回呈现的。
此外,在这段代码中,如果我们输入了 exit 或者 quit 。将退出程序的运行。
字符回调操作相关示例
最后几个函数我们将通过一个复杂的小测试来学习。
// 输出的内容进入这个回调函数中
function rl_callback($ret)
{
global $c, $prompting;
echo "您输入的内容是: $ret\n";
$c++;
readline_add_history($ret);
// 限制了就调用10次,也可以通过命令行输入的内容来判断,比如上面的 exit 那种进行退出
if ($c > 10) {
$prompting = false;
// 移除上一个安装的回调函数句柄并且恢复终端设置
readline_callback_handler_remove();
} else {
// 继续进行递归回调
readline_callback_handler_install("[$c] 输入点什么内容: ", 'rl_callback');
}
}
$c = 1;
$prompting = true;
// 初始化一个 readline 回调接口,然后终端输出提示信息并立即返回,需要等待 readline_callback_read_char() 函数调用后才会进入到回调函数中
readline_callback_handler_install("[$c] 输入点什么内容: ", 'rl_callback');
// 当 $prompting 为 ture 时,一直等待输入信息
while ($prompting) {
$w = null;
$e = null;
$r = array(STDIN);
$n = stream_select($r, $w, $e, null);
if ($n && in_array(STDIN, $r)) {
// 当一个行被接收时读取一个字符并且通知 readline 调用回调函数
readline_callback_read_char();
}
}
echo "结束,完成所有输入!\n";
// [1] 输入点什么内容: A
// 您输入的内容是: A
// [2] 输入点什么内容: B
// 您输入的内容是: B
// [3] 输入点什么内容: C
// 您输入的内容是: C
// [4] 输入点什么内容: D
// 您输入的内容是: D
// [5] 输入点什么内容: E
// 您输入的内容是: E
// [6] 输入点什么内容: F
// 您输入的内容是: F
// [7] 输入点什么内容: G
// 您输入的内容是: G
// [8] 输入点什么内容: H
// 您输入的内容是: H
// [9] 输入点什么内容: I
// 您输入的内容是: I
// [10] 输入点什么内容: J
// 您输入的内容是: J
// 结束,完成所有输入!
print_r(readline_list_history());
// Array
// (
// [0] => A
// [1] => B
// [2] => C
// [3] => D
// [4] => E
// [5] => F
// [6] => G
// [7] => H
// [8] => I
// [9] => J
// )
首先,我们先不管上面的这个自定义的函数,直接向下看到 readline_callback_read_char() 。它的作用是当一个行被接收时读取一个字符并且通知 readline 调用回调函数。也就是当一行输入完成后,键入了回车之后,这个函数将通知 Readline 组件去调用 readline_callback_handler_install() 注册的回调函数。
readline_callback_handler_install() 函数的功能是初始化一个 readline 回调接口,然后终端输出提示信息并立即返回,如果在回调函数中不进行什么操作的话,这个函数就只是输出一个提示就结束了。在我们例子中的这个回调函数 rl_callback() 中,我们根据当前接收命令的次数,判断如果接收的命令在十次内,则继续接收命令直到十次命令为止就调用 readline_callback_handler_remove() 移除上一个 readline_callback_handler_install() 安装的回调并恢复终端的默认设置。
最后执行的结果就是注释中的内容,大家也可以自己复制下代码后运行调试,只有自己进行过的调试才能理解的更加深入。
总结
Readline 很强大,而且也是 PHP 默认安装包中自带的扩展。一般被加入默认的扩展都是经过时间检验而且非常有用的扩展,大家可以根据这些内容再进行更加深入的学习并运用到实战中。
测试代码:
参考文档:
https://www.php.net/manual/zh/book.readline.php
https://www.php.cn/php-weizijiaocheng-339883.html
===============
关注公众号:【硬核项目经理】获取最新文章
添加微信/QQ好友:【xiaoyuezigonggong/149844827】免费得PHP、项目管理学习资料
知乎、公众号、抖音、头条搜索【硬核项目经理】
B站ID:482780532
PHP的命令行扩展Readline相关函数学习的更多相关文章
- Linux命令行扩展和被括起来的集合
命令行扩展:`` 和 $() 单引号'' 双引号"" 反向单引号`` 在很多场景下效果不同 [root@centos8 ~]#echo "echo $HOSTNAME&q ...
- Linux上超酷的命令行扩展工具Oh My Zsh
Oh My Zsh 是一款社区驱动的命令行工具,正如它的主页上说的,Oh My Zsh 是一种生活方式.它基于 zsh 命令行,提供了主题配置,插件机制,已经内置的便捷操作.给我们一种全新的方式使用命 ...
- Linux命令行及Vim简单学习记录
Linux命令行 1.打开命令行 Ctrl+Alt+t 2.目录 显示当前目录的文件列表 ls 跳转至当前目录中的x文件夹 cd x 返回根目录 cd 3.文件 新建文件1.cpp touch ./1 ...
- 探索Windows命令行系列(2):命令行工具入门
1.理论基础 1.1.命令行的前世今生 1.2.命令执行规则 1.3.使用命令历史 2.使用入门 2.1.启动和关闭命令行 2.2.执行简单的命令 2.3.命令行执行程序使用技巧 3.总结 1.理论基 ...
- Windows命令行command的Shell命令详细解析和语法
CMD命令大全及详细解释和语法 Microsoft Windows XP [版本 5.1.2600] 有关某个命令的详细信息,请键入 HELP 命令名 ASSOC 显示或修改文件扩展名关联. A ...
- ubuntu命令行下java工程编辑与算法(第四版)环境配置
ubuntu命令行下java工程编辑与算法(第四版)环境配置 java 命令行 javac java 在学习算法(第四版)中的实例时,因需要安装配套的java编译环境,可是在编译java文件的时候总是 ...
- [Nuget]Nuget命令行工具安装
下载 地址:https://www.nuget.org/downloads 直接下最新推荐版本(recommended latest)就好了. 是个单一的nuget.exe文件. 安装配置 想要在wi ...
- 转载:linux编程,命令行参数输入getopt
下面资料来自百度百科: getopt(分析命令行参数) 相关函数 表头文件 #include<unistd.h> 定义函数 int getopt(int argc,char * const ...
- 学习 Linux,101: Linux 命令行
概述 本教程将简要介绍 bash shell 的一些主要特性,涵盖以下主题: 使用命令行与 shell 和命令交互 使用有效的命令和命令序列 定义.修改.引用和导出环境变量 访问命令历史和编辑工具 调 ...
随机推荐
- 批量删除gmail邮件
以删除tor.com发送的邮件为例说明. 首先点击邮件搜索框右边的三角,在"发件人"下面写上"tor.com": 点"搜索"按钮,看一下范围 ...
- Notes about WindowPadX
WindowPadX乃一Autohotkey脚本,具有强大的单/多显示器窗口排布能力且易于配置.有了它,那些Pro版收费的.需要安装的DisplayFusion, MultiMon TaskBar, ...
- awk-03-操作符
操作符 在awk中,有3种情况表达式为假: 1.数字是0 2.空字符串 3.未定义的值 数值运算,未定义变量初始值为0 字符运算,未定义变量初始值为空 示例 1.截取整数( + - ) 2.感叹号 读 ...
- 【笔记】KNN之超参数
超参数 超参数 很多时候,对于算法来说,关于这个传入的参数,传什么样的值是最好的? 这就涉及到了机器学习领域的超参数 超参数简单来说就是在我们运行机器学习之前用来指定的那个参数,就是在算法运行前需要决 ...
- Linux下MySQL主从复制(Binlog)的部署过程
什么是 MySQL 的主从复制 Mysql内建的复制功能是构建大型高性能应用程序的基础, 将Mysql数据分布到多个系统上,这种分布机制是通过将Mysql某一台主机数据复制到其它主机(slaves)上 ...
- WPF下获取文件运行路径、运行文件名等
在客户端开发过程中,经常需要获取相对路径的一些资源,而相对路径的就与客户端运行文件的路径息息相关了.在以前的winform开发中,我们可以使用 System.Windows.Forms.Applica ...
- JavaWeb单体项目的分层设计与实现
1.概述 为什么要把一个完整的项目(Project)按层拆分成多个模块(Module)? 1)使项目层次更加的清晰: 2)提高代码的复用性: 3)细化分工: 4)解耦. 是不是听起来很高大尚,今天就简 ...
- Java-线程池专题 (美团)
实现多线程的三种方式,继承Thread,实现Runnable 和 实现 Executor接口 ,具体参考:Java 多线程 三种实现方式 去美团,问到了什么是线程池,如何使用,为什么要用,以下做个总结 ...
- vue2.0中文文档
地址1: 链接: https://pan.baidu.com/s/1uEzM990A-W-fl23ref2zww 提取码: rkpt 复制这段内容后打开百度网盘手机App,操作更方便哦 地址2:htt ...
- 深入浅出Mybatis系列(一)---Mybatis简介
1.什么是MyBatis? MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且 ...