bash:从管道分配变量?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42963395/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 15:55:51  来源:igfitidea点击:

bash: Assign variable from pipe?

bash

提问by agc

In bash, what's the most efficient way to assign a variable using piped input -- using only left to right syntax? Suppose the left side of the pipe is seq 3, so we'd want:

在 中bash,使用管道输入分配变量的最有效方法是什么——仅使用从左到右的语法?假设管道的左侧是seq 3,所以我们想要:

seq 3 | x=<put some code here>


NB: Not an answer, although probably functionally equivalent:

注意:不是答案,虽然可能在功能上是等效的:

x=`seq 3`

...because seq 3is not on the leftside of a pipe.

...因为seq 3不在管道的左侧

For this Q, please ignore the possibility of exceeding the variable's memory, which pipes could certainly do.

对于这个问题,请忽略超出变量内存的可能性,管道肯定可以做到。

回答by Charles Duffy

This is covered in detail in BashFAQ #24.

这在BashFAQ #24中有详细介绍。

You can reliablyuse a variable collected on the right-hand side of a pipeline only if the code referencing it is alsoon the right-hand side of that pipeline.

只有当引用它的代码也在该管道的右侧时,您才能可靠地使用在管道右侧收集的变量。

#!/bin/bash
echo "hello" | { read -r var; echo "Read value: $var"; }
echo "After the pipeline exited, var contains: $var"

Typical output is:

典型的输出是:

Read value: hello
After the pipeline exited, var contains:

The POSIX sh specification neither requires nor precludes the right-hand side of a pipeline being executed in the same shell which later executes subsequent commands. Thus, a shell mayexecute the readon the second line in the same shell where it executes the echoon the third -- but bash, specifically, will notdo so unless the lastpipeshell option is enabled.

POSIX sh 规范既不要求也不排除在同一 shell 中执行的管道的右侧,该 shell 稍后执行后续命令。因此,外壳可以执行read在它执行相同的外壳在第二行echo第三-但bash中,具体地讲,不会,除非这样做lastpipe外壳选项启用。

回答by mklement0

To complement Charles Duffy's helpful answerwith a focus on making it workin bash:

为了补充Charles Duffy 的有用答案,重点是使其bash以下方面发挥作用:

By default, and on Bash v4.1- invariably, any variable creations / modifications in a (multi-segment) pipeline happen in a subshell, so that the result will not be visible to the calling shell.

默认情况下,并猛砸v4.1-无一例外,任何变量创作/在(多段)管道修改发生在一个子shell,这样的结果将是不可见的调用shell。

In Bash v4.2+, you can set option lastpipeto make the lastpipeline segment run in the currentshell, so that variable creations/modifications made there arevisible to it.

Bash v4.2+ 中,您可以设置选项lastpipe使最后一个管道段在当前shell 中运行,以便在那里进行的变量创建/修改对其可见。

For that to work in an interactiveshell, you must additionally turn off job control with set +m.

为了在交互式shell 中工作,您必须另外关闭作业控制set +m

Here's a complete example (Bash v4.2+):

这是一个完整的示例(Bash v4.2+):

$ unset x; shopt -s lastpipe; set +m; seq 3 | x=$(cat); echo "$x"
1
2
3


That said,

那说,

x=$(seq 3)

(the modern equivalent of your x=`seq 3`) is much simpler - it is POSIX-compliant and therefore works on older Bash versions too, and it requires no fiddling with global options.

(你的现代等价物x=`seq 3`)要简单得多 - 它符合 POSIX,因此也适用于较旧的 Bash 版本,并且不需要摆弄全局选项。

回答by Isaac

piped input

管道输入

The simplest method to set a variable is to readit:

设置变量的最简单方法是read

seq 3 | read -d '' x

Will also work in zshbut not in ksh(no NULallowed for the -doption in ksh).

也可以在zsh但不能在ksh(ksh 中NUL-d选项不允许)。

One way to use the value of xis to do so in the subshell created by the pipe:

使用值的一种方法x是在管道创建的子外壳中这样做:

$ seq 3 | { read -d '' x; echo "$x"; }
1
2
3

Note that the exit condition of read is of failure (because no '' character was found). More details could be found in Bash FAQ 24

注意read的退出条件是failure(因为没有找到''字符)。更多细节可以在Bash FAQ 24 中找到

In ksh and zsh the value could be used after the pipe has ended (using a char that is not in the input (but not NUL) to make ksh also work):

在 ksh 和 zsh 中,可以在管道结束后使用该值(使用不在输入中的字符(但不是 NUL)使 ksh 也可以工作):

$ seq 3 | read -d ':' x
$ echo "$x"
1
2
3

That's because, originally in ksh (later in zsh) the last command on a pipe would run in the same process as the parent shell, keeping the value of the variable changed after the pipe has ended.

这是因为,最初在 ksh(后来在 zsh)中,管道上的最后一个命令将在与父 shell 相同的进程中运行,在管道结束后保持变量的值发生变化。

One way to emulate this capacity in bash is to use lastpipe(which will only work if JOB CONTROLis disabled (inside a non-interactive script or with set +m)):

在 bash 中模拟这种能力的一种方法是使用lastpipe(只有在禁用JOB CONTROL时才有效(在非交互式脚本中或使用set +m)):

$ set +m; shopt -s lastpipe
$ seq 3 | read -d '' x
$ echo "$x"
1
2
3

Capture command output

捕获命令输出

Using Process Substitution:

使用进程替换

$ read -d '' x < <(seq 3)
$ echo "$x"
1
2
3

Or, using the old here-doc:

或者,使用旧的 here-doc:

$ read -d '' x <<-Hello
> $(seq 3)
> Hello
$ echo "$x"
1
2
3

Or, using printf:

或者,使用 printf:

$ printf -v x "$(seq 3)"
$ echo "$x"
1
2
3

回答by Gordon Davisson

You can sort-of do this, if you're willing to be flexible about what you mean by "piped input" and "left to right syntax". Try this:

如果您愿意灵活地理解“管道输入”和“从左到右语法”的含义,您可以这样做。尝试这个:

< <(seq 3) read -r -d '' var

I do not recommend this. At all. Just use var=$(seq 3).

我不推荐这个。在所有。只需使用var=$(seq 3).

回答by agc

One (revised) method, maybe not the best though, and may fail the OP criteria depending how we look at it:

一种(修订后的)方法,虽然可能不是最好的,并且可能无法满足 OP 标准,具体取决于我们如何看待它:

# load $x, echo it quoted, then unquoted.
seq 3 | { x=$(</dev/stdin) ; echo "$x" ; echo $x ; } 

Output:

输出:

1
2
3
1 2 3

This catvariant works in POSIX shells (yash, dash):

cat变体适用于 POSIX shell ( yash, dash):

seq 3 | { n=$(cat /dev/stdin) ; echo "$n" ; echo $n ; }

回答by Sohail Si

You can use a temporary file:

您可以使用临时文件:

seq 3 >/var/tmp/agc-bashvar1; x=$(cat /var/tmp/agc-bashvar1 ); echo x is $x

seq 3 >/var/tmp/agc-bashvar1; x=$(cat /var/tmp/agc-bashvar1 ); echo x is $x

The following cleans up after use:

使用后清理如下:

seq 3 >/var/tmp/agc-bashvar1; x=$(cat /var/tmp/agc-bashvar1 ); rm /var/tmp/agc-bashvar1

seq 3 >/var/tmp/agc-bashvar1; x=$(cat /var/tmp/agc-bashvar1 ); rm /var/tmp/agc-bashvar1

Then echo x is $x

然后 echo x is $x

Rationale: Based on @charles-duffy 's answer, you cannot survive a variable based on content received directly at the right hand side of a pipe. So why not use an external place to store the content it as a side effect (as opposed to a no-side-effect pipeline).

基本原理:根据@charles-duffy 的回答,您无法根据直接在管道右侧接收到的内容来保留变量。那么为什么不使用外部位置将内容存储为副作用(而不是无副作用管道)。

Make sure the temporary filename is not used by other programs.

确保其他程序未使用临时文件名。