我有一个 bash 脚本,用于在while 循环sqlcmd体中从数据库获取数据:
#!/bin/bash
tmpfile=$(mktemp)
echo -e "1\n2" > tmpfile
echo "---------------content from the tmp file--------------"
cat tmpfile
echo "------------------------------------------------------"
while read -r line || [[ -n $line ]]
do
echo "${line}"
echo " ------------sqlcmd operation start------------"
sqlcmd -Q "set nocount on;select 'dummy'"
echo -e " ------------sqlcmd operation done------------\n\n"
done < tmpfile
echo "-----------------------script done---------------------"
Run Code Online (Sandbox Code Playgroud)
预计会根据 file.txt 中的内容行进行交互,在本例中是两次,但实际上它将无限地交互第一行:

我得到了关于如何从这里解决无限循环的答案,当使用 sqlcmd 时循环变量被固定为常量
此处Unexpected behavior of file描 述符和/或 I/O 流在 ksh 中使用 -i 选项(从文件读取 sql)运行 sqlcmd 后创建无限循环。
简单来说,如果我们重定向输入流就可以解决,例如:
#!/bin/bash
tmpfile=$(mktemp)
echo -e "1\n2" > tmpfile
echo "---------------content from the tmp file--------------"
cat tmpfile
echo "------------------------------------------------------"
while read -r line || [[ -n $line ]]
do
echo "${line}"
echo " ------------sqlcmd operation start------------"
sqlcmd -Q "set nocount on;select 'dummy'" < /dev/null
echo -e " ------------sqlcmd operation done------------\n\n"
done < tmpfile
echo "-----------------------script done---------------------"
Run Code Online (Sandbox Code Playgroud)
下面的脚本只是替换sqlcmd为其他命令,例如date然后就正常了。我认为这可以证明这个问题与sqlcmd:
#!/bin/bash
tmpfile=$(mktemp)
echo -e "1\n2" > tmpfile
echo "---------------content from the tmp file--------------"
cat tmpfile
echo "------------------------------------------------------"
while read -r line || [[ -n $line ]]
do
echo "${line}"
echo " ------------sqlcmd operation start------------"
#sqlcmd -Q "set nocount on;select 'dummy'"
date
echo -e " ------------sqlcmd operation done------------\n\n"
done < tmpfile
echo "-----------------------script done---------------------"
Run Code Online (Sandbox Code Playgroud)
更有趣的是,如果文件内容少于4个字符(包括不可见字符),则不会导致无限迭代:
#!/bin/bash
tmpfile=$(mktemp)
#less than 4 char -- normal
echo -ne "123" > tmpfile
echo "---------------content from the tmp file--------------"
cat tmpfile
echo "------------------------------------------------------"
while read -r line || [[ -n $line ]]
do
echo "${line}"
echo " ------------sqlcmd operation start------------"
sqlcmd -Q "set nocount on;select 'dummy'"
#date
echo -e " ------------sqlcmd operation done------------\n\n"
done < tmpfile
echo "-----------------------script done---------------------"
Run Code Online (Sandbox Code Playgroud)
当字符超过 3 个时为无限:
#!/bin/bash
tmpfile=$(mktemp)
#more than 3 chars -- infinite
echo -ne "1234" > tmpfile
echo "---------------content from the tmp file--------------"
cat tmpfile
echo "------------------------------------------------------"
while read -r line || [[ -n $line ]]
do
echo "${line}"
echo " ------------sqlcmd operation start------------"
sqlcmd -Q "set nocount on;select 'dummy'"
#date
echo -e " ------------sqlcmd operation done------------\n\n"
done < tmpfile
echo "-----------------------script done---------------------"
Run Code Online (Sandbox Code Playgroud)
总而言之,我很好奇如何sqlcmd导致无限循环。我可以理解,它sqlcmd继承了来自 outsiede 的输入流while loop,并且由于它继承了输入流,如果它吸收了内容,那么它应该会导致 external 的迭代减少while loop,但是怎么可能导致无限循环呢?
我最好的猜测是sqlcmd使用lseek将其标准输入的文件偏移量设置回文件的开头。当然,这样做会导致无限循环问题。此Shellcheck -clean 代码用于perl重现该问题:
#! /bin/bash -p
while read -r line || [[ -n $line ]]; do
printf '%s\n' "$line"
perl -e 'seek(STDIN, 0, 0)'
done <"${BASH_SOURCE[0]}"
Run Code Online (Sandbox Code Playgroud)
使用 Bash 运行包含此代码的文件会生成输出
#! /bin/bash -p
#! /bin/bash -p
#! /bin/bash -p
...
Run Code Online (Sandbox Code Playgroud)
直到被中断。
验证这一理论的一种方法是使用strace ( strace -f -o strace.out SHELLPROG) 运行 shell 程序。我希望该过程的输出包含类似以下内容sqlcmd:
... lseek(0, 0, SEEK_SET) = 0
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
214 次 |
| 最近记录: |