将命令序列作为子进程fork后无法正常执行的问题排查
问题分析与解决方案
核心问题:预查询阻塞了流程
你脚本里先执行了一次sqlplus -s "$u/$p@sredb1_high" @getdata.sql "${from}" "${to}" | tail +2 | tr -d ' '来获取总记录数,这一步其实是完整执行了一次全量数据查询,会一直阻塞直到整个查询跑完,所以后面的后台执行代码根本没机会启动——这就是你看到执行输出卡在sqlplus那一行、只能终止进程的原因。
除此之外还有几个细节问题:
wc -l "${workdir}/data.psv"的输出格式是行数 文件名,直接赋值给count会导致整数比较失败$((count / records))是整数除法,永远出不来百分比小数结果set -e会在后台进程或辅助命令出错时直接终止脚本(比如文件未创建时wc会报错)
修复方案
1. 替换全量预查询为快速count统计
不要跑全量查询拿记录数,改成执行一个只统计行数的查询,比如新建getcount.sql:
set heading off set feedback off set pagesize 0 select count(*) from 你的目标表 where 日期字段 between to_date('&1', 'YYYY-MM-DD HH24:MI:SS') and to_date('&2', 'YYYY-MM-DD HH24:MI:SS'); exit;
然后在脚本里用这个快速获取总记录数:
records=$(sqlplus -s "$u/$p@sredb1_high" @getcount.sql "${from}" "${to}" | tr -d '[:space:]')
这样几秒钟就能拿到总数,不会阻塞后续流程。
2. 修正脚本里的其他问题
修改后的extract函数示例:
function extract() { local from="${1:?You must supply a query from date as 'YYYY-MM-DD HH24:MI:SS'}" local to="${2:?You must supply a query from date as 'YYYY-MM-DD HH24:MI:SS'}" local u="${3:-${OMC_USERNAME:?no db username supplied}}" local p="${4:-${OMC_PASSWORD:?No db password supplied}}" local workdir records workdir="$(mkWorkdir "${from}" "${to}")" # 用count查询替代全量预查询 records=$(sqlplus -s "$u/$p@sredb1_high" @getcount.sql "${from}" "${to}" | tr -d '[:space:]') # 后台执行数据提取,关闭sqlplus缓冲确保实时写入文件 (sqlplus -s "$u/$p@sredb1_high" <<-EOF set heading off set feedback off set pagesize 0 set flush on @getdata.sql "${from}" "${to}" EOF | tail +2 > "${workdir}/data.psv") & local process=$! local count="0" # 提前创建文件,避免wc报错 touch "${workdir}/data.psv" until [ "${count}" -eq "${records}" ]; do # 只取行数,过滤掉文件名输出 count=$(wc -l < "${workdir}/data.psv") # 用bc计算浮点百分比 printf '%.2f%%\r' $(echo "scale=2; $count / $records * 100" | bc) sleep 5 done wait $process }
3. SQLPlus原生进度监控方案
如果要完全用SQLPlus实现进度跟踪,可以修改getdata.sql,在输出数据的同时输出当前进度,脚本直接解析输出即可:
修改后的getdata.sql示例:
set heading off set feedback off set pagesize 0 set serveroutput on size unlimited set flush on declare v_total number; v_count number := 0; begin -- 先获取总记录数 select count(*) into v_total from 你的目标表 where 日期字段 between to_date('&1', 'YYYY-MM-DD HH24:MI:SS') and to_date('&2', 'YYYY-MM-DD HH24:MI:SS'); dbms_output.put_line('TOTAL:' || v_total); -- 逐行输出数据并更新进度 for rec in (select * from 你的目标表 where 日期字段 between to_date('&1', 'YYYY-MM-DD HH24:MI:SS') and to_date('&2', 'YYYY-MM-DD HH24:MI:SS')) loop v_count := v_count + 1; -- 输出数据(替换成你的字段拼接逻辑) dbms_output.put_line(rec.字段1 || '|' || rec.字段2); -- 每100行输出一次进度(可调整频率) if mod(v_count, 100) = 0 then dbms_output.put_line('PROGRESS:' || round(v_count/v_total*100, 2) || '%'); end if; end loop; end; / exit;
对应的脚本执行逻辑:
sqlplus -s "$u/$p@sredb1_high" @getdata.sql "${from}" "${to}" | while read line; do if [[ $line == TOTAL:* ]]; then records=${line#TOTAL:} elif [[ $line == PROGRESS:* ]]; then echo -ne "${line#PROGRESS:}\r" else echo "$line" >> "${workdir}/data.psv" fi done
这种方式不需要单独的count查询,也不需要后台监控文件行数,完全通过SQLPlus的输出实现进度跟踪。
内容的提问来源于stack exchange,提问作者Christian Bongiorno
相关产品推荐
相关产品推荐

