简体   繁体   English

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

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

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 (aka . )实用程序,它将从文件中获取内容并在当前shell中执行它们。

Now, I'm transforming some text into shell commands, and then running them, as follows: 现在,我正在将一些文本转换为shell命令,然后运行它们,如下所示:

$ ls | sed ... | sh

ls is just a random example, the original text can be anything. ls只是一个随机的例子,原始文本可以是任何东西。 sed too, just an example for transforming text. sed ,只是转换文本的一个例子。 The interesting bit is sh . 有趣的是sh I pipe whatever I got to sh and it runs it. 我管不管我到sh并运行它。

My problem is, that means starting a new sub shell. 我的问题是,这意味着启动一个新的子shell。 I'd rather have the commands run within my current shell. 我宁愿让命令在我当前的shell中运行。 Like I would be able to do with source some-file , if I had the commands in a text file. 就像我可以用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/stdin thing 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 $1 $2.doc/i' | source /dev/stdin

Which ensures all .doc files have their extension lowercased. 这确保所有.doc文件的扩展名都是小写的。

And which incidentally, can be handled with xargs , but that's besides the point. 顺便xargs ,可以使用xargs处理,但除此之外。

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

So, when I run the former, it'll exit right away, nothing happens. 所以,当我运行前者时,它会马上退出,没有任何反应。

The eval command exists for this very purpose. 为此目的存在eval命令。

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

More from the bash manual : 更多来自bash手册

eval 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. 参数连接在一起形成一个命令,然后读取并执行该命令,并将其退出状态作为eval的退出状态返回。 If there are no arguments or only empty arguments, the return status is zero. 如果没有参数或只有空参数,则返回状态为零。

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

UPDATE: This works in bash 4.0, as well as tcsh, and dash (if you change source to . ). 更新:这适用于bash 4.0,以及tcsh和dash(如果您将source更改为. )。 Apparently this was buggy in bash 3.2. 显然这是bash 3.2中的错误。 From the bash 4.0 release notes : 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. 无法从非常规文件(如设备或命名管道)读取和执行命令。

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/stdin answer, but I think I found a better one. 无论如何 - 我不喜欢source /dev/stdin答案,但我认为我找到了更好的答案。 It's deceptively simple actually: 实际上看起来很简单:

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 1 option means you use (at most) 1 line per command execution. -L 1选项意味着每个命令执行使用(最多)1行。 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. 因此,请确保每一行以非空格结束。

Finally, you can do 最后,你可以做到

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

to see what commands are executed (-t is verbose). 查看执行的命令(-t是详细的)。

Hope someone reads this! 希望有人读到这个!

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

source <(echo id)

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 while loop; 也就是说,人们可以进入while循环; the read command command takes one line from its stdin and assigns it to the variable $line . read命令从其stdin获取一行并将其赋值给变量$line $line then becomes the command executed within the loop; $line然后成为循环中执行的命令; and it continues until there are no further lines in its input. 并继续直到输入中没有其他行。

This still won't work with some control structures (like another loop), but it fits the bill in this case. 这仍然不适用于某些控制结构(如另一个循环),但在这种情况下它符合要求。

`ls | sed ...`

I sort of feel like ls | sed ... | source - 我有点像ls | sed ... | source - ls | sed ... | source - ls | sed ... | source - would be prettier, but unfortunately source doesn't understand - to mean stdin . ls | sed ... | source -会更漂亮,但遗憾的是source不明白-意思是stdin

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节

为什么不使用source呢?

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

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

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

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

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