我有一个 bash 脚本(名为opsin
),它只管理 Java 二进制文件的调用。所有参数都转发${@:1}
到 Java 二进制文件。
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
java -jar $DIR/../bin-java/opsin/opsin.jar ${@:1}
这个二进制文件opsin.jar
也接受,stdin
所以你可以这样做
echo "abcd" | java -jar opsin.jar
但我可以用上面的 bash 脚本做同样的事情:
echo "abcd" | opsin
是如何stdin
传递到
java -jar $DIR/../bin-java/opsin/opsin.jar ${@:1}
在 bash 脚本中?
答案1
魔法!
特别是,外壳将fork(2)
本身;这会产生两个(大部分)相同的 shell 进程副本;值得注意的是,子 shell 进程从父 shell 进程继承标准输入的副本:
* The child inherits copies of the parent’s set of open file descrip-
tors. Each file descriptor in the child refers to the same open
file description (see open(2)) as the corresponding file descriptor
in the parent. This means that the two descriptors share open file
status flags, current file offset, and signal-driven I/O attributes
(see the description of F_SETOWN and F_SETSIG in fcntl(2)).
然后,子shell进程将调用exec(3)
启动java
,即子 shell 进程取代本身与java
.通过此调用java
获取从父级继承的文件描述符的副本,并且可以对传递给它的标准输入进行操作。
(如果在文件描述符上设置“执行时关闭”标志,则可能会出现复杂情况,但这不是标准输入等标准标志的默认设置。)
另外,如果父 shell 在运行时不需要保留在内存中java
,则代码可以编写为:
#!/bin/bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
exec java -jar $DIR/../bin-java/opsin/opsin.jar ${@:1}
这样就java
替换了父 shell 进程,而不是让 shell 进程不必要地在内存中徘徊。
有关更多详细信息,《Unix 环境中的高级编程》(APUE) 有一些章节介绍了所涉及的各种 fork、exec、dup、pipe 系统调用。