C程序调用shell脚本

3

我有一个调用shell脚本myScript.sh的小型C程序。ret的值是256。请帮我了解系统调用出了什么问题?

int main()
{
int ret;
ret = system (myScript.sh);
ret >>=  ret;
if (ret != 0)
{
  printf("ret is [%d]",ret);
}
}

在使用ksh shell的64位UNIX操作系统上工作


发布myScript.sh的代码。如果没有它的代码,我们无法提供太多帮助。 - Karel Petranek
如果从命令提示符(cmd-prompt)调用该脚本,它会返回什么? myScript.sh; echo $? - khachik
系统接受一个char*,你肯定是想要 ret = system("myScript.sh");。 - Matthieu
@Matthieu:当然。如果这段代码能够编译通过并且他可以得到ret的值,那么他肯定不是那么了。 - Conrad Meyer
myScript.sh 有一个可执行文件来生成该文件。我同意Mattieu的观点。 - Sachin Chourasiya
你真的应该只在原型设计时使用 system()。在实际代码中,应该使用 fork()/exec(),这样可以更好地访问来自该代码的结果。 - Lee-Man
8个回答

5
在我的系统上,man system 的输出如下:
 The system() function returns the exit status of the shell as returned by
 waitpid(2), or -1 if an error occurred when invoking fork(2) or
 waitpid(2).  A return value of 127 means the execution of the shell
 failed.

waitpid手册描述了一组宏,例如WEXITSTATUS(),它们从返回值中提取实际的退出码。

我不太确定你打算用ret >>= ret做什么,但那肯定是不对的。


给出+1的原因是提到了waitpoid()和WEXITSTATUS()(以及相关函数)。但几乎要扣掉-1的原因是没有提到system()一般来说不是一个好的选择。 - Lee-Man

4
通常情况下,*nix系统中system函数的工作方式是调用fork函数,然后子进程使用/bin/sh -c中的一个exec函数,再加上您传递给system函数的字符串,将子进程变成/bin/sh程序的实例,从而运行命令。父进程调用其中之一的wait函数,等待/bin/sh退出,它与shell脚本具有相同的退出状态,然后system函数也返回该值。
如果查看wait系统调用的手册页:
main 3 wait

你需要获取一些关于返回值的信息以及一些宏函数来帮助你理解它。 WIFEXITED(stat_val) 宏可用于测试程序是否正常退出而不是使用信号退出。正常退出涉及调用 exit 系统调用。如果此函数返回非零值,则可以使用 WEXITSTATUS(stat_val) 宏获取实际返回的值。 WIFSIGNALED(stat_val) 宏可用于测试程序是否被信号终止,如果是,则 WTERMSIG(stat_val) 宏将返回导致终止的信号编号。
还有一些其他的宏可以告诉您进程是停止还是继续运行,而不是终止,但我认为它们对您并不是特别有用,但您可能想了解一下。
至于在这种情况下实际发生了什么,很难说。如果 fork 调用失败,则 system 将能够返回-1并设置 errno 反映错误。如果 fork 没有失败,则错误可能发生在子进程中,并且更难定位。在您的平台上,可能会在分叉之前进行一些测试,以确保您有权限执行适当的文件并设置 errno 反映该问题,但也可能不会。
您应该查看 perror 函数以在设置了 errno 的情况下打印出错误消息。
如果失败发生在 fork 之后且在子进程中,则需要让 shell 告诉您更多有关正在发生的事情,或者让 shell 脚本告诉您。这可以通过在脚本中包含类似于在 C 程序中使用打印语句的 echo 语句来实现。
您还应该查看 access 函数以测试是否具有读取和/或执行文件的权限。
如果您正在使用 Linux,则应该能够执行:
strace -o my_program.strace -f ./my_program

或者

ltrace -o my_program.ltrace -f -S ./my_program

然后检查跟踪文件(在-o之后)以查看程序和内核相互交流的内容。 ltrace 查看程序如何与库函数通信,而 strace 则查看系统调用,但是 -S 告诉 ltrace 也要查看系统调用。 -f 参数告诉它们两个都要跟踪创建的程序子进程。

我刚刚注意到你说你正在使用 ksh

正如我提到的,在 Posix 系统下,system 应该使用 /bin/sh 或兼容的 shell。这并不意味着 /bin/sh 不会运行 /bin/ksh 来运行您的脚本(或者内核不会使用脚本文件开头的 #! 行来执行此操作),但这可能会成为一个问题。有一些方法可以运行 shell 脚本,使得不需要使用此行来知道应该使用哪个 shell。其中最显著的是:

. myshell.sh

period(.)和空格实际上是将文件文本转储到当前的shell会话中,而不是在另一个进程中运行它(这对于设置环境非常有用)。 如果您正在执行以下操作:

int x = system(". myshell.sh");

那可能会是个问题。

2

该命令的退出状态由两个字节编码:

  • 高位字节包含退出状态。
  • 低位字节包含杀死它的信号(如果有)。

由于0x0100等于256的十进制,因此您的Shell脚本以状态1退出。请检查您的Shell脚本,并确保在成功时以状态0退出。


如果 ret 的打印值为 256,则实际(未移位)的返回值为 512 --> 0x0200,shell 以状态 2 退出。大体上是相同的事情。 - Conrad Meyer
@Conrad:将256向右移位>> 256是无操作,因为移位距离通常取模于字长(32)。所以,它是256 >> 0。 - Greg Hewgill
2
@Greg:这不是无操作。这是未定义的行为。 - pmg

2

从标准中可以看到:

6.5.7/3 如果右操作数的值为负数或大于或等于左操作数,且升级后的左操作数的宽度,则结果未定义

因此,当您执行以下操作时:

ret >>= ret;

system函数的返回值在出现错误时可能为-1。

如果你的调用返回了这样一个负数,接下来的操作ret >>= ret;(等同于ret = -1 >> -1;)会得到一个没有意义的结果:你不能将位数右移一个负数位。

当你试图进行没有意义的操作时,C语言可以做任何事情……任何事情(包括什么都不做、按照你的预期执行、重新格式化你的硬盘、将你的银行账户转移到我的名下、让恶魔从你的鼻子里飞出来,......)。


@Sachin:在我编辑我的答案之前,请阅读这个问题和答案 :-) - pmg
谢谢 PMG,我喜欢这个措辞 :) - Sachin Chourasiya

1
确保您的脚本可执行并在路径中,或者使用完整路径。

1
事实上,我的猜测是OP习惯于在“PATH”中使用“.”,这是一个非常糟糕的想法。 - R.. GitHub STOP HELPING ICE

0

没有出错。你有阅读文档吗?请参见:

返回值
如果出错(例如 fork(2) 失败),则返回值为-1;否则为命令的返回状态,格式如 wait(2) 中所指定的格式。 因此,命令的退出代码将是 WEXITSTATUS(status)。如果无法执行 /bin/sh,则退出状态将是一个调用 exit(127) 的命令的状态。

由于256不等于-1,因此调用未失败。


脚本myScript.sh应该创建一个文件,但实际上没有创建。我没有得到返回值256。 - Sachin Chourasiya
那么你的意思是退出状态为-1吗? - Sachin Chourasiya
1
@Falmarri:255是无符号的,单字节-1...256不是。而且 system() 返回一个int。在大多数平台上,无符号int -1是0xffffffff。 - Conrad Meyer
由于他正在使用system(),因此返回值实际上是来自shell的返回值。因此,为了更精确的返回值,建议使用fork / exec。 - Lee-Man

0
为什么你要将结果进行位移?只需要删除 ret >>= ret 这一行代码,程序就可以正常运行了。

那么,海报将获得512,这在waitpid(2)状态代码中具有某种含义...这与shell脚本的退出状态有什么关系? - Conrad Meyer

0

我正在使用Linux,使用system("sh script.sh")调用脚本非常有帮助。


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