Estou escrevendo um pequeno script utilitário que pode ser parametrizado para complementar time
sua execução e também gravar seu stdout em outro arquivo.
Vamos supor que o script básico seja simplesmente:
ls "$@"
Então, o seguinte seria uma implementação ingênua, aqui usando cat
como um manequim para manipulação mais complexa da saída:
if [[ "$TIMEIT" -eq 1 ]]; then
if [[ "$LOGIT" -eq 1 ]]; then
time ls "$@" | tee >(cat)
else
time ls "$@"
fi
else
if [[ "$LOGIT" -eq 1 ]]; then
ls "$@" | tee >(cat)
else
ls "$@"
fi
fi
Você pode ver qual é o problema com isso, especialmente se novos parâmetros devem ser adicionados.
Uma reescrita que seria mais flexível é:
cmd="ls $@"
if [[ "$TIMEIT" -eq 1 ]]; then
cmd="time $cmd"
fi
if [[ "$LOGIT" -eq 1 ]]; then
cmd="$cmd | tee >(cat)"
fi
eval $cmd
Isso funciona, mas quebra quando você o chama como:
./script.sh "file 1" file2
Porque os nomes dos arquivos não serão tratados corretamente.
Quando aplico esta solução , posso tokenizar adequadamente os argumentos ao preceder o comando com time
:
function token_quote {
local quoted=()
for token; do
quoted+=( "$(printf '%q' "$token")" )
done
printf '%s\n' "${quoted[*]}"
}
cmd=(ls "$@")
if [[ "$TIMEIT" -eq 1 ]]; then
cmd=(time "${cmd[@]}")
fi
eval "$(token_quote "${cmd[@]}")"
Mas o mesmo método para manusear o cachimbo não funciona. Quando eu o adapto usando esta solução :
function eval_args {
local quoted=''
while (( $# )); do
if [[ $1 = '|' ]]; then
quoted+="| "
else
printf -v quoted '%s%q ' "$quoted" "$1"
fi
shift
done
eval "$quoted"
}
cmd=(ls "$@")
# prepend to array
if [[ "$TIMEIT" -eq 1 ]]; then
cmd=(time "${cmd[@]}")
fi
# append to array?
if [[ "$LOGIT" -eq 1 ]]; then
cmd=("${cmd[@]}" "|" "tee" ">(cat)")
fi
eval_args "${cmd[@]}"
Ele cria um arquivo >(cat)
… então estou entrando na toca do coelho aqui, e parece que este não é o caminho a seguir.
O que posso fazer para construir o comando e lidar com espaços/caracteres especiais em nomes de arquivos, incluindo pipes/substituição de processo?
avaliação
Seu último método pode funcionar, contanto que você o ignore para suas adições personalizadas e o use apenas para gerar o comando original. Ou seja, altere eval_args para criar apenas a string de comando:
Na verdade, o loop é desnecessário, pois o printf do bash repetirá automaticamente a mesma string de formato com argumentos extras:
O Bash 5.1 possui um
${foo@Q}
modificador que é uma alternativa mais conveniente:Alternativa para avaliação
Digamos que seu script tenha seu código principal em uma função:
Você pode definir condicionalmente uma função wrapper para ele:
(A própria definição da função pode ser gerada em uma string e avaliada.)
Reexec
Em alguns casos, pode fazer sentido reexecutar todo o script no novo ambiente:
Diversos
Quanto aos redirecionamentos especificamente, você pode codificar o uso
tee
e apenas trocar o nome do arquivo (não muito eficiente, pois ainda faz com que o tee seja executado e duplica as gravações, mas ... faz o trabalho):O mesmo para redirecionamentos diretos para um arquivo:
Como alternativa, você pode canalizar uma condição ou uma função que envolva a condição (também não é 100% eficiente devido a obrigatório
cat
):