Substituição de processo condicional

2

Eu tenho um comando com vários argumentos:

my_command --arg1 <arg1> \
    --arg2 <arg2> \
    --arg3 <arg3>

Eu envolvi a invocação de my_command em uma função que configura os valores para os vários argumentos, que podem ou não ser passados:

run_my_command() {
    if [[ ... ]]; then
        ARG1FLAGS="--arg1 $ARG1"
    else
        ARG1FLAGS=''
    fi
    if [[ ... ]]; then
        ARG2FLAGS="--arg2 $ARG2"
    else
        ARG2FLAGS=''
    fi
    if [[ ... ]]; then
        ARG3FLAGS="--arg3 $ARG3"
    else
        ARG3FLAGS=''
    fi

    my_command $ARG1FLAGS \
        $ARG2FLAGS \
        $ARG3FLAGS
}

Em alguns casos, é necessário passar um argumento de nome de arquivo para my_command :

run_my_command() {
    # ...
    if [[ ... ]]; then
        FILEARG="--input_file $SOME_FILE"
    else
        FILEARG=''
    fi

    my_command $FILEARG \
        ...
}

Tudo isso funciona bem. Agora, gostaria de usar condicionalmente a substituição de processo para FILEARG , mas é claro que isso não funciona:

run_my_command() {
    # ...
    if [[ ... ]]; then
        FILEFLAG='--input_file'
        FILEARG=<(other_cmd)
    else
        FILEARG=''
    fi

    my_command $FILEFLAG $FILEARG \
        ...
}

porque no momento em que my_command é executado, $FILEARG , um canal anônimo com aparência de /dev/fd/63 , já está fechado.

No momento, resolvi isso colocando todos os my_command em uma condição:

run_my_command() {
    # Get ARG1, ARG2, ARG3...
    if [[ ... ]]; then
        my_command --input_file <(other_cmd) \
            $ARG1FLAGS \
            $ARG2FLAGS \
            $ARG3FLAGS
    else
        my_command $ARG1FLAGS \
            $ARG2FLAGS \
            $ARG3FLAGS
    fi
}

mas não gosto da duplicação. Eu sinto que talvez haja algo que eu possa fazer com eval , ou possivelmente eu posso colocar my_command em outra função, mas eu ainda não descobri. Como posso usar condicionalmente a substituição de processos para gerar um arquivo do qual my_command possa ler sem duplicar a chamada inteira para my_command ?

Note que estou executando o bash 4.4.19. Para minha considerável surpresa, o bash 3.2.57 parece se comportar da maneira que o wiki do Bash Hackers, abaixo, sugere:

doit() {
    local -r FOO=<(echo hi)
    cat $FOO
}

doit

# bash 3.2.57:
$ ./test.sh
hi
# bash 4.4.19:
$ ./test.sh
cat: /dev/fd/63: Bad file descriptor

Aqui estão algumas perguntas que examinei, mas das quais não consegui obter uma resposta em funcionamento:

Além disso, as notas do wiki do Bash Hackers são bastante enigmáticas:

If a process substitution is expanded as an argument to a function, expanded to an environment variable during calling of a function, or expanded to any assignment within a function, the process substitution will be "held open" for use by any command within the function or its callees, until the function in which it was set returns. If the same variable is set again within a callee, unless the new variable is local, the previous process substitution is closed and will be unavailable to the caller when the callee returns.

In essence, process substitutions expanded to variables within functions remain open until the function in which the process substitution occured returns - even when assigned to locals that were set by a function's caller. Dynamic scope doesn't protect them from closing.

mas eu não consegui obter uma substituição de processo "mantida aberta" apenas atribuindo-a a uma variável em uma função.

    
por ravron 20.02.2018 / 18:42

3 respostas

2

Isso é mais como evitar o problema, mas há menos duplicação (de $ARGnFLAGS ) se você colocar os outros argumentos em uma matriz em vez de variáveis individuais.

Também é a coisa apropriada a fazer em qualquer caso, pois você pode obter os argumentos para o comando sem que a divisão de palavras ou a globalização de nomes de arquivo fique no caminho.

run_my_command() {
    args=()
    if [[ ... ]]; then
        args+=(--arg1 "$ARG1")
    fi
    if [[ ... ]]; then
        args+=(--arg2 "$ARG2")
    fi

    if [[ ... ]]; then
        my_command --input_file <(other_cmd) "${args[@]}"
    else
        my_command "${args[@]}" 
    fi
}
    
por 20.02.2018 / 20:15
1
run_my_command() {
    # ...
    if [[ ... ]]; then
        tmpfile=$(mktemp)
        trap 'rm -f "$tmpfile"' RETURN
        other_cmd >"$tmpfile"
        FILEFLAG='--input_file'
        FILEARG="$tmpfile"
    else
        FILEARG=''
    fi

    my_command $FILEFLAG $FILEARG \
        ...

    return
}

... ou algo nesse sentido.

Ou seja, use um arquivo temporário em vez de tentar usar uma substituição de processo.

    
por 20.02.2018 / 19:15
1

Veja como você pode usar eval para conseguir o que deseja:

run_my_command() {
    # Get ARG1, ARG2, ARG3...
    commandline="my_command $ARG1FLAGS $ARG2FLAGS $ARG3FLAGS"
    if [[ ... ]]; then
        commandline="$commandline --input_file <(other_cmd)"
    fi
    eval $commandline
}
    
por 20.02.2018 / 19:17