パイプの一部が完全に「通過」するように指定する方法はありますか?

パイプの一部が完全に「通過」するように指定する方法はありますか?

かなり大きなパイプを介してデータをストリーミングして処理するスクリプトがあります。パイプラインのさまざまな部分は、実際にはいくつかの外部パラメータに基づいてさまざまなタスクを実行する「スイッチボード」機能です。以下に人為的な例を示します。

#! /bin/bash

switchboard() {
    # Select the appropriate command depending on input.
    case "$1" in
        1)
            sort
            ;;
        2)
            awk '{ print $5 }' | sort
            ;;
        *)
            cat  # <= Is there something more optimal here?
            ;;
    esac
}

# The data processing pipeline.
<"$1" tr '[:upper:]' '[:lower:]' | switchboard "$2" | head -n 10

「スイッチボード」機能では、フォールバックは単にcat入力を出力に直接送信するために使用されます。これはうまく機能しますが、私のパイプラインには多くの「スイッチボード」がある可能性があるので、可能であればcat何もしないプロセスの束を作成しないでください。

サブプロセスを使用せずにパイプの特定の部分がSTDOUTをSTDINに直接接続するように指定するために使用できるbash組み込み(または代替)がありますか? (試してみましたが、データだけをつかんで:食べますね。) それとも、catリソースが少なすぎて問題にならないのですか?

答え1

まず、cat他のものを使っても大きな違いがないので気にする必要はありません。

第二に、パイプラインを構成するコマンドは、外部コマンドでも組み込みコマンドでも別々のプロセスで実行されます。

$ a=0
$ a=1 | a=2 | a=3
$ echo $a
0

nop正確な質問に関しては、単に「stdin」を「stdout」に接続することは不可能です。シェルがパイプで使用されているときに競合が発生する組み込み関数がある場合(例:| nop |-> |)、シェルには方法はありません。パイプを設定するとき、「スイッチボード」nopが。awksort

パイプラインを直接構築してからevalを呼び出して実行することで、「スイッチボード」と同じ効果を得ることもできます。例:

$ cat test.sh
type=`file -zi "$1"`
case $type in
*application/gzip*)     mycat='zcat "$1"';;
*)                      mycat='cat "$1"';;
esac
case $type in
*charset=utf-16le*)     mycat="$mycat | iconv -f utf16le";;
esac
# highlight comments in blue
esc=`printf '\033'`;
mycat="$mycat | sed 's/^#.*/$esc[34m&$esc[m/'"
echo >&2 "$mycat"    # show the built pipeline
eval "$mycat"   # ... and run it
$ iconv -t utf16 test.sh > test16.sh; gzip test16.sh
$ sh test.sh test16.sh.gz

これは少し外れたトピックですが、Linuxではstdinをstdoutにコピーするより高速な方法があります(どちらかがパイプの場合)。これはsplice(2)、ユーザー領域の内外にデータを移動することに関連しないシステムコールです。

$ cat splice_cat.c
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdlib.h>
#include <err.h>

int main(int ac, char **av){
    ssize_t r;
    size_t block = ac > 1 ? strtoul(av[1], 0, 0) : 0x20000;
    for(;;)
            if((r = splice(0, NULL, 1, NULL, block, 0)) < 1){
                    if(r < 0) err(1, "splice");
                    return 0;
            }
}
$ cc -Wall splice_cat.c -o splice_cat
$ dd if=/dev/zero bs=1M count=100 status=none | (time cat >/dev/null)
real    0m0.153s
user    0m0.012s
sys     0m0.056s
$ dd if=/dev/zero bs=1M count=100 status=none | (time ./splice_cat >/dev/null)
real    0m0.100s
user    0m0.004s
sys     0m0.020s

しかし(私が知る限り)シェルやcatddなどではそれを使用しません。

関連情報