printf workfrapper parecido para prefixar e redirecionar mensagens de erro

1

Eu tenho uma função error no código shell do POSIX sh . Essencialmente parece

error () {
    printf 'utility: ERROR: ' >&2
    printf "$@" >&2
}

O segundo printf faz com que eu possa chamar a função usando, por exemplo.

error 'Something relating to "%s" went wrong!\n' "$thing"
exit 1

e a função prefixaria a mensagem com a string utility: ERROR: e organizaria com tudo sendo enviado para erro padrão enquanto eu ainda sou capaz de usar uma string de formatação como eu faria com printf .

Obviamente, o ShellCheck reclama sobre o segundo printf , dizendo

SC2059: Don't use variables in the printf format string. Use printf "..%s.." "$foo"

É "seguro" fazer o que eu faço e simplesmente ignorar esse aviso (talvez até mesmo desabilitando com um comentário # shellcheck disable=SC2059 no código)? Note, eu sempre uso minha error function exatamente como eu usaria printf , ou seja, com um primeiro argumento estático com aspas simples.

"É seguro?" aqui significa "A minha função funciona como um empacotador (sem o redirecionamento) em torno de printf de maneira apropriada?"

    
por Kusalananda 19.04.2018 / 12:45

1 resposta

2

É seguro que você pretenda que o formato seja passado como argumento. Mas você tem que lembrar quando usar a função que é o formato. Então, talvez você deva nomear sua função errorf como um lembrete.

Se você usou como:

error "invalid arg: $arg"

Você causaria problemas como valores de $arg como %99999999s

O ideal é que você queira dizer ao shellcheck para ignorar esse uso de printf e para sinalizar os usos de seu printf , onde o primeiro argumento contém variáveis.

Algumas notas:

  • Como você está invocando printf duas vezes, serão pelo menos 2 write() de chamadas do sistema. É improvável que faça alguma diferença e observe que algumas implementações printf como bash farão várias chamadas do sistema em algumas circunstâncias também. O tipo de problema em que estou pensando são as duas partes da mensagem de erro sendo entrelaçadas com a saída de outro comando em execução em paralelo.
  • Em error "%s\n" error1 error2 , o prefixo será gerado apenas para o primeiro erro. Tudo bem se é assim que você quer fazer. Você também pode fazer:

    errorf() { printf "utility: ERROR: $@" >&2; }
    

    para que o prefixo seja prefixado ao formato, para que seja emitido toda vez que o formato for reutilizado (o que também resolveria o problema múltiplo write() ).

    Isso, no entanto, não funcionaria se você quisesse que utility fosse uma variável.

    PROG_NAME=${0##*/}
    errorf() { printf >&2 "$PROG_NAME: ERROR: $@"; }
    
    não está correto, pois falha quando $0 contém % ou \ .

    e

    PROG_NAME=${0##*/}
    errorf() {
    format=$1; shift
    printf >&2 "%s: ERROR: $format" "$PROG_NAME" "$@"
    }
    não funcionariam nos casos em que o formato fosse reutilizado (e significaria que o usuário precisaria compensar por uma das diretivas %<n>$d (não que eu recomendaria usar aqueles que não são portáteis)).

    Não consigo pensar em nenhum trabalho fácil além de escapar do \ e % manualmente no $PROG_NAME , e mesmo isso não é fácil, já que com muitas implementações printf , você também precisaria para escapar dos 0x5c bytes encontrados em outros caracteres que não sejam \ (para os conjuntos como BIG5 ou GB18030 que possuem algum).

por 19.04.2018 / 13:18