【问题标题】:UNIX shell script do loop execute commandsUNIX shell 脚本循环执行命令
【发布时间】:2015-07-15 17:32:41
【问题描述】:

一般来说,我不明白如何使 UNIX shell 脚本中的大多数命令循环工作,就像它们直接从命令行工作一样(使用 bash)。

作为一个简单的测试,一个名为 looping.sh 的脚本来执行一个 SQL 脚本(在这种情况下 filelist.txt 中的内容无关紧要):

for i in $(cat filelist.txt) 
do  $(sqlplus DB_USER/password@abc @test.sql) 
done

结果

looping.sh:第 2 行:SQL*Plus:: 找不到命令

对于 filelist.txt 中的每一行。第二行的其他变体不起作用,例如将其放在引号中等。

或者,如果 filelist.txt 有其他 sh 脚本的名称,假设在这种情况下是一行 称为_file1.sh,我想执行它

for i in $(cat filelist.txt) 
do  exec $i
done

结果

: 未找到第 2 行: exec: called_file1.sh

文件都在同一个文件夹中。我尝试了第二行的变体,例如 /bin/sh $i,将其放在引号中等等。在do循环中执行命令的神奇方式是什么?

【问题讨论】:

  • 您的第一个示例是否打算引用"$i"?好像没有,而且错误信息与命令不符。
  • @kojiro 错误来自我们可以看到的命令输出的“命令”。

标签: bash shell loops unix


【解决方案1】:

$(...) 获取内容并将其作为命令运行,然后返回命令的输出。

所以当你写的时候:

for i in $(cat filelist.txt) 
do  $(sqlplus DB_USER/password@abc @test.sql) 
done

shell 在遇到循环体时执行的操作是运行 sqlplus DB_USER/password@abc @test.sql,然后它从该命令(无论它可能是什么)获取 输出 并将 $(...) 位替换为它。所以你最终会得到一个看起来像这样的循环(不完全是因为它会在每个循环中再次发生,而是为了说明):

for i in $(cat filelist.txt) 
do  <output of 'sqlplus DB_USER/password@abc @test.sql' command>
done

如果该输出不是有效的 shell 命令,您将收到错误。

解决方案是不这样做。你根本不想要包装$()

for i in $(cat filelist.txt) 
do  sqlplus DB_USER/password@abc @test.sql
done

在你的第二个例子中:

for i in $(cat filelist.txt) 
do  exec $i
done

您是在告诉 shell $i 中的文件名是它应该尝试像二进制或可执行 shell 脚本一样执行的东西。

在您的情况下,这里发生了两件事。 $i 中的文件名找不到并且(这更难注意到)$i 中的文件名在末尾包含回车符(可能是 DOS 行结束文件) . (这就是为什么错误消息比正常情况更令人困惑的原因。)(我实际上想知道这一点,因为我没想到来自未引用的$i 但来自引用的"$i" 但我可能只是错了.)

因此,对于这种情况,您需要从文件中删除回车符(有关此内容的更多信息,请参见 标签info wiki 的“在询问有问题的代码之前”部分的第 1 点)和那么你需要确保文件名是一个可执行脚本并且你有正确的路径。

哦,还有,exec 永远不会返回,所以循环只会执行一个文件。

如果您想要多次执行,请删除exec

大家都在说你Don't Read Lines With For。请参阅Bash FAQ 001,了解如何正确(且安全地)从文件中读取行。

【讨论】: