bash:将标准输入拆分到多个进程,合并输出,确保已知的完成/输出顺序

bash:将标准输入拆分到多个进程,合并输出,确保已知的完成/输出顺序

考虑以下将终端程序输出的列数据转换为 JSON 的函数的简化版本...

旁注:第二个例子可能读起来更快,以理解我的潜在问题。

function filter_proc () {

  local save=$(tempfile)
  local var1=''
  local var2=''
  local var3=''
  local var4=''


  cat > $save

  read var1 var2 var3 var4 < <(
     cat $save | grep "qualifying-line" | awk '{print $3,$4,$5,$7}'  
  );
  calculated=$((var1+var3-1))
  cat <<JSON
  {
     "header": {
       "var1": "$var2",
       "var4": "$var4"
JSON

  echo -n "   },"

  local state="column1";
  local count=0;

  for field in $(
  echo $(

      cat $save | perl -ne 'print unless 1../^MySearchTerm /'| sed -e '/^$/,$d' 
      echo -eod- 
  ) ) ; do

        case $field in 
          -eod-)
             echo -n '"count": '
             echo "$count }'


            break;
           ;;
        *)

            echo -n '"'
            echo -n "$state"
            echo -n '": "'
            echo -n "$field"
            echo -n '",'

            case $state in

               "column1") 
                state="column2"
                count=$((count+1))
                ;;
               "column2") 
                state="this_is_col3"
                ;;
               "this_is_col3") 
                state="the_fourth_column"
                ;;
               "the_fourth_column") 
                state="column1"
                ;;
            esac;

        ;;

        esac;

 done;

 rm $save
}

我试图避免使用临时文件(此示例中为 ($save)。问题是我无法读取 std_in 两次。我尝试使用 tee 来解决这个问题,但看起来使用 tee 的每个子进程都是同时发生的,所以我无法保证处理输出的顺序,正如这个愚蠢的例子所示:

time (echo hello | tee >( sleep 5 ; echo -n "first:" ; cat; ) >(sleep  3 ; echo -n "second:" ; cat;) | ( echo -n "piped:"; cat ;) ; )

其输出为:(等待约 5 秒后 - 如果当前未运行,则可能不是 8 秒)

piped:hello
second:hello
first:hello

real  0m5.030s
user  0m0.010s
sys   0m0.000s

确保(在第二个例子中)获得这个结果的最佳方法是什么?

first:hello
second:hello
piped:hello

real  0m5.030s
user  0m0.010s
sys   0m0.000s

请记住,我很高兴实际得到答案的时间是 8 秒。我只是不想使用临时文件,因为我不想写入磁盘(当然,我可以设置一个 RAM 驱动器,但那完全是另一个问题)

答案1

如果您还为您的 bash 脚本提供了输入和期望/实际输出示例,那就更好了,但我还是会尝试一下。

在第二个示例中,您绝对会获得 piped/second/first,并且您无法获得其他任何内容。second 和 first 的执行是并发的,但是first的休眠时间比 长second,因此它是这两个中最后一个执行的。piped由于它在主管道中,因此会立即执行,然后输出其余部分。tee首先发送一个简单的 hello,然后它获得 sleep 3 second,然后是 sleep 5 first

在一个严肃的专业示例中,我会选择以下任一方式:

  • 使用 python 将列读入数据结构并按所需顺序直接输出 json

  • 在读入所有内容时,给所有内容加上一个数字前缀,然后通过管道传输sort -n(sort 可能会使用临时文件,但除非真的有必要,否则不会使用)。但是,这无法很好地与 json 配合使用。

  • 通过在原始列输入前添加数字(nl)或时间戳作为前缀来过滤原始列输入,并将其集成到 json 中。

答案2

好吧,这个解决方案对于我最初尝试做的事情来说有点过度了,但我还是将它发布在这里以防它有用。我最终使用了命名管道并通过 tee 将它们链接起来。花了一些时间弄清楚如何将其布局得美观,但它就在这里。我最初的方法使用了 1 个临时文件,而这个方法使用了 3 个命名管道,所以这有点过度设计了。但它可能在其他地方有用。

编辑:更新以让其使用临时文件。

#create a temp folder using whatever method we have available
function tempfolder () {
    local folder;
    which mktemp  >/dev/null && folder=$(mktemp -d) ||
       (which tempfile >/dev/null && folder=$(tempfile) &&  rm $folder && mkdir $folder) ||
          (folder="/tmp/$(basename $0)_$$" && mkdir $folder)
    echo $folder
}


function pipename() {
     echo $1/pipe${2}
  }

  function createPipes() {
      local pib=$(tempfolder);
      while [[ "$1" != "" ]] ; do
        p=$(pipename $pib $1)
        [[ ! -p $p ]] && mkfifo $p
        shift
      done;
      echo $pib;
  }

  function closePipes() {
      local pib="$1"; 
      [[ $pib = "" ]] && exit;
      rm -r $pib
  }


function tester () {





      local pb=$(createPipes 1 2 3);

       tee >(
               echo "task1:fetching" >&2
               read greeting recipient random verbage greeter; 
               echo "task1:thinking" >&2
               sleep 5;
               echo "task1:replying" >&2
               echo "task1:hello $greeter. nice to meet you. but my name is not $recipient."  > $(pipename $pb 1)             
               echo "task1:i'm done" >&2
          )  >(   
                echo "task2:fetching" >&2
                read Mr Clinton Needs A Dictionary;
                echo "task2:thinking" >&2
                sleep 3;
                echo "task2:replying" >&2
                echo "task2:It depends on what the meaning of the word $A is."> $(pipename $pb 2)
                echo "task2:i'm done" >&2

          )  >(   
                echo "task3:fetching" >&2
                read I Read random words and comment;
                echo "task3:$random is a random word"  > $(pipename $pb 3) 
                echo "task3:i'm done" >&2

          ) | (   

                cat $(pipename $pb 1) $(pipename $pb 2) $(pipename $pb 3);
                closePipes $pb 1 2 3;
          )

}

time (echo  hello world it is me - thanks | tester;)

相关内容