复杂的bash功能:正确使用?

时间:2011-07-31 19:23:40

标签: bash function pipe


我对自动化日常任务感兴趣。直到最近,我的一个脚本的每个部分都运行得很顺利。但是现在我试图实现zenity,一切都崩溃了。现在我希望你能看看我做错了什么。

Soo,提到一点:我有一个残酷的bash脚本,看起来像这样:

#!/bin/bash

dosomething () {
  # code
  echo $1 >> ~/test.txt # For debugging
  $1 & # ← Important line
  # more code
}

main () {
  # other code
  dosomething "/usr/bin/rsync $rsync_options" # ← Call it "do_1"
  dosomething "/usr/bin/find $findme_path -iname \"*.gpx\" -print0 | xargs -0 $other_command" # ← Call it "do_2"
  # another code
}

( main | (zenity --progress $zenity_options || $still_another_command ) &

应该发生什么:main的输出通过管道传输到zenity(进度条)。 main并不真正执行任何命令,而是使用参数调用dosomething,该参数包含要执行的命令。 dosomething执行命令。

真正发生的事情:dosomething的“echo”部分与预期的一样。脚本执行后,do_1和do_2中的命令正确显示在〜/ test.txt中。 (如果我在终端中复制并粘贴〜/ test.txt的内容,则每个命令都会以预期结果执行。)
“do_1”的“重要行”将以预期结果执行。但“do_2”的“重要路线”似乎没有效果。至少,我在执行脚本后看不到$ other_command的影响。

我希望你至少能理解我的意思。如果你在这里给我一个提示出错的提示,那将是非常友好的。

2 个答案:

答案 0 :(得分:1)

简短回答:见BashFAQ #50

答案很长:当bash解析一行时,它会在进行变量替换之前解析引号和命令分隔符(|等);结果,它在函数中运行$1 &,$ 1的值中的引号和管道永远不会被解析,它们只是被传递给命令(在这种情况下为usr / bin / find)as争论的一部分。最终结果:它实际上运行的是/usr/bin/find $findme_path -iname '"*.gpx"' -print0 '|' xargs -0 $other_command"

通常,在这种情况下,我建议将命令作为一系列单词传递(即让函数运行"$@" &,并将其称为dosomething /usr/bin/find $findme_path -iname "*.gpx" -print0,但即使这样也无法处理命令中的管道 - 它仍将被视为另一个参数。

一种可能性是使用eval。如果可能的话,应该避免这种情况,因为eval是创建脚本错误的好方法 - 大量错误,微妙的错误,难以理解的错误,安全漏洞......它为增加了一层解析命令行中的所有,这意味着,例如,如果您尝试对名为Fred's file.txt的文件进行操作,则会将该撇号作为引号并变得非常混乱。对碰巧包含反引号的文件进行操作太难以想象了。基本上,这是个坏消息。

在快速查看实际脚本之后,我推荐的是各种策略:尽可能多地隐藏shell函数中的命令复杂性,这样你就不会尝试传递管道并重定向到{ {1}}函数,然后使用我前面提到的单词系列方法。对于问题中的脚本,我会这样做:

dosomething

这意味着您的日志文件不会包含正在执行的管道的详细信息,只有#!/bin/bash dosomething () { # code printf "%q " "$@" >> ~/test.txt # this gives a much better idea what's being done than echo $1 would "$@" & # more code } # hide the pipeline in a shell function find_and_do_something () { /usr/bin/find "$1" -iname "*.gpx" -print0 | xargs -0 $other_command } main () { # other code dosomething /usr/bin/rsync $rsync_options dosomething find_and_do_something "$findme_path" # another code } ( main | (zenity --progress $zenity_options || $still_another_command ) & ,但至少它会起作用。

答案 1 :(得分:0)

您在哪里定义$other_command?另请注意,您在末尾错过了一个结束括号(main打开一个未关闭的括号。)