如何在expect shell脚本中获取衍生进程的退出代码?

29

我试图执行一个脚本,该脚本执行了一个带有退出码的被孵化进程和EXPECT脚本。但是我无法将被孵化进程的退出码获取到主脚本中。我总是得到零作为成功的返回值。

EXPECT脚本如下:

 [Linux Dev:anr ]$ cat testexit.sh
 #!/bin/bash
 export tmp_script_file="/home/anr/tmp_script_temp.sh"
 cp /home/anr/tmp_script $tmp_script_file
 chmod a+x $tmp_script_file
 cat $tmp_script_file
 expect << 'EOF'
 set timeout -1
 spawn  $env(tmp_script_file)
 expect {
 "INVALID "  { exit 4 }
 timeout     { exit 4 }
 }
 EOF
 echo "spawned process status" $?
 rm -f $tmp_script_file
 echo "done"

生成的脚本:

 [Linux Dev:anr ]$ cat tmp_script
 exit 3

Expect脚本的执行:

 [Linux Dev:anr ]$ ./testexit.sh
 exit 3
 spawn /home/anr/tmp_script_temp.sh
 spawned process status 0
 done

问题是我无法获取从expect脚本中生成的退出返回代码。我希望将生成的脚本的退出代码3传递给主脚本,并且主脚本应该以退出代码3退出。

请帮助我将生成的退出代码传递给主脚本。


尝试将expect部分放在单独的文件中,即可执行的expect脚本,并尝试获取退出状态。 - PradyJord
@Jord,我必须在shell脚本中编写expect方法。它不应该是单独的可执行文件。 - ANR
我从链接得到了一些答案,但无法将该解决方案集成到我的代码中。我对expect方法还很陌生。 - ANR
3个回答

43
您可以使用 wait 命令获取生成进程的退出状态:
expect <<'END'
log_user 0
spawn sh -c {echo hello; exit 42}
expect eof
puts $expect_out(buffer)

lassign [wait] pid spawnid os_error_flag value

if {$os_error_flag == 0} {
    puts "exit status: $value"
} else {
    puts "errno: $value"
}
END

hello

exit status: 42

来自expect手册

wait [args]

延迟直到一个已经被生成的进程(如果没有指定进程,则为当前进程)终止。

wait 通常返回一个由四个整数组成的列表。第一个整数是等待的进程的pid。第二个整数是对应的生成id。第三个整数如果发生操作系统错误,则为-1,否则为0。如果第三个整数为0,则第四个整数是生成进程返回的状态。如果第三个整数为-1,则第四个整数是操作系统设置的errno值。全局变量errorCode也会被设置。


更改

expect {
"INVALID "  { exit 4 }
timeout     { exit 4 }
}

为了

expect {
    "INVALID "  { exit 4 }
    timeout     { exit 4 }
    eof
}

然后添加lassignif命令。

3
您的期望可能比较陈旧。请改用以下命令:foreach {pid spawnid os_error_flag value} [wait] break。此命令可用于等待子进程完成并获取其退出状态。 - glenn jackman
请问为什么要使用“[ ]”来包含等待命令? - allenwang
1
这是Tcl命令替换语法。您必须执行命令,然后将结果捕获到变量中。请参阅https://tcl.tk/man/tcl8.6/TclCmd/Tcl.htm规则7。 - glenn jackman

14

在 Glenn 的帮助下,我得到了解决方案... 我的最终脚本是:

expect 脚本如下:

 [Linux Dev:anr ]$ cat testexit.sh
 #!/bin/bash
 export tmp_script_file="/home/anr/tmp_script_temp.sh"
 cp /home/anr/tmp_script $tmp_script_file
 chmod a+x $tmp_script_file
 cat $tmp_script_file
 expect << 'EOF'
 set timeout -1
 spawn  $env(tmp_script_file)
 expect {
 "INVALID "  { exit 4 }
 timeout     { exit 4 }
 eof
 }

 foreach {pid spawnid os_error_flag value} [wait] break

 if {$os_error_flag == 0} {
     puts "exit status: $value"
     exit $value
 } else {
     puts "errno: $value"
     exit $value
 }
 EOF
 echo "spawned process status" $?
 rm -f $tmp_script_file
 echo "done"

生成的脚本:

 [Linux Dev:anr ]$ cat tmp_script
 exit 3

Expect脚本的执行:

 [Linux Dev:anr ]$ ./testexit.sh
 exit 3
 spawn /home/anr/tmp_script_temp.sh
 exit status: 3
 spawned process status 3
 done

再次感谢 Glenn..


感谢分享您的解决方案。foreach {pid spawnid os_error_flag value} [wait] break 相对于 @glennjackman 的 lassign [wait] pid spawnid os_error_flag value 有什么好处? - Stéphane Gourichon
lassign在Tcl 8.5中推出。对于旧版的Tcl,foreach是实现它的方法。 - glenn jackman

2
几天来,我一直在尝试扩展变量,但在 expect heredoc 中遇到了困难。最终,我发现了另一种方法,认为这可能对需要帮助的人有所帮助。我的要求是将命令和密码传递给一个 shell 函数,在 expect heredoc 中作为远程主机执行命令,并获取返回退出代码。
示例:
function shell_function {
   # Get the command and password as arguments
   # Run command using expect
   # Return the exit code
}

shell_function <cmd> <password>
echo $?

像其他人一样,在heredoc中扩展变量是一个问题,需要将值导出到环境变量中,并使用env在heredoc内获取变量。由于密码是其中一个参数,我不想将其存储为环境变量的一部分。因此,我没有用单引号括起来heredoc开头,而是转义了heredoc的变量。这允许直接使用传递的参数。

以下是最终脚本:

#! /bin/bash
# This function runs a command like 'ssh' and provides the password
function run_with_password {
    cmd="$2"
    paswd="$1"
    expect << END
        set timeout 60
        spawn $cmd
        expect {
            "yes/no" { send "yes\r" }
            "*assword*" { send -- $paswd\r }
        }
        expect EOF
        catch wait result
        exit [lindex \$result 3]
END
}

my_password="AnswerIS42Really?"
cmd_to_run="ssh userid@hostname"
cmd_to_run="$cmd_to_run ls .sawfish"
run_with_password $my_password "$cmd_to_run"
echo "Command run code: $?"

在上述代码中,被转义的 expect 变量是 $result。将变量更改为 \$result 后,脚本开始像魔法般工作。

我要真诚地感谢那些回答以下问题的用户,他们为我提供了跨越困难的阶梯。

Douglas Leeder: help with expect script, run cat on remote comp and get output of it to the variable

glenn jackman: How to return spawned process exit code in Expect script?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接