一、问题背景
最近接手的Oracle数据库比较多,但是Oracle 的 sqlplus 客户端默认不支持方向键查看历史命令的功能,也不支持backspace回退键,很不友好,严重影响工作效率。相信很多人在使用sqlplus的时候看到过类似的乱码提示:
SQL> select sysdate from dual;
SYSDATE
---------
14-SEP-25
--按上下方向键提示:
SQL> ^[[A " - rest of line ignored.
SQL> 042: unknown command "
SQL>
SQL>
--按backspace键提示:
SQL> select a ^H^H^H^H
2
SQL> exit
接下来详细介绍使用readline和rlwrap实现上下方向键查看历史命令以及正常使用backspace回退键的方法,实现 sqlplus 命令行的流畅操作。
二、分步操作步骤
1、安装readline依赖包
readline 提供命令行编辑与历史记录功能,是 rlwrap 的核心依赖。
yum install readline*
## 检查已安装的readline包
rpm -qa|grep readline
操作过程如下:
[root@node3 yum.repos.d]# yum install readline*
Loaded plugins: fastestmirror, langpacks
Loading mirror speeds from cached hostfile
* base: mirrors.aliyun.com
* extras: mirrors.aliyun.com
* updates: mirrors.aliyun.com
Package readline-6.2-11.el7.x86_64 already installed and latest version
Package readline-devel-6.2-11.el7.x86_64 already installed and latest version
Resolving Dependencies
--> Running transaction check
---> Package readline-static.x86_64 0:6.2-11.el7 will be installed
--> Finished Dependency Resolution
Dependencies Resolved
================================================================================================================================================================================================================================================================
Package Arch Version Repository Size
================================================================================================================================================================================================================================================================
Installing:
readline-static x86_64 6.2-11.el7 base 118 k
Transaction Summary
================================================================================================================================================================================================================================================================
Install 1 Package
Total download size: 118 k
Installed size: 550 k
Is this ok [y/d/N]: y
Downloading packages:
readline-static-6.2-11.el7.x86_64.rpm | 118 kB 00:00:00
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
Warning: RPMDB altered outside of yum.
Installing : readline-static-6.2-11.el7.x86_64 1/1
Verifying : readline-static-6.2-11.el7.x86_64 1/1
Installed:
readline-static.x86_64 0:6.2-11.el7
Complete!
## 检查已安装的readline包
[root@node3 yum.repos.d]# rpm -qa|grep readline
readline-static-6.2-11.el7.x86_64
readline-devel-6.2-11.el7.x86_64
readline-6.2-11.el7.x86_64
[root@node3 yum.repos.d]#
2、安装wlwrap包
rlwrap 是命令行包装工具,可给 sqlplus 附加方向键功能,需源码编译安装。
cd /soft
##下载wlwrap包
wget https://github.com/hanslub42/rlwrap/archive/refs/heads/master.zip
unzip rlwrap-master.zip -d rlwrap
cd /soft/rlwrap/rlwrap-master
yum install -y autoconf automake ##系统中不存在时安装
autoreconf --install
./configure
make
make install
安装过程如下:
[root@node3 soft]# rpm -qa|grep autoconf autoconf-2.69-11.el7.noarch [root@node3 soft]# rpm -qa|grep automake automake-1.13.4-3.el7.noarch [root@node3 soft]# ls rlwrap rlwrap-master.zip [root@node3 soft]# cd rlwrap/ [root@node3 rlwrap]# ls rlwrap-master [root@node3 rlwrap]# cd rlwrap-master/ [root@node3 rlwrap-master]# ls AUTHORS BUGS ChangeLog completions configure.ac COPYING doc filters INSTALL Makefile.am NEWS README.md src test [root@node3 rlwrap-master]# autoreconf --install configure.ac:32: installing 'tools/config.guess' configure.ac:32: installing 'tools/config.sub' configure.ac:34: installing 'tools/install-sh' configure.ac:34: installing 'tools/missing' parallel-tests: installing 'tools/test-driver' src/Makefile.am: installing 'tools/depcomp' [root@node3 rlwrap-master]# ls aclocal.m4 AUTHORS autom4te.cache BUGS ChangeLog completions config.h.in configure configure.ac COPYING doc filters INSTALL Makefile.am Makefile.in NEWS README.md src test tools [root@node3 rlwrap-master]# [root@node3 rlwrap-master]# ./configure checking build system type... x86_64-unknown-linux-gnu checking host system type... x86_64-unknown-linux-gnu checking for a BSD-compatible install... /usr/bin/install -c checking whether build environment is sane... yes checking for a thread-safe mkdir -p... /usr/bin/mkdir -p checking for gawk... gawk checking whether make sets $(MAKE)... yes checking whether make supports nested variables... yes checking whether make sets $(MAKE)... (cached) yes checking whether build environment is sane... yes checking for style of include used by make... GNU checking for gcc... gcc checking whether the C compiler works... yes checking for C compiler default output file name... a.out checking for suffix of executables... checking whether we are cross compiling... no checking for suffix of object files... o checking whether we are using the GNU C compiler... yes checking whether gcc accepts -g... yes checking for gcc option to accept ISO C89... none needed checking dependency style of gcc... gcc3 checking for ptytty_create in -lptytty... no configure: error: libptytty not found, install it or specify --without-libptytty ######此处报错 [root@node3 rlwrap-master]# [root@node3 rlwrap-master]# ./configure --without-libptytty checking build system type... x86_64-unknown-linux-gnu checking host system type... x86_64-unknown-linux-gnu checking for a BSD-compatible install... /usr/bin/install -c checking whether build environment is sane... yes checking for a thread-safe mkdir -p... /usr/bin/mkdir -p checking for gawk... gawk checking whether make sets $(MAKE)... yes checking whether make supports nested variables... yes checking whether make sets $(MAKE)... (cached) yes checking whether build environment is sane... yes checking for style of include used by make... GNU checking for gcc... gcc checking whether the C compiler works... yes checking for C compiler default output file name... a.out checking for suffix of executables... checking whether we are cross compiling... no checking for suffix of object files... o checking whether we are using the GNU C compiler... yes checking whether gcc accepts -g... yes checking for gcc option to accept ISO C89... none needed checking dependency style of gcc... gcc3 checking for ptytty_create in -lptytty... no checking for gcc... (cached) gcc checking whether we are using the GNU C compiler... (cached) yes checking whether gcc accepts -g... (cached) yes checking for gcc option to accept ISO C89... (cached) none needed checking dependency style of gcc... (cached) gcc3 checking how to run the C preprocessor... gcc -E checking for perl... /usr/bin/perl checking for strip... strip checking for sys/wait.h that is POSIX.1 compatible... yes checking for grep that handles long lines and -e... /usr/bin/grep checking for egrep... /usr/bin/grep -E checking for ANSI C header files... yes checking for sys/types.h... yes checking for sys/stat.h... yes checking for stdlib.h... yes checking for string.h... yes checking for memory.h... yes checking for strings.h... yes checking for inttypes.h... yes checking for stdint.h... yes checking for unistd.h... yes checking errno.h usability... yes checking errno.h presence... yes checking for errno.h... yes checking fcntl.h usability... yes checking fcntl.h presence... yes checking for fcntl.h... yes checking libgen.h usability... yes checking libgen.h presence... yes checking for libgen.h... yes checking libutil.h usability... no checking libutil.h presence... no checking for libutil.h... no checking for stdlib.h... (cached) yes checking for string.h... (cached) yes checking sched.h usability... yes checking sched.h presence... yes checking for sched.h... yes checking sys/file.h usability... yes checking sys/file.h presence... yes checking for sys/file.h... yes checking sys/ioctl.h usability... yes checking sys/ioctl.h presence... yes checking for sys/ioctl.h... yes checking for sys/wait.h... (cached) yes checking sys/resource.h usability... yes checking sys/resource.h presence... yes checking for sys/resource.h... yes checking stddef.h usability... yes checking stddef.h presence... yes checking for stddef.h... yes checking termios.h usability... yes checking termios.h presence... yes checking for termios.h... yes checking for unistd.h... (cached) yes checking for stdint.h... (cached) yes checking time.h usability... yes checking time.h presence... yes checking for time.h... yes checking sys/time.h usability... yes checking sys/time.h presence... yes checking for sys/time.h... yes checking getopt.h usability... yes checking getopt.h presence... yes checking for getopt.h... yes checking regex.h usability... yes checking regex.h presence... yes checking for regex.h... yes checking curses.h usability... yes checking curses.h presence... yes checking for curses.h... yes checking stropts.h usability... no checking stropts.h presence... no checking for stropts.h... no checking termcap.h usability... yes checking termcap.h presence... yes checking for termcap.h... yes checking util.h usability... no checking util.h presence... no checking for util.h... no checking stdarg.h usability... yes checking stdarg.h presence... yes checking for stdarg.h... yes checking for term.h... yes checking for ncurses/term.h... yes checking whether PROC_PIDVNODEPATHINFO is declared... no checking whether procstat_open_sysctl is declared... no checking whether procstat_getprocs is declared... no checking whether procstat_getfiles is declared... no checking whether STAILQ_FOREACH is declared... no checking argument type of tputs putc function... int checking for an ANSI C-conforming const... yes checking for pid_t... yes checking for getopt_long... yes checking for basename... yes checking for dirname... yes checking for flock... yes checking for getopt_long... (cached) yes checking for isastream... yes checking for pselect... yes checking for sched_yield... yes checking for setitimer... yes checking for setsid... yes checking for setrlimit... yes checking for sigaction... yes checking for system... yes checking whether mkstemps is declared... yes checking whether snprintf is declared... yes checking whether strlcat is declared... no checking whether strnlen is declared... yes checking whether setenv is declared... yes checking whether putenv is declared... yes checking whether readlink is declared... yes checking whether nice is declared... yes checking for openpty in -lutil... yes checking for openpty... yes checking for getpty... no checking for grantpt... yes checking for unlockpt... yes checking for getpt... checking for pty/tty type... checking pty.h usability... yes checking pty.h presence... yes checking for pty.h... yes OPENPTY configure: checking for pty ranges checking for tgetent... no checking for tgetent in -ltinfo... yes checking for readline in -lreadline... yes checking for tigetnum... yes checking readline/readline.h usability... yes checking readline/readline.h presence... yes checking for readline/readline.h... yes checking whether your readline headers and library know about rl_set_screen_size... yes checking whether your readline headers and library know about rl_basic_quote_characters... yes checking whether your readline headers and library know about rl_variable_value... yes checking whether your readline headers and library know about rl_readline_version... yes checking whether your readline headers and library know about rl_executing_keyseq... no checking whether the private symbol _rl_horizontal_scroll_mode is visble in your readline libs... yes Will rlwrap find command's working directory under /proc/<commands pid>/cwd? let's see... checking for /proc/67789/cwd/configure.ac... yes checking whether we can find command line under <opt_proc_mountpoint>/<pid>/cmdline and mirror it by overwriting our own *argv (this may take a few secs)... yes Trying to backdate src/completion.rb a few secs in order to prevent a spurious call to a (possibly not installed) helper 'rbgen' checking that generated files are newer than configure... done checking that generated files are newer than configure... done configure: creating ./config.status config.status: creating Makefile config.status: creating filters/Makefile config.status: creating doc/Makefile config.status: creating src/Makefile config.status: creating doc/rlwrap.man config.status: creating config.h config.status: executing depfiles commands Now do: make (or gmake) to build rlwrap make check for instructions how to test it make install to install it [root@node3 rlwrap-master]# make make all-recursive make[1]: Entering directory `/soft/rlwrap/rlwrap-master' Making all in doc make[2]: Entering directory `/soft/rlwrap/rlwrap-master/doc' sed -e 's#@DATADIR@#/usr/local/share#' rlwrap.man > rlwrap.1 make[2]: Leaving directory `/soft/rlwrap/rlwrap-master/doc' Making all in src make[2]: Entering directory `/soft/rlwrap/rlwrap-master/src' gcc -DHAVE_CONFIG_H -I. -I.. -DDATADIR=\"/usr/local/share\" -g -O2 -MT main.o -MD -MP -MF .deps/main.Tpo -c -o main.o main.c mv -f .deps/main.Tpo .deps/main.Po gcc -DHAVE_CONFIG_H -I. -I.. -DDATADIR=\"/usr/local/share\" -g -O2 -MT signals.o -MD -MP -MF .deps/signals.Tpo -c -o signals.o signals.c mv -f .deps/signals.Tpo .deps/signals.Po gcc -DHAVE_CONFIG_H -I. -I.. -DDATADIR=\"/usr/local/share\" -g -O2 -MT readline.o -MD -MP -MF .deps/readline.Tpo -c -o readline.o readline.c mv -f .deps/readline.Tpo .deps/readline.Po gcc -DHAVE_CONFIG_H -I. -I.. -DDATADIR=\"/usr/local/share\" -g -O2 -MT pty.o -MD -MP -MF .deps/pty.Tpo -c -o pty.o pty.c mv -f .deps/pty.Tpo .deps/pty.Po gcc -DHAVE_CONFIG_H -I. -I.. -DDATADIR=\"/usr/local/share\" -g -O2 -MT completion.o -MD -MP -MF .deps/completion.Tpo -c -o completion.o completion.c mv -f .deps/completion.Tpo .deps/completion.Po gcc -DHAVE_CONFIG_H -I. -I.. -DDATADIR=\"/usr/local/share\" -g -O2 -MT term.o -MD -MP -MF .deps/term.Tpo -c -o term.o term.c mv -f .deps/term.Tpo .deps/term.Po gcc -DHAVE_CONFIG_H -I. -I.. -DDATADIR=\"/usr/local/share\" -g -O2 -MT ptytty.o -MD -MP -MF .deps/ptytty.Tpo -c -o ptytty.o ptytty.c mv -f .deps/ptytty.Tpo .deps/ptytty.Po gcc -DHAVE_CONFIG_H -I. -I.. -DDATADIR=\"/usr/local/share\" -g -O2 -MT utils.o -MD -MP -MF .deps/utils.Tpo -c -o utils.o utils.c mv -f .deps/utils.Tpo .deps/utils.Po gcc -DHAVE_CONFIG_H -I. -I.. -DDATADIR=\"/usr/local/share\" -g -O2 -MT string_utils.o -MD -MP -MF .deps/string_utils.Tpo -c -o string_utils.o string_utils.c mv -f .deps/string_utils.Tpo .deps/string_utils.Po gcc -DHAVE_CONFIG_H -I. -I.. -DDATADIR=\"/usr/local/share\" -g -O2 -MT malloc_debug.o -MD -MP -MF .deps/malloc_debug.Tpo -c -o malloc_debug.o malloc_debug.c mv -f .deps/malloc_debug.Tpo .deps/malloc_debug.Po gcc -DHAVE_CONFIG_H -I. -I.. -DDATADIR=\"/usr/local/share\" -g -O2 -MT multibyte.o -MD -MP -MF .deps/multibyte.Tpo -c -o multibyte.o multibyte.c mv -f .deps/multibyte.Tpo .deps/multibyte.Po gcc -DHAVE_CONFIG_H -I. -I.. -DDATADIR=\"/usr/local/share\" -g -O2 -MT filter.o -MD -MP -MF .deps/filter.Tpo -c -o filter.o filter.c mv -f .deps/filter.Tpo .deps/filter.Po gcc -DDATADIR=\"/usr/local/share\" -g -O2 -o rlwrap main.o signals.o readline.o pty.o completion.o term.o ptytty.o utils.o string_utils.o malloc_debug.o multibyte.o filter.o -lutil -lreadline -ltinfo make[2]: Leaving directory `/soft/rlwrap/rlwrap-master/src' Making all in filters make[2]: Entering directory `/soft/rlwrap/rlwrap-master/filters' pod2man --section=3pm RlwrapFilter.pm > RlwrapFilter.3pm make[2]: Leaving directory `/soft/rlwrap/rlwrap-master/filters' make[2]: Entering directory `/soft/rlwrap/rlwrap-master' make[2]: Leaving directory `/soft/rlwrap/rlwrap-master' make[1]: Leaving directory `/soft/rlwrap/rlwrap-master' [root@node3 rlwrap-master]# make install Making install in doc make[1]: Entering directory `/soft/rlwrap/rlwrap-master/doc' make[2]: Entering directory `/soft/rlwrap/rlwrap-master/doc' make[2]: Nothing to be done for `install-exec-am'. /usr/bin/mkdir -p '/usr/local/share/man/man1' /usr/bin/install -c -m 644 rlwrap.1 '/usr/local/share/man/man1' make[2]: Leaving directory `/soft/rlwrap/rlwrap-master/doc' make[1]: Leaving directory `/soft/rlwrap/rlwrap-master/doc' Making install in src make[1]: Entering directory `/soft/rlwrap/rlwrap-master/src' make[2]: Entering directory `/soft/rlwrap/rlwrap-master/src' /usr/bin/mkdir -p '/usr/local/bin' /usr/bin/install -c rlwrap '/usr/local/bin' make[2]: Nothing to be done for `install-data-am'. make[2]: Leaving directory `/soft/rlwrap/rlwrap-master/src' make[1]: Leaving directory `/soft/rlwrap/rlwrap-master/src' Making install in filters make[1]: Entering directory `/soft/rlwrap/rlwrap-master/filters' make[2]: Entering directory `/soft/rlwrap/rlwrap-master/filters' make[2]: Nothing to be done for `install-exec-am'. /usr/bin/mkdir -p '/usr/local/share/man/man3' /usr/bin/install -c -m 644 RlwrapFilter.3pm '/usr/local/share/man/man3' make[2]: Leaving directory `/soft/rlwrap/rlwrap-master/filters' make[1]: Leaving directory `/soft/rlwrap/rlwrap-master/filters' make[1]: Entering directory `/soft/rlwrap/rlwrap-master' make[2]: Entering directory `/soft/rlwrap/rlwrap-master' make[2]: Nothing to be done for `install-exec-am'. /usr/bin/mkdir -p '/usr/local/share/rlwrap' /usr/bin/mkdir -p '/usr/local/share/rlwrap/filters' /usr/bin/install -c -m 644 filters/README filters/RlwrapFilter.pm filters/RlwrapFilter.3pm filters/count_in_prompt filters/pipeto filters/logger filters/null filters/unbackspace filters/pipeline filters/ftp_filter filters/handle_hotkeys filters/history_format filters/simple_macro filters/template filters/scrub_prompt filters/paint_prompt filters/censor_passwords filters/listing filters/paint_prompt.py filters/handle_hotkeys.py filters/logger.py filters/pipeto.py filters/rlwrapfilter.py filters/null.py filters/censor_passwords.py filters/edit_history filters/count_in_prompt.py filters/ftp_filter.py filters/debug_null filters/handle_sigwinch filters/outfilter filters/makefilter filters/dissect_prompt filters/nl_and_then_prompt.py '/usr/local/share/rlwrap/filters' /usr/bin/mkdir -p '/usr/local/share/rlwrap/completions' /usr/bin/install -c -m 644 completions/testclient completions/coqtop '/usr/local/share/rlwrap/completions' make install-data-hook make[3]: Entering directory `/soft/rlwrap/rlwrap-master' chmod a+x /usr/local/share/rlwrap/filters/* make[3]: Leaving directory `/soft/rlwrap/rlwrap-master' make[2]: Leaving directory `/soft/rlwrap/rlwrap-master' make[1]: Leaving directory `/soft/rlwrap/rlwrap-master'
3、查看rlwrap的路径
[root@node3 rlwrap-master]# which rlwrap /usr/local/bin/rlwrap
4、配置oracle用户环境变量
通过别名(alias)让 Oracle 用户启动 sqlplus 时自动加载 rlwrap,无需每次手动输入。
cat >> /home/oracle/.bash_profile<<EOF alias sqlplus='/usr/local/bin/rlwrap /data/app/oracle/product/19.3.0/db/bin/sqlplus' EOF source /home/oracle/.bash_profile
到现在为止,在sqlplus客户端工具中已经可以实现自由翻页了。
5、功能验证
- 启动 sqlplus:
sqlplus 用户名/密码@数据库实例名
- 测试功能:
- 按 上/下方向键:查看历史执行命令。
- 按 Backspace 键:删除光标前的字符。
- 无乱码提示即配置成功。
三、常见问题解决
| 问题现象 | 原因 | 解决方案 |
|---|---|---|
执行 ./configure 时报 libptytty not found |
缺少 libptytty 依赖库 | 重新执行 ./configure --without-libptytty(跳过该依赖) |
启动 sqlplus 提示 rlwrap: command not found |
rlwrap 路径未加入环境变量 | 直接使用绝对路径(如 /usr/local/bin/rlwrap),或执行 echo 'export PATH=$PATH:/usr/local/bin' >> ~/.bash_profile |
| 别名不生效 | 未执行 source 命令,或编辑了错误的环境变量文件 |
执行 source ~/.bash_profile,确认文件为 Oracle 用户的 ~/.bash_profile(非 root 用户) |
四、总结
通过“安装readline依赖 → 编译rlwrap工具 → 配置sqlplus别名”三步,可快速解决 sqlplus 方向键、backspace回退键失效问题。配置后,sqlplus 操作体验与 Linux 原生命令行一致,大幅提升 SQL 编写与调试效率。赶快试试吧!
关于作者
网名:飞天,墨天轮2024年度优秀原创作者,拥有 Oracle 10g OCM 认证、PGCE认证、MySQL 8.0 OCP认证以及OBCA、KCP、KCSM、ACP、YCP、磐维等众多国产数据库认证证书,目前从事Oracle、Mysql、PostgresSQL、磐维数据库管理运维工作,喜欢结交更多志同道合的朋友,热衷于研究、分享数据库技术。
微信公众号:飞天online
墨天轮:https://www.modb.pro/u/15197
如有任何疑问,欢迎大家留言,共同探讨~~~




