Como atribuir variáveis de ambiente em paralelo no bash

5

Estou tentando definir várias variáveis de ambiente com os resultados da substituição de comandos. Eu quero executar os comandos em paralelo com & e wait . O que eu tenho atualmente parece algo como

export foo='somecommand bar' &
export fizz='somecommand baz' &
export rick='somecommand morty' &
wait

Mas, aparentemente, ao usar & atribuições de variáveis, não se atenha. Então, depois do wait , todas essas variáveis não são atribuídas.

Como posso atribuir essas variáveis em paralelo?

ATUALIZAÇÃO: Veja o que acabei usando com base na resposta aceita

declare -a data
declare -a output
declare -a processes

var_names=(
    foo
    fizz
    rick
)

for name in "${var_names[@]}"
do
    processes+=("./get_me_a_value_for $name")
done

index=0
for process in "${processes[@]}"; do
    output+=("$(mktemp)")
    ${process} > ${output[$index]} &
    index=$((index+1))
done
wait

index=0
for out in "${output[@]}"; do
    val="$(<"${out}")"
    rm -f "${out}"

    export ${var_names[index]}="$val"

    index=$((index+1))
done

unset data
unset output
unset processes
    
por kag0 06.04.2017 / 00:04

2 respostas

6

Depois de algumas ruminações, desenvolvi uma solução feia:

#!/bin/bash
proc1=$(mktemp)
proc2=$(mktemp)
proc3=$(mktemp)

/path/to/longprocess1 > "$proc1" &
pid1=$!
/path/to/longprocess2 > "$proc2" &
pid2=$!
/path/to/longprocess3 > "$proc3" &
pid3=$!

wait "$pid1" "$pid2" "$pid3"
export var1="<("$proc1")"
export var2="<("$proc2")"
export var3="<("$proc3")"
rm -f "$proc1" "$proc2" "$proc3"

Conforme solicitado em um comentário, veja como tornar isso mais extensível para uma lista arbitrariamente grande:

#!/bin/bash
declare -a pids
declare -a data
declare -a output
declare -a processes

# Generate the list of processes for demonstrative purposes
processes+=("/path/to/longprocess1")
processes+=("/path/to/longprocess2")
processes+=("/path/to/longprocess3")

index=0
for process in "${processes[@]}"; do
    output+=("$(mktemp")
    $process > ${output[$index]} &
    pids+=("$!")
    index=$((index+1))
done
wait ${pids[@]}
index=0
for process in "${processes[@]}"; do
    data+="$(<"${output[index]}")"
    rm -f "${output[index]}"
    index=$((index+1))
done
export data

A saída resultante estará na matriz data .

    
por 06.04.2017 / 00:35
0

Se você tem mais trabalhos que podem rodar em paralelo ao mesmo tempo, você pode usar o GNU Parallel:

i="$(parallel --files ::: "${cmd[@]}" | perl -pe '$_="\"\'cat $_;rm $_\'\" "')"
eval my_result_array=($i)
unset i

Ou em geral:

parset() {
  local vname
  vname="$1"
  shift
  if [[ "$(declare -p $vname 2>/dev/null)" =~ "declare -a" ]]; then
      # vname refers to an array
      # The array elements refers to variable names to put output into
      eval $(
          parallel --files "$@" |
              perl -pe 'chop;$_="\"\'cat $_; rm $_\'\"\n"' |
              parallel echo {2}={1} :::: - :::+ $(eval echo '${'$vname'[@]}')
      )
  else
    # vname is not an existing array: Put output into that
    eval $vname="( $( parallel --files "$@" |
      perl -pe 'chop;$_="\"\'cat $_; rm $_\'\" "' ) )"
  fi
}

# Put output into vars $seq, $pwd, $ls
into_vars=(seq pwd ls)
parset into_vars ::: "seq 10" pwd ls
echo "$ls"

# Put output into aaa
unset aaa    
parset aaa seq 3 ::: 4 5 6
echo "${aaa[1]}"

# Output can contain spaces
parset out ::: "echo '<<joe  \"double  space\"  cartoon>>'" "pwd"
echo "${out[0]}"
echo "${out[1]}"

# The commands to run can be an array
cmd=("echo '<<joe  \"double  space\"  cartoon>>'" "pwd")
parset data ::: "${cmd[@]}"
echo "${data[0]}"
echo "${data[1]}"

# You cannot pipe into parset, but must use a tempfile
seq 10 > parallel_input
parset res echo :::: parallel_input
echo "${res[0]}"
echo "${res[9]}"
    
por 10.04.2017 / 08:23