为什么用管道将输入“读取"?仅当输入"while read ..."时有效构造? [英] Why piping input to "read" only works when fed into "while read ..." construct?

查看:125
本文介绍了为什么用管道将输入“读取"?仅当输入"while read ..."时有效构造?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在尝试从程序输出中将输入读入环境变量,如下所示:

I've been trying to read input into environment variables from program output like this:

echo first second | read A B ; echo $A-$B 

结果是:

-

A和B始终为空.我读到有关bash在子shell中执行管道命令的信息,基本上可以防止bash用管道传输输入以进行读取.但是,以下内容:

Both A and B are always empty. I read about bash executing piped commands in sub-shell and that basically preventing one from piping input to read. However, the following:

echo first second | while read A B ; do echo $A-$B ; done

似乎可以正常工作,结果是:

Seems to work, the result is:

first-second

有人可以解释一下这里的逻辑吗?

Can someone please explain what is the logic here? Is it that the commands inside the while ... done construct are actually executed in the same shell as echo and not in a sub-shell?

推荐答案

如何针对 stdin 进行循环并获取存储在变量中的结果

的问题(以及其他的问题),当您使用|另一个命令,您将隐式创建一个 fork ,这是当前会话的子级,并且不会影响当前会话的环境的子外壳.

How to do a loop against stdin and get result stored in a variable

Under bash (and other shell also), when you pipe something by using | to another command, you will implicitely create a fork, a subshell who's a child of current session and who can't affect current session's environ.

所以这个:

TOTAL=0
printf "%s %s\n" 9 4 3 1 77 2 25 12 226 664 |
  while read A B;do
      ((TOTAL+=A-B))
      printf "%3d - %3d = %4d -> TOTAL= %4d\n" $A $B $[A-B] $TOTAL
    done
echo final total: $TOTAL

不会给出预期的结果! :

won't give expected result! :

  9 -   4 =    5 -> TOTAL=    5
  3 -   1 =    2 -> TOTAL=    7
 77 -   2 =   75 -> TOTAL=   82
 25 -  12 =   13 -> TOTAL=   95
226 - 664 = -438 -> TOTAL= -343
echo final total: $TOTAL
final total: 0

计算出的 TOTAL 不能在主脚本中重复使用.

Where computed TOTAL could'nt be reused in main script.

通过使用 处理替代此处文档此处字符串,您可以将叉子反向:

By using bash Process Substitution, Here Documents or Here Strings, you could inverse the fork:

read A B <<<"first second"
echo $A
first

echo $B
second

此处文档

while read A B;do
    echo $A-$B
    C=$A-$B
  done << eodoc
first second
third fourth
eodoc
first-second
third-fourth

循环外:

echo : $C
: third-fourth

此处命令

TOTAL=0
while read A B;do
    ((TOTAL+=A-B))
    printf "%3d - %3d = %4d -> TOTAL= %4d\n" $A $B $[A-B] $TOTAL
  done < <(
    printf "%s %s\n" 9 4 3 1 77 2 25 12 226 664
)
  9 -   4 =    5 -> TOTAL=    5
  3 -   1 =    2 -> TOTAL=    7
 77 -   2 =   75 -> TOTAL=   82
 25 -  12 =   13 -> TOTAL=   95
226 - 664 = -438 -> TOTAL= -343

# and finally out of loop:
echo $TOTAL
-343

现在,您可以在主脚本中使用$TOTAL .

Now you could use $TOTAL in your main script.

但是对于仅针对 stdin 的工作,您可以在 fork 中创建一种脚本:

But for working only against stdin, you may create a kind of script into the fork:

printf "%s %s\n" 9 4 3 1 77 2 25 12 226 664 | {
    TOTAL=0
    while read A B;do
        ((TOTAL+=A-B))
        printf "%3d - %3d = %4d -> TOTAL= %4d\n" $A $B $[A-B] $TOTAL
    done
    echo "Out of the loop total:" $TOTAL
  }

会给:

  9 -   4 =    5 -> TOTAL=    5
  3 -   1 =    2 -> TOTAL=    7
 77 -   2 =   75 -> TOTAL=   82
 25 -  12 =   13 -> TOTAL=   95
226 - 664 = -438 -> TOTAL= -343
Out of the loop total: -343

注意:$TOTAL不能在主脚本中使用(在最后一个大括号}之后).

Note: $TOTAL could not be used in main script (after last right curly bracket } ).

正如@CharlesDuffy正确指出的那样,有一个bash选项用于更改此行为.但是为此,我们必须首先禁用 工作控制:

As @CharlesDuffy correctly pointed out, there is a bash option used to change this behaviour. But for this, we have to first disable job control:

shopt -s lastpipe           # Set *lastpipe* option
set +m                      # Disabling job control
TOTAL=0
printf "%s %s\n" 9 4 3 1 77 2 25 12 226 664 |
  while read A B;do
      ((TOTAL+=A-B))
      printf "%3d - %3d = %4d -> TOTAL= %4d\n" $A $B $[A-B] $TOTAL
    done

  9 -   4 =    5 -> TOTAL= -338
  3 -   1 =    2 -> TOTAL= -336
 77 -   2 =   75 -> TOTAL= -261
 25 -  12 =   13 -> TOTAL= -248
226 - 664 = -438 -> TOTAL= -686

echo final total: $TOTAL
-343

这可以工作,但是我(个人)不喜欢这样,因为这不是标准,也无助于使脚本可读.同样,禁用工作控制对于访问这种行为来说似乎也很昂贵.

This will work, but I (personally) don't like this because this is not standard and won't help to make script readable. Also disabling job control seem expensive for accessing this behaviour.

注意:默认情况下,仅在 interactive 会话中启用 Job control .因此,普通脚本中不需要set +m.

Note: Job control is enabled by default only in interactive sessions. So set +m is not required in normal scripts.

因此,如果在控制台中运行或在脚本中运行,则在脚本中被遗忘的set +m会创建不同的行为.这不会使它易于理解或调试...

So forgotten set +m in a script would create different behaviours if run in a console or if run in a script. This will not going to make this easy to understand or to debug...

这篇关于为什么用管道将输入“读取"?仅当输入"while read ..."时有效构造?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆