
注: 本文为安丫科技刘峰的原创,请尊重知识产权,转发请注明出处,不接受任何抄袭、演绎和未经注明出处的转载。
在 Linux 的世界里,命令行是力量的源泉。而在这股力量的背后,重定向(Redirection)技术如同一位无形的指挥家,优雅地控制着数据的流向。
对于 Oracle 数据库管理员(DBA)和 Linux 系统管理员而言,掌握重定向不仅是一项基本功,更是实现自动化、提升运维效率、精准处理日志和错误的关键。
本文将深入探讨 Linux 的重定向技术,并结合 Oracle 数据库管理的日常场景和 Linux 运维的核心要点,展示如何将简单的符号化身为强大的生产力工具。
重定向的核心:理解标准流与文件描述符
在 Linux 中,每个进程默认都会打开三个标准的数据流:
标准输入 (stdin):进程接收输入的地方,文件描述符为 0。默认来自键盘。
标准输出 (stdout):进程输出正常结果的地方,文件描述符为 1。默认输出到屏幕。
标准错误 (stderr):进程输出错误信息的地方,文件描述符为 2。默认也输出到屏幕。
重定向技术的核心就是改变这些数据流的默认目标,让它们不指向键盘或屏幕,而是指向文件或其他命令。

01
输出重定向典型场景
> 覆盖输出:常用于将 SQL 查询结果定向到日志文件
sqlplus hr/hr@192.168.74.45:1521/freepdb1 <<EOF > query_result.log
SELECT COUNT(*) FROM USER_TABLES;
EXIT;
EOF
输出示例
$ cat query_result.log
SQL*Plus: Release 23.0.0.0.0 - Production on Tue Aug 1223:23:062025
Version 23.8.0.25.04
Copyright (c) 1982, 2025, Oracle. All rights reserved.
Last Successful login time: Tue Aug 12202523:22:32 +08:00
Connected to:
Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
Version 23.8.0.25.04
SQL>
COUNT(*)
----------
8
SQL> Disconnected from Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
Version 23.8.0.25.04
>> 追加输出:适用于持续记录数据库启停日志
echo "Database started at $(date)" >> var/log/oracle/startup.log
输出示例
$ cat startup.log
Database started at Tue Aug 12 11:23:59 PM CST 2025
2> 错误流定向:捕获 expdp 导出过程中的异常
expdp system/oracle directory=DUMP_DIR dumpfile=full.dmp logfile=expdp.log 2> expdp_errors.log
输出示例
$ cat expdp_errors.log
Export: Release 23.0.0.0.0 - Production on Tue Aug 12 23:25:15 2025
Version 23.8.0.25.04
Copyright (c) 1982, 2025, Oracle and/or its affiliates. All rights reserved.
Connected to: Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
ORA-39002: invalid operation
ORA-39070: Unable to open the log file.
ORA-39087: Directory name DUMP_DIR is invalid.
&> 合并输出:同时记录正常输出和错误信息
rman target <<EOF &> rman_backup.log
backup database;
exit;
EOF
输出示例
ecovery Manager: Release 23.0.0.0.0 - Production on Tue Aug 12 23:29:23 2025
Version 23.8.0.25.04
Copyright (c) 1982, 2025, Oracle and/or its affiliates. All rights reserved.
connected to target database: FREE (DBID=1474443220)
RMAN> backup database;
Starting backup at 12-AUG-25
using target database control file instead of recovery catalog
allocated channel: ORA_DISK_1
channel ORA_DISK_1: SID=257 device type=DISK
02
高级重定向技术应用
Here 文档:批量执行 SQL 脚本并捕获结果
SQL_OUTPUT=$(sqlplus -S hr/hr@192.168.74.45:1521/freepdb1 <<EOF
SET PAGESIZE 0 -- 取消分页
SET FEEDBACK OFF -- 不显示"已选择X行"
SET HEADING OFF -- 不显示列标题
SET TRIMSPOOL ON -- 去除每行末尾的空格
SET LINESIZE 1000 -- 设置足够大的行宽,避免内容换行
SET ECHO OFF -- 不显示执行的SQL语句
SET VERIFY OFF -- 不显示变量替换信息
-- 执行查询
SELECT table_name FROM user_tables WHERE num_rows > 100;
EXIT;
EOF
)
输出示例
# 过滤空行,只保留有效结果
echo "$SQL_OUTPUT" | sed '/^$/d'
--输出
T1
EMPLOYEES
进程替换:动态生成 RMAN 备份配置
rman target @<(echo "
run {
allocate channel ch1 device type disk format '/backup/%U';
backup database;
release channel ch1;
}
")
输出示例
rman target @<(echo "
run {
allocate channel ch1 device type disk format '/backup/%U';
backup database;
release channel ch1;
}
")
上面的示例通过CLIENT_INFO查询没有结果,需要参照另一篇文章进行配置《在 Oracle 23ai 中精准追踪用户登录 IP:从经典方法到最佳实践》
03
Linux 系统场景:管道符简化
日常管理
场景一:自动化备份与日志记录
一个合格的运维脚本,必须能记录执行日志和错误日志。
需求
编写一个 shell 脚本执行 Oracle 的 expdp 数据泵导出,并将正常日志和错误日志分别记录。
#!/bin/bash
# 定义日志文件路径,文件名包含日期
LOG_DIR="/home/oracle/logs/expdp"
DATE_TAG=$(date +%Y%m%d)
LOG_FILE="${LOG_DIR}/backup_${DATE_TAG}.log"
ERROR_FILE="${LOG_DIR}/backup_${DATE_TAG}.err"
DMP_FILE="/orabackup/full_backup_${DATE_TAG}.dmp"
# 创建日志目录(如果不存在)
mkdir -p ${LOG_DIR}
echo "--- Backup started at $(date) ---" >> ${LOG_FILE}
# 执行数据泵导出
# 1> 将标准输出追加到日志文件
# 2> 将标准错误追加到错误文件
expdp \'/ as sysdba\' \
full=y \
directory=DATA_PUMP_DIR \
dumpfile=${DMP_FILE} \
logfile=full_backup_${DATE_TAG}.log \
1>> ${LOG_FILE} 2>> ${ERROR_FILE}
# 检查错误日志文件是否为空
if [ -s "${ERROR_FILE}" ]; then
echo "--- Backup FAILED at $(date). Check error log: ${ERROR_FILE} ---" >> ${LOG_FILE}
# 此处可添加邮件告警等操作
# mail -s "Oracle Backup FAILED" your_email@example.com < ${ERROR_FILE}
else
echo "--- Backup SUCCEEDED at $(date) ---" >> ${LOG_FILE}
fi

解析

1>> ${LOG_FILE}
将 expdp 运行过程中的所有标准输出信息(如进度、对象统计等)追加到 .log 文件。
2>> ${ERROR_FILE}
如果 expdp 遇到任何问题(如权限不足、对象不存在),产生的错误信息(ORA-xxxxx)将被追加到 .err 文件。
脚本最后通过判断错误文件的大小 (-s) 来确定备份是否成功,这是非常可靠的运维实践。
场景二:使用 Here Document 运行复杂的 SQL
需求
编写一个脚本,动态生成并执行一个 SQL 来查询表空间使用率,并将结果格式化输出。
这是 <<EOF 的主场,它允许我们在 Shell 脚本中无缝嵌入多行 SQL。
#!/bin/bash
# 使用 Here Document 将多行 SQL 传递给 sqlplus
# -s 静默模式,不显示 SQL*Plus 横幅
# <<EOF 开始,EOF 结束
sqlplus -s as sysdba <<EOF
SET LINESIZE 200
SET PAGESIZE 100
COL tablespace_name FOR A30
COL total_mb FOR 999,999,990.00
COL used_mb FOR 999,999,990.00
COL free_mb FOR 999,999,990.00
COL pct_used FOR A10
-- 这是一个标准的表空间使用率查询
SELECT
fs.tablespace_name,
df.total_mb,
(df.total_mb - fs.free_mb) AS used_mb,
fs.free_mb,
TO_CHAR(ROUND((df.total_mb - fs.free_mb) df.total_mb * 100, 2)) || '%'AS pct_used
FROM
(SELECT tablespace_name, ROUND(SUM(bytes) / 1024 1024, 2) AS free_mb
FROM dba_free_space GROUP BY tablespace_name) fs
JOIN
(SELECT tablespace_name, ROUND(SUM(bytes) / 1024 1024, 2) AS total_mb
FROM dba_data_files GROUP BY tablespace_name) df
ON fs.tablespace_name = df.tablespace_name
ORDER BY 1;
EXIT;
EOF
输出示例
TABLESPACE_NAME TOTAL_MB USED_MB FREE_MB PCT_USED
------------------------------ --------------- --------------- --------------- ----------
SYSAUX 1,140.00 783.37 356.63 68.72%
SYSTEM 1,060.00 1,059.56 0.44 99.96%
UNDOTBS1 55.00 49.25 5.75 89.55%
USERS 7.00 6.06 0.94 86.57%
场景三:将查询结果直接用于操作系统命令
需求
查询所有无效的数据库对象,并生成一个 recompile.sql 脚本,然后立即执行它。
下面通过具体步骤模拟一个失效对象的产生过程:
步骤 1:创建基础对象(被依赖的表)
首先创建一个表EMP作为基础对象,后续的对象将依赖它:
-- 创建基础表
CREATE TABLE EMP (
EMP_ID NUMBER(6) PRIMARY KEY,
ENAME VARCHAR2(50),
SALARY NUMBER(10,2),
DEPT_ID NUMBER(4)
);
-- 插入测试数据
INSERT INTO EMP VALUES (1001, '张三', 5000, 10);
INSERT INTO EMP VALUES (1002, '李四', 6000, 20);
COMMIT;
步骤 2:创建依赖对象(视图和存储过程)
创建依赖EMP表的视图和存储过程,初始状态为 “有效(VALID)”:
-- 创建依赖EMP表的视图
CREATE OR REPLACE VIEW V_EMP_SALARY AS
SELECT EMP_ID, ENAME, SALARY
FROM EMP
WHERE SALARY > 5000;
-- 创建依赖EMP表的存储过程
CREATE OR REPLACE PROCEDURE P_PRINT_EMP(
p_dept_id IN NUMBER
) IS
BEGIN
FOR c IN (SELECT ENAME, SALARY FROM EMP WHERE DEPT_ID = p_dept_id) LOOP
DBMS_OUTPUT.PUT_LINE('员工: ' || c.ENAME || ', 工资: ' || c.SALARY);
END LOOP;
END;
/
步骤 3:查询对象初始状态(均为有效)
通过数据字典USER_OBJECTS查看对象状态:
<section id="nice" data-tool="mdnice编辑器" data-website="https://www.mdnice.com" style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; padding-top: 0px; padding-bottom: 0px; padding-left: 10px; padding-right: 10px; background-attachment: scroll; background-clip: border-box; background-color: rgba(0, 0, 0, 0); background-image: none; background-origin: padding-box; background-position-x: 0%; background-position-y: 0%; background-repeat: no-repeat; background-size: auto; width: auto; font-family: Optima, 'Microsoft YaHei', PingFangSC-regular, serif; font-size: 16px; color: rgb(0, 0, 0); line-height: 1.5em; word-spacing: 0em; letter-spacing: 0em; word-break: break-word; overflow-wrap: break-word; text-align: left;"><pre class="custom" data-tool="mdnice编辑器" style="border-radius: 5px; box-shadow: rgba(0, 0, 0, 0.55) 0px 2px 10px; text-align: left; margin-top: 10px; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; padding-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px;"><span style="display: block; background: url(https://files.mdnice.com/user/3441/876cad08-0422-409d-bb5a-08afec5da8ee.svg); height: 30px; width: 100%; background-size: 40px; background-repeat: no-repeat; background-color: #282c34; margin-bottom: -7px; border-radius: 5px; background-position: 10px 10px;"></span><code class="hljs" style="overflow-x: auto; padding: 16px; color: #abb2bf; padding-top: 15px; background: #282c34; border-radius: 5px; display: -webkit-box; font-family: Consolas, Monaco, Menlo, monospace; font-size: 12px;">SELECT OBJECT_NAME, OBJECT_TYPE, <span class="hljs-function" style="line-height: 26px;">STATUS <br>FROM USER_OBJECTS <br>WHERE OBJECT_NAME <span class="hljs-title" style="color: #61aeee; line-height: 26px;">IN</span> <span class="hljs-params" style="line-height: 26px;">(<span class="hljs-string" style="color: #98c379; line-height: 26px;">'EMP'</span>, <span class="hljs-string" style="color: #98c379; line-height: 26px;">'V_EMP_SALARY'</span>, <span class="hljs-string" style="color: #98c379; line-height: 26px;">'P_PRINT_EMP'</span>)</span></span>;<br><br></code></pre>
</section>
初始结果(所有对象状态为VALID):
OBJECT_NAME | OBJECT_TYPE | STATUS
---------------|--------------|-------
EMP | TABLE | VALID
V_EMP_SALARY | VIEW | VALID
P_PRINT_EMP | PROCEDURE | VALID
步骤 4:修改基础表结构,破坏依赖关系
删除EMP表中被依赖的列(如SALARY),此时依赖该列的视图和存储过程会变为失效状态:
-- 修改基础表,删除被依赖的SALARY列
ALTER TABLE EMP DROP COLUMN SALARY;
步骤 5:验证对象失效状态
再次查询对象状态,发现依赖SALARY列的对象已变为INVALID:
SELECT OBJECT_NAME, OBJECT_TYPE, STATUS
FROM USER_OBJECTS
WHERE OBJECT_NAME IN ('V_EMP_SALARY', 'P_PRINT_EMP');
失效结果
OBJECT_NAME | OBJECT_TYPE | STATUS
---------------|--------------|-------
V_EMP_SALARY | VIEW | INVALID
P_PRINT_EMP | PROCEDURE | INVALID
这个例子完美展示了重定向如何成为数据库和操作系统之间的桥梁。
#!/bin/bash
RECOMPILE_SCRIPT="/tmp/recompile_invalid_objects.sql"
SPOOL_LOG="/tmp/recompile.log"
# 第一步:使用 Here Document 和重定向 > 生成 SQL 脚本
sqlplus sys/oracle@db1:1521/freepdb1 as sysdba <<EOF > ${RECOMPILE_SCRIPT}
SET HEADING OFF;
SET FEEDBACK OFF;
SET PAGESIZE 0;
SET TRIMSPOOL ON -- 去除每行末尾的空格
SET LINESIZE 1000 -- 设置足够大的行宽,避免内容换行
SET ECHO OFF -- 不显示执行的SQL语句
SET VERIFY OFF -- 不显示变量替换信息
-- 直接在 SQL 中生成另一个可执行的 SQL 语句
SELECT 'ALTER ' || object_type || ' ' || owner || '."' || object_name || '" COMPILE;'
FROM dba_objects
WHERE status = 'INVALID' and owner='HR';
EXIT;
EOF
echo "Recompile script generated at ${RECOMPILE_SCRIPT}"
echo "Now executing the script..."
# 第二步:使用重定向 < 将生成的脚本作为输入,并用 >> 保存执行日志
sqlplus sys/oracle@db1:1521/freepdb1 as sysdba < ${RECOMPILE_SCRIPT} >> ${SPOOL_LOG}
echo "Recompilation finished. Check log at ${SPOOL_LOG}"
解析
第一个 sqlplus 块的输出(ALTER... 命令)通过 > 被重定向到了 recompile.sql 文件中。
第二个 sqlplus 命令通过 < 从 recompile.sql 读取命令来执行,并将其执行结果通过 >> 追加到日志文件。
输出示例
$ sh cm.sh
Recompile script generated at tmp/recompile_invalid_objects.sql
Now executing the script...
Recompilation finished. Check log at tmp/recompile.log
场景四:静默执行与错误捕获 (运维要点:Cron Job 最佳实践)
需求
在 Cron 定时任务中运行一个检查脚本,希望它在没有错误时保持完全静默,只在出错时发送邮件通知。
>/dev/null 2>&1 是 Cron Job 的黄金搭档。
# 在 crontab 中的配置
# 每天凌晨3点执行健康检查脚本
# 正常运行时,所有输出(stdout 和 stderr)都被丢到黑洞
# 如果脚本以非零状态退出(即出错),cron 的 MAILTO 机制会发送邮件
MAILTO="dba-team@example.com"
0 3 * * * path/to/health_check.sh >/dev/null 2>&1
而在 health_check.sh 脚本内部,我们可以更精细地控制:
#!/bin/bash
ERROR_LOG="/var/log/oracle/health_check.err"
# 执行一个可能会失败的操作,例如检查监听器状态
# 正常输出被丢弃,错误输出被重定向到错误日志
lsnrctl status >/dev/null2> ${ERROR_LOG}
# 如果 lsnrctl 失败,它会返回一个非零的退出码
if [ $? -ne 0 ]; then
echo "Listener check failed at $(date)"
cat ${ERROR_LOG} # 将捕获到的错误信息输出,这样 cron 就会把它发送出去
exit 1 # 以非零状态退出,触发 cron 告警
fi
exit 0 # 正常退出
写在最后
Linux 重定向技术在 Oracle 数据库运维中展现出强大的灵活性和扩展性,通过与数据库管理操作的深度整合,能够显著提升运维效率和系统稳定性。
在实际应用中,需要结合 Linux 系统特性,从性能优化、安全加固、日志管理等多个维度进行综合考量,形成完整的运维解决方案。
通过合理运用重定向技术,运维人员可以实现数据库全生命周期的精细化管理,为企业关键业务系统的稳定运行提供坚实保障。

作者介绍
大家好,我是刘峰,安丫科技创始人 & 数据库技术高级讲师,专注于 PostgreSQL、国产数据库运维与迁移、数据库性能优化 等方向。
作为 PG中国分会官方授权讲师、PostgreSQL ACE 讲师认证专家,我长期活跃在一线项目实战中,拥有 10年以上大型数据库管理与优化经验,曾深度参与电信、金融、政务等多个行业的数据库性能调优与迁移项目。
欢迎关注我,一起深入探索数据库的无限可能,技术交流不设限!
📌 觉得有收获的话,记得点赞、收藏、转发支持一下哦,别忘了关注我获取更多数据库干货~
关键词回复(可见相应文章):
oracle、mysql、pg、postgresql、sql、性能优化、故障处理、数据迁移、备份恢复、版本升级、补丁管理、深度巡检、解决方案、架构设计......

有任何问题或疑问,欢迎加V进群探讨哦~
\ | /
★
动动你的手指
给【安呀智数据坊】加个星标吧~
这样你就不会丢下我啦~
记得加星标呀!









