繁体   English   中英

将第一个命令的输出作为输入传递给第二个命令,以便第一个命令在当前shell中运行

[英]Pass output from first command as input to second command such that the first command runs in current shell

以下代码描述了我的问题。

# File: foo.sh

# We have a work function that may define variables and produce some
# complex output.
work()
{
    a=foo
    ps -ef
}

# This function uses the work function.
f()
{
    # We want to filter the complex output produced by work function but
    # at the same time we want that any variables defined by work
    # function is available in this function.
    work | head -n 2

    # Since pipeline launches subshells, the variable defined by work
    # function is no longer available in this function.
    echo a: $a
}

f

这是输出。

$ sh foo.sh
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 Jul15 ?        00:00:03 /sbin/init
a:

由于管道创建了一个子外壳,这意味着当我们返回f()时, work()中定义的变量会丢失,因此我以这种方式解决了该问题。

# File: bar.sh 

# We have a work function that may define variables and produce some
# complex output.
work()
{
    a=foo
    ps -ef
}

# This function uses the work function.
f()
{
    # We want to filter the complex output produced by work function but
    # at the same time we want that any variables defined by work
    # function is available in this function.
    work > out.txt
    head -n 2 out.txt
    echo a: $a
}

f

这是输出。

$ sh bar.sh 
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 Jul15 ?        00:00:03 /sbin/init
a: foo

尽管第二个脚本对于此玩具示例而言效果很好,但是当f()最终以递归方式调用自身时,可能会导致问题(尽管可解决问题),这将导致out.txt在被调用时被-f()覆盖。调用者f()仍将依赖它。 当然,也可以通过采取有关的使用充分注意要解决out.txt ,即使用out.txt仅F()之前调用自身,并避免使用out.txt通话下方。

这使我想知道是否有任何方法可以在不依赖于创建临时文件的情况下解决此问题。 有没有一种方法可以将work的输出传递到另一个命令,而不必求助于文件I / O,这样work可以在当前Shell中执行? 我正在寻找可以在任何POSIX shell上运行的解决方案。

一个kudge(在yash测试),将变量输出到文件描述符3并绕过head ,然后将其复制回STDIN

{ { work ; echo a: $a 1>&3 ; } | head -n 2 ; } 3>&1

输出:

UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 Jul15 ?        00:00:41 /sbin/init splash
a: foo

注意花括号的两个级别:

  1. 在最后之前,将文件描述符3移回STDIN,head看不到它。
  2. 为了使workecho保持在同一外壳中。

与上面相同的代码,在函数f

f() { { { work ; echo a: $a 1>&3 ; } | head -n 2 ; } 3>&1 }

我已经实现了一个函数pseudopipe管道,用于以请求的方式执行管道(即管道的第一个命令在当前shell中执行)。 但是请注意,该实现会创建临时文件,因此不符合避免文件I / O的要求。

用法:

以下任何形式和其他变体都可以使用:

pseudopipe "cmd1|cmd2"
pseudopipe 'cmd1|cmd2'
pseudopipe cmd1\|cmd2

当前实施的局限性:

  • 仅正确处理由两个命令组成的管道

  • 命令中不得包含带引号或转义的' | 字符。 例如,以下操作将无法正常工作:

     pseudopipe "work|grep '|'" 
  • 您的脚本不得使用保留变量PSEUDOPIPE_TMPDIRPSEUDOPIPE_CMD1PSEUDOPIPE_CMD2

实现和示例:

#!/bin/sh

pseudopipe()
{
    PSEUDOPIPE_TMPDIR=$(mktemp -d)
    PSEUDOPIPE_CMD1=$(echo "$@"|sed 's/|/\n/g'|head -n 1)
    PSEUDOPIPE_CMD2=$(echo "$@"|sed 's/|/\n/g'|tail -n 1)
    mkfifo "$PSEUDOPIPE_TMPDIR"/fifo
    eval "$PSEUDOPIPE_CMD2 < $PSEUDOPIPE_TMPDIR/fifo &" \
         "$PSEUDOPIPE_CMD1 > $PSEUDOPIPE_TMPDIR/fifo ;" \
         "rm -r $PSEUDOPIPE_TMPDIR"
    wait $!
}

work()
{
    a=foo
    ps -ef
}

f()
{
    a=bar
    echo "before pseudopipe: a: $a"
    pseudopipe "work|head -n 2"
    echo "after pseudopipe:  a: $a"
}

f

输出:

$ sh pseudopipe.sh 
before pseudopipe: a: bar
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 Jul22 ?        00:00:06 /sbin/init splash
after pseudopipe:  a: foo

问题是您希望从子外壳中获取多个信息。 一种解决方案是将所需的所有内容从子shell放入标准输出管道,然后全部返回父shell端:

work()
{
    a=foo
    echo $a
    ps -ef
}

然后,您需要在父外壳程序端进行一些解析工作。 在这里,我将分隔符设置为换行符,并将返回字符串转换为每个数组索引处只有一行的数组:

f()
{
    output=$(work | head -n 3)
    IFS=$'\n'
    lines=($output)
    a=${lines[0]}
    echo ${lines[1]}
    echo ${lines[2]}
    echo a=$a
}

f

另一个解决方案是使用命名管道,并将您感兴趣的每个变量分配到其自己的命名管道中。 在子外壳程序端,您可以创建和编写管道:

mkfifo /tmp/variable_a
echo $a > /tmp/variable_a

在父外壳程序端,您从相同的命名管道读取:

read a < /tmp/variable_a
rm -f /tmp/variable_a

暂无
暂无

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

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