bash 如何在当前 shell 中执行命令的输出?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1279953/
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-09 18:24:52  来源:igfitidea点击:

How to execute the output of a command within the current shell?

bashshellunixpipe

提问by kch

I'm well aware of the source(aka .) utility, which will take the contents from a file and execute them within the current shell.

我很清楚source(又名.)实用程序,它将从文件中获取内容并在当前 shell 中执行它们。

Now, I'm transforming some text into shell commands, and then running them, as follows:

现在,我将一些文本转换为 shell 命令,然后运行它们,如下所示:

$ ls | sed ... | sh

lsis just a random example, the original text can be anything. sedtoo, just an example for transforming text. The interesting bit is sh. I pipe whatever I got to shand it runs it.

ls只是一个随机的例子,原文可以是任何东西。sed同样,只是一个转换文本的例子。有趣的一点是sh。我用管道输送任何东西sh,它运行它。

My problem is, that means starting a new sub shell. I'd rather have the commands run within my current shell. Like I would be able to do with source some-file, if I had the commands in a text file.

我的问题是,这意味着启动一个新的子 shell。我宁愿让命令在我当前的 shell 中运行。就像我可以用source some-file,如果我在文本文件中有命令一样。

I don't want to create a temp file because feels dirty.

我不想创建临时文件,因为感觉很脏。

Alternatively, I'd like to start my sub shell with the exact same characteristics as my current shell.

或者,我想用与我当前的 shell 完全相同的特性来启动我的子 shell。

update

更新

Ok, the solutions using backtick certainly work, but I often need to do this while I'm checking and changing the output, so I'd much prefer if there was a way to pipe the result into something in the end.

好的,使用反引号的解决方案肯定有效,但我经常需要在检查和更改输出时执行此操作,因此我更喜欢是否有办法将结果通过管道传输到最后。

sad update

悲伤的更新

Ah, the /dev/stdinthing looked so pretty, but, in a more complex case, it didn't work.

啊,这/dev/stdin东西看起来很漂亮,但是,在更复杂的情况下,它不起作用。

So, I have this:

所以,我有这个:

find . -type f -iname '*.doc' | ack -v '\.doc$' | perl -pe 's/^((.*)\.doc)$/git mv -f  .doc/i' | source /dev/stdin

Which ensures all .docfiles have their extension lowercased.

这确保所有.doc文件的扩展名都是小写的。

And which incidentally, can be handled with xargs, but that's besides the point.

顺便说一句,可以用 处理xargs,但这不是重点。

find . -type f -iname '*.doc' | ack -v '\.doc$' | perl -pe 's/^((.*)\.doc)$/ .doc/i' | xargs -L1 git mv

So, when I run the former, it'll exit right away, nothing happens.

所以,当我运行前者时,它会立即退出,什么也没有发生。

采纳答案by mark4o

$ ls | sed ... | source /dev/stdin

UPDATE:This works in bash 4.0, as well as tcsh, and dash (if you change sourceto .). Apparently this was buggy in bash 3.2. From the bash 4.0 release notes:

更新:这适用于 bash 4.0,以及 tcsh 和 dash(如果您更改source.)。显然这是 bash 3.2 中的错误。来自bash 4.0 发行说明

Fixed a bug that caused `.' to fail to read and execute commands from non-regular files such as devices or named pipes.

修复了导致“.”的错误 无法从非常规文件(例如设备或命名管道)读取和执行命令。

回答by Juliano

The evalcommand exists for this very purpose.

eval命令正是为此目的而存在的。

eval "$( ls | sed... )"

More from the bash manual:

更多来自bash 手册

eval

          eval [arguments]

The arguments are concatenated together into a single command, which is then read and executed, and its exit status returned as the exit status of eval. If there are no arguments or only empty arguments, the return status is zero.

评估

          eval [arguments]

参数连接在一起形成一个命令,然后读取并执行该命令,其退出状态作为 eval 的退出状态返回。如果没有参数或只有空参数,则返回状态为零。

回答by rabensky

Wow, I know this is an old question, but I've found myself with the same exact problem recently (that's how I got here).

哇,我知道这是一个老问题,但我最近发现自己遇到了同样的问题(这就是我来到这里的方式)。

Anyway - I don't like the source /dev/stdinanswer, but I think I found a better one. It's deceptively simple actually:

无论如何 - 我不喜欢这个source /dev/stdin答案,但我想我找到了一个更好的答案。其实很简单:

echo ls -la | xargs xargs

Nice, right? Actually, this still doesn't do what you want, because if you have multiple lines it will concat them into a single command instead of running each command separately. So the solution I found is:

不错,对吧?实际上,这仍然不能满足您的要求,因为如果您有多行,它会将它们合并为一个命令,而不是单独运行每个命令。所以我找到的解决方案是:

ls | ... | xargs -L 1 xargs

the -L 1option means you use (at most) 1 line per command execution. Note:if your line ends with a trailing space, it will be concatenated with the next line! So make sure each line ends with a non-space.

-L 1选项意味着您每次执行命令最多使用 1 行。注意:如果您的行以尾随空格结尾,它将与下一行连接!因此,请确保每行都以非空格结尾。

Finally, you can do

最后,你可以做

ls | ... | xargs -L 1 xargs -t

to see what commands are executed (-t is verbose).

查看执行了哪些命令(-t 是冗长的)。

Hope someone reads this!

希望有人读到这里!

回答by rishta

Try using process substitution, which replaces output of a command with a temporary file which can then be sourced:

尝试使用进程替换,它将命令的输出替换为一个临时文件,然后可以获取该文件:

source <(echo id)

回答by Geoff Nixon

I believe this is "the right answer" to the question:

我相信这是这个问题的“正确答案”:

ls | sed ... | while read line; do $line; done

That is, one can pipe into a whileloop; the readcommand command takes one line from its stdinand assigns it to the variable $line. $linethen becomes the command executed within the loop; and it continues until there are no further lines in its input.

也就是说,可以通过管道进入while循环;该read命令命令占一行从其stdin并给它分配给变量$line$line然后成为循环内执行的命令;它一直持续到输入中没有其他行为止。

This still won't work with some control structures (like another loop), but it fits the bill in this case.

这仍然不适用于某些控制结构(如另一个循环),但在这种情况下它符合要求。

回答by chaos

`ls | sed ...`

I sort of feel like ls | sed ... | source -would be prettier, but unfortunately sourcedoesn't understand -to mean stdin.

我有点觉得ls | sed ... | source -会更漂亮,但不幸的source是不明白-是什么意思stdin

回答by Eric Wendelin

I think your solution is command substitution with backticks: http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html

我认为您的解决方案是带反引号的命令替换:http: //tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html

See section 3.4.5

见第 3.4.5 节

回答by ish-west

To use the mark4o's solution on bash 3.2 (macos) a here string can be used instead of pipelines like in this example:

要在 bash 3.2 (macos) 上使用 mark4o 的解决方案,可以使用 here 字符串代替此示例中的管道:

. /dev/stdin <<< "$(grep '^alias' ~/.profile)"

回答by Kaleb Pederson

Why not use sourcethen?

那为什么不使用source呢?

$ ls | sed ... > out.sh ; source out.sh