Shell脚本While循环中执行Oracle数据库INSERT语句仅运行一次的问题排查与解决
解决Shell脚本While循环调用Oracle SQLcl仅执行一次的问题
我一眼就看出问题所在了——你的sql命令(SQLcl)会抢占while read循环的标准输入!
问题根源
当你用while read -r line done < $reference_file时,整个循环的输入来自$reference_file。但每次调用sql命令时,SQLcl默认会从**标准输入(stdin)**读取交互输入,这就会把$reference_file里剩下的所有行都当作自己的输入吃掉。第一次循环执行完sql后,文件里剩下的2行已经被SQLcl读走了,while read自然没有内容可以继续循环,直接退出。
核心解决方案
让sql命令不要读取原来的标准输入,最简单的方法就是把它的输入重定向到/dev/null,这样它就不会干扰while循环的输入流了。
修改你的runScript函数里的sql命令行,加上< /dev/null即可:
runScript() { sql ${USER}/\"${PASS}\"@${DB_INSTANCE} @temp.sql $1 $2 $3 $4 < /dev/null if [[ $? -ne 0 ]]; then echo "Error"; exit 2; fi }
完整修改后的脚本
#!/bin/sh ODATE=${1} file_deliver_time="20210218:12:56:76" reference_file=/var/tmp/Sneha/SSM_KPI_source_file_checklist.txt HOME_DIR="/var/tmp/Sneha" source ~/env/INFORMATICA.env source ${HOME_DIR}/db.properties runScript() { sql ${USER}/\"${PASS}\"@${DB_INSTANCE} @temp.sql $1 $2 $3 $4 < /dev/null if [[ $? -ne 0 ]]; then echo "Error"; exit 2; fi } cd ${HOME_DIR} while read -r line do echo $line if [[ -f ${HOME_DIR}/temp.sql ]]; then rm -f ${HOME_DIR}/temp.sql; fi echo "whenever sqlerror exit failure;" > temp.sql echo "set define on echo on" >> temp.sql echo "INSERT INTO test_ssm_kpi (source_system,file_name,delivery_timestamp,ODATE) VALUES ('&1' , '&2' , '&3' , '&4');" >> temp.sql echo "exit;" >> temp.sql file_path=$(echo $line | awk -F ';' '{print $1}' | xargs echo -n ) file_str=$(echo $line | awk -F ';' '{print $2}' | xargs echo -n ) file_type=$(echo $line | awk -F ';' '{print $3}' | xargs echo -n ) file_source_sys=$(echo $line | awk -F ';' '{print $4}' | xargs echo -n ) echo "runScript ${file_path} ${file_str} ${file_type} ${file_source_sys}" runScript ${file_source_sys} ${file_str} ${file_type} ${ODATE} done < $reference_file
额外优化建议(可选)
为了让脚本更简洁高效,你还可以做以下调整:
- 用Here Document一次性生成
temp.sql,替代多次echo操作:
cat > temp.sql <<EOF whenever sqlerror exit failure; set define on echo on INSERT INTO test_ssm_kpi (source_system,file_name,delivery_timestamp,ODATE) VALUES ('&1' , '&2' , '&3' , '&4'); exit; EOF
- 用
IFS分割行内容,替代多次awk调用:
# 用分号作为分隔符,直接读取所有字段 IFS=';' read -r file_path file_str file_type file_source_sys <<< "$line" # 去除字段首尾空格(如果有) file_path=$(xargs echo -n <<< "$file_path") file_str=$(xargs echo -n <<< "$file_str") file_type=$(xargs echo -n <<< "$file_type") file_source_sys=$(xargs echo -n <<< "$file_source_sys")
调整后的循环部分会更简洁,运行效率也更高。
内容的提问来源于stack exchange,提问作者Sneha
相关产品推荐
相关产品推荐

