简体   繁体   English

Bash子shell消耗父进程的标准输入

[英]Bash subshell consumes stdin of the parent process

Let's say I have a main.sh script that will be calling one.sh via a subshell. 假设我有一个main.sh脚本,它将通过子shell调用one.sh

one.sh : one.sh

#! /bin/bash

set -euo pipefail

(if [ -t 0 ]; then
  echo "one little two little three little buses"
else
  cat -
fi) | awk '{ $1 = "111"; print $0 }'

main.sh : main.sh

#! /bin/bash                                                

set -euo pipefail                                           

main() {                                                    
  echo "one_result) $(./one.sh)"                            

  echo "one_piped) $(echo "the quick brown fox" | ./one.sh)"
}                                                           

main                                                        

Now, each of them works as expected: 现在,它们每个都按预期工作:

$ ./one.sh
111 little two little three little buses

$ ./main.sh
one_result) 111 little two little three little buses
one_piped) 111 quick brown fox

But, when I pipe something to main.sh , I was not expecting (or, rather, I don't want) one.sh to know about the piped content, because I thought one.sh was in its own subshell in one_result) : 但是,当我通过管道将一些内容传输到main.sh ,我并不希望(或者,我不想) one.sh知道管道内容,因为我认为one.sh位于one_result)的子one_result)

$ echo "HELLO WORLD MAIN" | ./main.sh                                                                                
one_result) 111 WORLD MAIN
one_piped) 111 quick brown fox

Is it the case my if condition in one.sh is not what I want? 是我的one.sh if条件不是我想要的吗? I would like one.sh to not create any side-effects of consuming my main.sh 's stdin - since now it has consumed it, my main.sh is now effectively stdin -less, as stdin can only be read once unless I store it away. 我希望one.sh不会产生消耗main.sh的stdin的任何副作用-因为现在它已经消耗掉了,我的main.sh现在实际上是stdin -less,因为stdin只能读取一次,除非我存放起来。

Thoughts? 思考? TIA. TIA。

In general, subshells (and other processes that a shell spawns) inherit stdin from the parent shell. 通常,子shell(以及shell产生的其他进程)从父shell继承stdin。 If that's the terminal, your test will work as you expect; 如果那是终端,您的测试将按预期工作; if it's a pipe then it will detect that it's a pipe and go ahead and consume it. 如果是管道,它将检测到这是管道,然后继续使用它。 There's no way for the subshell to tell whether it got that pipe by having it explicitly assigned (as in echo "the quick brown fox" | ./one.sh ) or via inheritance. 子外壳无法通过显式分配(例如echo "the quick brown fox" | ./one.sh )或通过继承来告知是否获得了该管道。

As far as I can see, the only way to avoid this problem is to explicitly redirect one.sh 's input to something other than a pipe to avoid it inheriting the parent shell's stdin (which might be a pipe). 据我所知,避免此问题的唯一方法是将one.sh的输入显式重定向到管道以外的其他东西,以避免它继承父外壳程序的stdin(可能是管道)。 Something like: 就像是:

echo "one_nonpipe) $(./one.sh </dev/null)"                            

echo "one_piped) $(echo "the quick brown fox" | ./one.sh)"

... but what'd be even better would be to add a flag to tell one.sh whether to read from stdin or not, rather than trying to figure it out from the type of file attached to stdin: ...但是最好是添加一个标志来告诉one.sh是否从stdin读取,而不是尝试从附加到stdin的文件类型中找出来:

#! /bin/bash
# Usage: one.sh [-i]
#   -i    Read from stdin

set -euo pipefail

if [ "$1" = "-i" ]; then
  cat -
else
  echo "one little two little three little buses"
fi | awk '{ $1 = "111"; print $0 }'

...

echo "one_result) $(./one.sh)"                            

echo "one_piped) $(echo "the quick brown fox" | ./one.sh -i)"

(Note that I also removed the unnecessary parentheses around the if block -- they created another level of subshell for no good reason.) (请注意,我还删除了if块周围的不必要的括号-它们没有充分的理由就创建了另一级子shell。)

By default, every process inherits its standard input (and output and error) from its parent. 默认情况下,每个进程都从其父级继承其标准输入(以及输出和错误)。 Input redirection and pipes are two ways to change standard input to a different file descriptor when starting the child process. 输入重定向和管道是启动子进程时将标准输入更改为不同文件描述符的两种方法。

It is the responsibility of main.sh , if needs to read from its standard input, to know that one.sh also reads from standard input, and it will need to prevent one.sh from consuming it. 如果需要从其标准输入中读取内容,这是main.sh的责任,要知道one.sh 从标准输入中进行读取,这将需要防止one.sh进行使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM