Bash espera por trabalhos e limita a contagem de trabalhos [duplicados]

1

Suponha que um loop invoque um comando

 grep -v '#' < files.m3u | sed 's/\\/\/\//g' | sed 's/\/\//g' | while read line
 do
    filename=$(basename "$line")
    avconv -i "$line" "${filename%.*}.wav"
 done

Colocando & após avconv continuará gerando avconv para cada arquivo. Agora quero fazer duas coisas:

  • Quero limitar o número de processos gerados para 4
  • Quando o loop terminar, quero aguardar que o último esteja pronto
por user877329 23.08.2012 / 11:23

3 respostas

3

Você pode lembrar o PID de cada novo filho (verifique $! depois de iniciá-lo). Verifique periodicamente quantos filhos ainda existem (por exemplo, por kill -0 ), se o número desce, crie um novo, etc. No final, apenas wait .

Aqui está um script que escrevi pelo mesmo motivo:

#! /bin/bash

## Tries to run commands in parallel. Commands are read from STDIN one
## per line, or from a given file specified by -f.
## Author: E. Choroba

file='-'
proc_num=$(grep -c ^processor'\b' /proc/cpuinfo)
prefix=$HOSTNAME-$USER-$$
sleep=10

children=()
names=()

if [[ $1 =~ ^--?h(elp)?$ ]] ; then
    cat <<-HELP
    Usage: ${0##*/} [-f file] [-n max-processes] [-p tmp-prefix] -s [sleep]
      Defaults:
        STDIN for file
        $proc_num for max-processes (number of processors)
        $prefix for tmp-prefix
        $sleep for sleep interval
    HELP
    exit
fi

function debug () {
    if ((DEBUG)) ; then
        echo "$@" >&2
    fi
}

function child_count () {
    debug Entering child_count "${children[@]}"
    child_count=0
    new_children=()
    for child in "${children[@]}" ; do
        debug Trying $child
        if kill -0 $child 2>/dev/null ; then
            debug ... exists
            let child_count++
            new_children+=($child)
        fi
    done

    children=("${new_children[@]}")
    echo $child_count
    debug Leaving child_count "${children[@]}"
}

while getopts 'f:n:p:s:' arg ; do
    case $arg in
        f ) file=$OPTARG ;;
        n ) proc_num=$((OPTARG)) ;;
        p ) prefix=$OPTARG;;
        s ) sleep=$OPTARG;;
        * ) echo "Warning: unknown option $arg" >&2 ;;
    esac
done

i=0
while read -r line ; do
    debug Reading $line
    name=$prefix.$i
    let i++
    names+=($name)

    while (('child_count'>=proc_num)) ; do
        sleep $sleep
        debug Sleeping
    done

    eval $line 2>$name.e >$name.o &
    children+=($!)
    debug Running "${children[@]}"
done < <(cat $file)

debug Loop ended
wait
cat "${names[@]/%/.o}"
cat "${names[@]/%/.e}" >&2
rm "${names[@]/%/.o}" "${names[@]/%/.e}"
    
por 23.08.2012 / 11:28
2

A partir da pergunta vinculada , adaptada à sua variação:

sed -n -e '/#/!s,\,/,g' files.m3u | xargs -d '\n' -I {} -P 4 \
    sh -c 'line=$1; file=${line##*/}; avconv -i "$line" "${file%.*}.wav"' avconv_sh {}

Mais uma vez, o GNU xargs ou alguma versão que suporta -d e -P é necessário. Também tome cuidado com os espaços extras no arquivo de entrada no início e no final da linha - este trecho de código os manterá se existirem, o que pode causar problemas.

    
por 23.08.2012 / 16:09
0

Eu resolvi assim. Obrigado pelo $! dicas

#!/bin/bash
children[0]=0
children[1]=0
children[2]=0
children[3]=0

i=0
grep -v '#' < files.m3u | sed 's/\\/\/\//g' | sed 's/\/\//g' | while read line
do
    filename=$(basename "$line")
    let k="$i%4"
    wait ${children[k]}
    avconv -i "$line" "${filename%.*}.wav" &
    children[k]=$!
    let i++
done

wait ${children[0]}
wait ${children[1]}
wait ${children[2]}
wait ${children[3]}
    
por 23.08.2012 / 11:57