如何延迟 shell 脚本中的命令替换?

How to delay a command substitution in a shell script?

我有一个 bash 脚本,它在 nutshell 中通过其他命令生成一个文件(工作正常),然后在脚本中我对生成的文件执行 wc 命令。我的问题是我在 wc 命令上使用命令替换,当我执行脚本时,它会立即执行此替换,而不是等待脚本中较早生成的文件。我有哪些选择?

该脚本是 运行 Oracle SQL 加载程序的 shell 程序,用于将数据加载到 Oracle Table。 SQLLoader 命令生成一个包含被拒绝记录的文件,我正在尝试对其进行字数统计。这是我的代码:

#!/bin/sh

#   Set variables

program_name="My Program Name"
input_dir="/interface/inbound/hr_datafile"
log_dir="/interface/inbound/log"
bad_dir="/interface/inbound/hr_bad"
archive_dir="/interface/inbound/hr_archive"
input_base="Import_20171213"
input_ext="csv"

control_file="data_loader.ctl"

exit_status=0
d=`date "+%Y%m%d"`
t=`date "+%H%M"`


#   Check if data file exists, count records

if [ -f ${input_dir}/${input_base}*.${input_ext} ]; then
    data_file_name=`ls -1rt ${input_dir}/${input_base}*.${input_ext} | head -1 | cut -c 32-100`
    data_file_base=${data_file_name%.*}
    echo "Data file name: " ${data_file_name}
    echo "Data file base: " ${data_file_base}
    no_of_recs=`expr $(wc -l ${input_dir}/${data_file_name} | cut -c 1-8) - 1`
    echo "DEBUG no_of_recs: " ${no_of_recs}
    no_of_errs=0
else
    echo
    echo
    echo 
    echo "----------------------------- ${program_name} ------------------------------------------"
    echo "----------------------------------- Error report : ------------------------------------------------"
    echo 
    echo "Please place your Data files in the UNIX directory => "${input_dir}
    echo "${program_name} Process exiting ..."
    echo
    echo "---------------------------------------------------------------------------------------------------"
    echo
    echo
    echo
    echo
    echo 
    exit 1
fi


#   Run SQL*Loader

echo
echo "==> Loading Data...into MY_TABLE table"
echo

# Create a temporary control file to pass the data file name in
cat $XX_TOP/bin/${control_file} | sed "s/:FILENAME/${data_file_name}/g" > $XX_TOP/bin/${data_file_base}_temp.ctl

# NOTE:  The following sqlldr format is used to "hide" the oracle user name and password
sqlldr errors=100000 skip=1 control=$XX_TOP/bin/${data_file_base}_temp.ctl data=${input_dir}/${data_file_name} log=${log_dir}/${data_file_base}.log bad=${bad_dir}/${data_file_base}.bad <<-END_SQLLDR > /dev/null
apps/`cat ${DB_SCRIPTS}/.${ORACLE_SID}apps`
END_SQLLDR

exit_status=$?
echo "DEBUG exit_status " ${exit_status}    

# Remove temporary control file
rm -rf $XX_TOP/bin/${data_file_base}_temp.ctl


#   Check for Errors

if [ -f ${bad_dir}/${data_file_base}.bad ]; then
    echo
    echo "----------------------------- ${program_name} ------------------------------------------"
    echo "----------------------------------- Error report : ------------------------------------------------"
    echo
    grep 'Record' ${log_dir}/${data_file_base}.log > ${log_dir}/${data_file_base}.rec
    grep 'ORA' ${log_dir}/${data_file_base}.log > ${log_dir}/${data_file_base}.err
    paste ${log_dir}/${data_file_base}.rec ${log_dir}/${data_file_base}.err ${bad_dir}/${data_file_base}.bad
    echo
    echo "<---------------------------------End of Error Report---------------------------------------------->"
    echo

    # Count error records
    no_of_errs=$(wc -l ${bad_dir}/${data_file_base}.bad | cut -c 1-8)
    no_of_recs=$(expr ${no_of_recs} - ${no_of_errs})

    # Remove temp files
    rm ${log_dir}/${data_file_base}.rec
    rm ${log_dir}/${data_file_base}.err
    rm ${bad_dir}/${data_file_base}.bad
else
    echo "Bad File not found at ${bad_dir}/${data_file_base}.bad"
fi

if (( ${no_of_errs} > 0 )); then 
    echo "Error found in data file..."
    exit_status=1
else
    # Archive the data file if no errors
    mv ${input_dir}/${data_file_name} ${archive_dir}/${data_file_base}_$d"_"$t.${input_ext}
    echo "Data file archived to ${archive_dir}"
    exit_status=0
fi

echo
echo
echo 
echo "----------------------------- ${program_name} ------------------------------------------"
echo 
echo "Total records errored out         :" ${no_of_errs}
echo "Total records successfully read   :" ${no_of_recs}
echo "---------------------------------------------------------------------------------------------------"
echo




#   Final Exit Status



if [ ${exit_status} -eq 1 ]; then
    echo "==> Exiting process...Status : ${exit_status}"
    exit 1
fi

if 条件检查中引用的文件是生成的文件,所以我正在检查文件是否存在,然后 运行在其上设置 wc。我知道它过早地执行了,因为这个 wc 错误出现在我的脚本输出之前应该:

Data file name:  Import_20171213.csv
Data file base:  Import_20171213
DEBUG no_of_recs:  27

==> Loading Data...into MY_TABLE table

wc: 0653-755 Cannot open /interface/inbound/hr_bad/Import_20171213.bad.
Username:
SQL*Loader: Release 10.1.0.5.0 - Production on Thu Dec 21 12:42:39 2017

Copyright (c) 1982, 2005, Oracle.  All rights reserved.

Commit point reached - logical record count 27
Program exited with status 2

在代码中,.bad 文件上的 wc 命令在 sqlldr 部分之后执行,但日志显示错误发生在调用 sqlldr 之前。

如有任何想法,我们将不胜感激!谢谢!

您没有看到预期的所有输出 - 调用 SQL*Loader 之后的任何内容都丢失了 - 来自 wc 的错误出现在错误的位置,而不是之前在 Username; 提示之后。但它不应该错误地给出您使用的构造 - 如果文件不存在,if 测试应该停止到达该行。

问题是它没有看到 heredoc 的结尾。 heredoc 开始之后的所有命令都作为 heredoc 处理的一部分执行,但不会去任何地方或显示到终端(如预期的那样),并且所有正在评估的方式导致 wc 到 运行 意外,即使该文件不存在。您会看到 stderr 的输出。

这一切都发生在 SQL*Loader 启动之前,因此您会在之后看到 Username: 提示。

从评论看来,heredoc 结尾的 END_SQLLDR 确实在您的实际代码中缩进了,这没有反映在发布的问题中。当您使用 <<- heredoc 形式时,这意味着它缩进了空格而不是制表符。这导致无法将其识别为此处文档的结尾。

来自The Linux Documentation Project

The closing limit string, on the final line of a here document, must start in the first character position. There can be no leading whitespace. Trailing whitespace after the limit string likewise causes unexpected behavior. The whitespace prevents the limit string from being recognized.

删除空格,使其成为行中的第一件事即可解决问题。