在 while 循环体内使用 sqlcmd 时无限循环

Rai*_*ndy 0 bash ksh sqlcmd

我有一个 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)

没有sqlcmd则输出正常

更有趣的是,如果文件内容少于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)

少于4个字符时正常

当字符超过 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)

超过 3 个字符则无限

总而言之,我很好奇如何sqlcmd导致无限循环。我可以理解,它sqlcmd继承了来自 outsiede 的输入流while loop,并且由于它继承了输入流,如果它吸收了内容,那么它应该会导致 external 的迭代减少while loop,但是怎么可能导致无限循环呢?

pjh*_*pjh 5

我最好的猜测是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)

  • @Raindy,这是一个重置输入流的 perl 命令,而不是[直接] bash shell。尝试询问 ChatGPT 是否可以使用 Unix 命令重置输入流,因为这是一个不同的问题。我在 ChatGPT 中看到的一致问题是它回答了所提出的问题:-)。几周前,我们在 SE 上遇到了一个问题,有人问 ChatGPT 如何做 X,它生成了一些代码来主要做 X,然后 OP 在 SE 上询问如何修复/增强它。结果 OP 不需要任何代码,他们只需要引用他们的变量 - ChatGPT 不知道,但人们知道 (2认同)