扩展bash函数以添加输入重定向和/或管道

时间:2016-02-11 17:29:53

标签: bash function pipe expansion

我正在尝试设置一个bash函数,该函数使用一些前缀来注释带有命令的命令的输出。目前,我有一堆代码行如下:

git pull              2>&1 | sed "s/^/   [git pull] /"
clean_cmake_fortests  2>&1 | sed "s/^/   [cmake] /"
make -j 2             2>&1 | sed "s/^/   [make] /"
docker rmi $(docker images -a -q) 2>&1 | sed "s/^/   [docker rmi] /" | grep "removed" || true

我的目标是用函数替换2>&1 | sed "s/^/$ [$1] /"位,这样我就可以使上面的行看起来像:

git pull              `annotate "git pull"`
clean_cmake_fortests  `annotate "cmake"`
make -j 2             `annotate "make"`
docker rmi $(docker images -a -q) `annotate "docker rmi"` | grep "removed" || true

我将函数annotate定义为

function annotate {
   2>&1 | sed "s/^/    [$1] /"
}

但是在执行时,它没有任何影响,并且命令都只是将其标准输出转储为未修改。我怎样才能实现我的目标?我正在寻找类似于C内联宏扩展的东西。

如果有人好奇,那就是让我生成这样的日志:

04: Getting proto images
    [docker_get_proto_images] Fetching proto docker images...
    ...
    [docker_get_proto_images] Status: Image is up to date for api:dev-proto
    [docker_get_proto_images] ... Done.
05: Building local docker containers
    [docker_local_build] [sh] Building local docker images...
    [docker_local_build] [sh] NOTE: Odd behaviour may result if using outdated bases...
    [docker_local_build] [sh] Local docker image build complete.
    ...
    [docker_local_build] [sh] For advanced usage, see $ARE_TOP/deployment/docker/README
    [docker_local_build]
06: Running docker-compose
    [docker-compose] Starting docker_datacachedisk_1
    [docker-compose] Starting docker_djangodisk_1
    ...

而不是:

04: Getting proto images
Fetching proto docker images...
...
Status: Image is up to date for api:dev-proto
... Done.
05: Building local docker containers
[sh] Building local docker images...
[sh] NOTE: Odd behaviour may result if using outdated bases...
[sh] Local docker image build complete.
...
[sh] For advanced usage, see $ARE_TOP/deployment/docker/README

06: Running docker-compose
Starting docker_datacachedisk_1
Starting docker_djangodisk_1
...

一段时间之后难以阅读。

2 个答案:

答案 0 :(得分:2)

重定向与命令相关联,因此您无法将2>&1与其影响的命令分开。不过,您可以定义类似于现在的annotate。 (不要使用sed,因为在不知道正在使用哪个分隔符的情况下在其命令中包含变量非常困难。)

annotate ()
while IFS= read -r line; do
       printf '    [%s] %s\n' "$1" "$line"
done

(是的,缺少大括号是有意的,虽然没有必要。函数的主体可以是任何复合命令,而不仅仅是大括号组。)

然后将其称为

git pull &> >( annotate "git pull" )

你可以使用一个简单的管道git pull |& annotate "git pull",但是在子shell中运行命令,这可能是不可取的。

答案 1 :(得分:0)

执行函数中的命令。例如:

annotate () {
    "$@" |& sed "s/^/    [$1] /"
}

在这里,像往常一样编写命令,只需在开头添加annotate

$ annotate ls /
    [ls] bin
    [ls] boot
    [ls] dev
    [ls] etc
    [ls] home
    ...

Awk可能是一个更好的选择。如果您的IFS以空格开头(默认情况下是这样),您可以使用$*获取整个命令:

annotate () {
    "$@" |& awk -v tag="$*" '{printf "\t[%s] | %s\n", tag, $0}'
}

再次:

$ annotate ls /
    [ls /] | bin
    [ls /] | boot
    [ls /] | dev
    [ls /] | etc
    [ls /] | home

或者,您可以将第一个参数保留为标记,以便可以传递任意标记:

annotate () {
    "${@:2}" |& awk -v tag="$1" '{printf "\t[%s] | %s\n", tag, $0}'
}
$ annotate "foo bar" ls /
    [foo bar] | bin
    [foo bar] | boot
    [foo bar] | dev
    [foo bar] | etc
    [foo bar] | home
    [foo bar] | lib