如何使用管道组成bash函数?


18

我用这种方式定义的功能很少:

function f {
  read and process $1
  ...
  echo $result
}

我想将它们组合在一起,以便调用看起来像f | g | h

我应该使用哪种惯用法将用于参数的函数转换为从stdin读取参数的函数?是否可以从流中读取成对的参数元组而无需将其转义(例如,空终止)?


您想要类似的东西,h(g(f(...)))或者每个功能都从标准输入(read x; ...)读取并写入标准输出(echo ...)。
vonbrand 2014年

Answers:


21

一种可能的方法是while...read在您的函数中放置一个构造,该构造将处理通过STDIN进入该函数的任何数据,对其进行操作,然后通过STDOUT发出结果数据。

function X {
  while read data; do
    ...process...
  done
}

while ..read..由于组件将高度依赖于它们能够可靠使用的数据类型,因此需要特别注意如何配置组件。您可能会想到一个最佳配置。

$ logF() { while read data; do echo "[F:$(date +"%D %T")] $data"; done; }
$ logG() { while read data; do echo "G:$data";                    done; }
$ logH() { while read data; do echo "H:$data";                    done; }

这是每个功能本身。

$ echo "hi" | logF
[F:02/07/14 20:01:11] hi

$ echo "hi" | logG
G:hi

$ echo "hi" | logH
H:hi

当我们一起使用它们时,它们就在这里。

$ echo "hi" | logF | logG | logH
H:G:[F:02/07/14 19:58:18] hi

$ echo -e "hi\nbye" | logF | logG | logH
H:G:[F:02/07/14 19:58:22] hi
H:G:[F:02/07/14 19:58:22] bye

他们可以接受各种样式的输入。

#-- ex. #1
$ cat <<<"some string of nonsense" | logF | logG | logH
H:G:[F:02/07/14 20:03:47] some string of nonsense

#-- ex. #2    
$ (logF | logG | logH) <<<"Here comes another string."
H:G:[F:02/07/14 20:04:46] Here comes another string.

#-- ex. #3
$ (logF | logG | logH)
Look I can even
H:G:[F:02/07/14 20:05:19] Look I can even
type to it
H:G:[F:02/07/14 20:05:23] type to it
live
H:G:[F:02/07/14 20:05:25] live
via STDIN
H:G:[F:02/07/14 20:05:29] via STDIN
..type Ctrl + D to stop..

#-- ex. #4
$ seq 5 | logF | logG | logH
H:G:[F:02/07/14 20:07:40] 1
H:G:[F:02/07/14 20:07:40] 2
H:G:[F:02/07/14 20:07:40] 3
H:G:[F:02/07/14 20:07:40] 4
H:G:[F:02/07/14 20:07:40] 5

#-- ex. #5
$ (logF | logG | logH) < <(seq 2)
H:G:[F:02/07/14 20:15:17] 1
H:G:[F:02/07/14 20:15:17] 2

4

作为slm答案的附录,我做了一些尝试,将以null分隔的元组作为函数参数:

$ sayTuple() { 
    IFS= read -r -d $'\0' d1
    IFS= read -r -d $'\0' d2
    echo "sayTuple: -$d1- -$d2-"
}

注意:sayTuple两次读取以空值结尾的记录,该记录-d $'\0'处理input周围的任何空间IFS=echo后面的记录被包围-

结果表明它正确地处理包含空终止输入\n\t

$ printf "%s\0%s\0" "Hello " $' Brave\n\tWorld' | sayTuple 
sayTuple: -Hello - - Brave
        World-

请在注释中添加改进建议,这是一个有趣的话题。


+1喜欢您的想法。我们可以在其中放一个循环,让它接受任意#个args。sayTuple() { arr=() ; while IFS= read -r -d $'\0' arg; do arr+="$arg"; done; echo "sayTuple: ${arr[@]}"; }
slm

似乎您应该可以进行操作,IFS= read -r -d $'\0' -a arg但我无法执行此操作。这将允许删除while,这似乎不必要,但这是我可以使用的唯一模式。
slm
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.