Citazione degli argomenti della riga di comando negli script di shell
Domanda
Il seguente script di shell accetta un elenco di argomenti, trasforma i percorsi Unix in percorsi WINE/Windows e richiama l'eseguibile fornito sotto WINE.
#! /bin/sh
if [ "${1+set}" != "set" ]
then
echo "Usage; winewrap EXEC [ARGS...]"
exit 1
fi
EXEC="$1"
shift
ARGS=""
for p in "$@";
do
if [ -e "$p" ]
then
p=$(winepath -w $p)
fi
ARGS="$ARGS '$p'"
done
CMD="wine '$EXEC' $ARGS"
echo $CMD
$CMD
Tuttavia, c'è qualcosa di sbagliato nella citazione degli argomenti della riga di comando.
$ winewrap '/home/chris/.wine/drive_c/Program Files/Microsoft Research/Z3-1.3.6/bin/z3.exe' -smt /tmp/smtlib3cee8b.smt
Executing: wine '/home/chris/.wine/drive_c/Program Files/Microsoft Research/Z3-1.3.6/bin/z3.exe' '-smt' 'Z: mp\smtlib3cee8b.smt'
wine: cannot find ''/home/chris/.wine/drive_c/Program'
Notare che:
- Il percorso dell'eseguibile viene troncato nel primo spazio, anche se è racchiuso tra virgolette singole.
- Il valore letterale " " nell'ultimo percorso viene trasformato in un carattere di tabulazione.
Ovviamente, le virgolette non vengono analizzate nel modo previsto dalla shell.Come posso evitare questi errori?
MODIFICARE:La " " viene espansa attraverso due livelli di indiretto:Primo, "$p"
(e/o "$ARGS"
) viene espanso in Z:\tmp\smtlib3cee8b.smt
;Poi, \t
viene espanso nel carattere di tabulazione.Questo è (apparentemente) equivalente a
Y='y\ty'
Z="z${Y}z"
echo $Z
che cede
zy\tyz
E non
zy yz
AGGIORNAMENTO: eval "$CMD"
fa il trucco.IL "\t
" il problema sembra essere colpa di echo:"Se il primo operando è -N o se uno qualsiasi degli operandi contiene un carattere di backslash (''), i risultati sono definiti dall'implementazione." (Specifica POSIX di echo
)
Soluzione
Se vuoi avere l'assegnazione a CMD che dovresti usare
eval $CMD
invece che solo $CMD
nell'ultima riga del tuo script.Questo dovrebbe risolvere il tuo problema con gli spazi nei percorsi, non so cosa fare per il problema " ".
Altri suggerimenti
- gli array di bash non sono portabili ma rappresentano l’unico modo sensato per gestire gli elenchi di argomenti nella shell
- Il numero di argomenti è in ${#}
- Se nella directory corrente sono presenti nomi di file che iniziano con un trattino, accadranno cose brutte con il tuo script
- Se l'ultima riga del tuo script esegue semplicemente un programma e non ci sono trappole all'uscita, dovresti eseguirlo
Con quello in mente
#! /bin/bash
# push ARRAY arg1 arg2 ...
# adds arg1, arg2, ... to the end of ARRAY
function push() {
local ARRAY_NAME="${1}"
shift
for ARG in "${@}"; do
eval "${ARRAY_NAME}[\${#${ARRAY_NAME}[@]}]=\${ARG}"
done
}
PROG="$(basename -- "${0}")"
if (( ${#} < 1 )); then
# Error messages should state the program name and go to stderr
echo "${PROG}: Usage: winewrap EXEC [ARGS...]" 1>&2
exit 1
fi
EXEC=("${1}")
shift
for p in "${@}"; do
if [ -e "${p}" ]; then
p="$(winepath -w -- "${p}")"
fi
push EXEC "${p}"
done
exec "${EXEC[@]}"
sostituisci l'ultima riga da $CMD a just
vino '$EXEC' $ARGS
Noterai che l'errore è ''/home/chris/.wine/drive_c/Program' e non '/home/chris/.wine/drive_c/Program'
Le virgolette singole non vengono interpolate correttamente e la stringa viene divisa in spazi.
Puoi provare a far precedere gli spazi con \ in questo modo:
/home/chris/.wine/drive_c/Program Files/Microsoft\ Research/Z3-1.3.6/bin/z3.exe
Puoi anche fare lo stesso con il tuo problema : sostituiscilo con \ .