向前然后向后输出每一行的命令

向前然后向后输出每一行的命令

我正在寻找一个“元命令”xyz,这样:

(echo "foo"; echo "bar") | xyz rev

将返回:

foo oof
bar rab

我想避免临时文件,即我正在寻找比以下更简洁的解决方案:

tempfile=$(mktemp)
cat > $tempfile
cat $tempfile | rev | paste $tempfile -

(当然,我想要一个通用的解决方案,对于任何命令,而不仅仅是rev,您可以假设该命令为每个输入行输出一行。)

Zsh 解决方案也是可以接受的。

答案1

由于 stdio 缓冲的工作方式,大多数情况下都会出现很多问题。 Linux 的解决方法可能是使用该stdbuf程序并通过 coproc 运行命令,这样您就可以显式控制输出的交错。

以下假设该命令将在每行输入后输出一行。

#!/bin/bash
coproc stdbuf -i0 -o0 "$@"
IFS=
while read -r in ; do
    printf "%s " "$in"
    printf "%s\n" "$in" >&${COPROC[1]}
    read -r out <&${COPROC[0]}
    printf "%s\n" "$out"
done

如果需要一个更通用的解决方案,因为OP只需要程序的每一行输入最终输出一行而不是立即输出,那么需要一种更复杂的方法。创建一个事件循环,用于read -t 0尝试从标准输入和协进程中读取数据,如果两者具有相同的“行号”,则输出,否则存储该行。为了避免使用 100% 的 cpu,如果在事件循环的任何一轮中都没有准备好,则在再次运行事件循环之前引入一个小的延迟。如果进程输出部分行,则会出现额外的复杂情况,需要缓冲这些行。

如果需要这个更通用的解决方案,我会使用预计因为它已经对处理多个输入流上的模式匹配提供了良好的支持。然而,这不是 bash/zsh 解决方案。

答案2

一个外壳函数。这适用于任何支持<<<此处字符串的 shell,包括 zsh 和 bash。

xyz() { 
    while read line
    do 
        printf "%s " "$line"
        "$@" <<<"$line"
    done
}

$ (echo "foo"; echo "bar") | xyz rev
foo oof
bar rab

答案3

在这种特殊情况下,我会使用perl

printf '%s\n' foo bar | perl -Mopen=locale -lpe '$_ .= " " . reverse$_'
foo oof
bar rab

您可以扩展它来使用字素簇:

$ printf '%s\n' $'complique\u301' |
    perl -Mopen=locale -lpe '$_ .= " " . join "", reverse/\X/g'
compliqué éuqilpmoc

或者zsh

while IFS= read -r line; do
  print -r -- $line ${(j::)${(s::Oa)line}}
done

尽管我会避免使用while read循环来处理文本,即使在zsh(即使在这种特殊情况下,它也没有那么糟糕,因为只使用了内置命令)。

在一般情况下,使用临时文件可能是最好的方法。使用zsh,您可以通过以下方式执行此操作:

(){paste -d ' ' $1 <(rev <$1)} =(print -l foo bar)

(其中=(...)负责创建和清理临时文件)。

在一般情况下,用管道和某种形式的teeing 替换它会导致死锁。有关死锁情况的一些方法和详细信息,请参阅这些类似的问题:

答案4

命名管道来救援!

xyz() {
  pipe="/tmp/$$pipe"
  mkfifo "$pipe"
  tee "$pipe" | "$@" | paste "$pipe" -
  rm "$pipe"
}

示例命令的结果:

$ (echo "foo"; echo "bar") | xyz rev
foo oof
bar rab

上面的函数完全按照您的描述进行操作。

相关内容