Anführungszeichen von Befehlszeilenargumenten in Shell-Skripten
Frage
Das folgende Shell-Skript nimmt eine Liste von Argumenten, wandelt Unix-Pfade in WINE/Windows-Pfade um und ruft die angegebene ausführbare Datei unter WINE auf.
#! /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
Allerdings stimmt etwas mit der Anführung von Befehlszeilenargumenten nicht.
$ 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'
Beachten Sie, dass:
- Der Pfad zur ausführbaren Datei wird am ersten Leerzeichen abgeschnitten, obwohl er in einfache Anführungszeichen gesetzt ist.
- Das Literal „ “ im letzten Pfad wird in ein Tabulatorzeichen umgewandelt.
Offensichtlich werden die Zitate von der Shell nicht so analysiert, wie ich es beabsichtigt hatte.Wie kann ich diese Fehler vermeiden?
BEARBEITEN:Das „ “ wird durch zwei Indirektionsebenen erweitert:Erste, "$p"
(und/oder "$ARGS"
) wird erweitert in Z:\tmp\smtlib3cee8b.smt
;Dann, \t
wird zum Tabulatorzeichen erweitert.Dies ist (scheinbar) gleichbedeutend mit
Y='y\ty'
Z="z${Y}z"
echo $Z
was ergibt
zy\tyz
Und nicht
zy yz
AKTUALISIEREN: eval "$CMD"
macht den Trick.Der "\t
„Das Problem scheint Echos Schuld zu sein:"Wenn der erste Operand -n ist oder wenn einer der Operanden einen Backslash -Zeichen ('') enthält, sind die Ergebnisse implementiert." (POSIX-Spezifikation von echo
)
Lösung
Wenn Sie die Zuordnung zu CMD haben möchten, sollten Sie diese verwenden
eval $CMD
statt nur $CMD
in der letzten Zeile Ihres Skripts.Dies sollte Ihr Problem mit Leerzeichen in den Pfaden lösen. Ich weiß nicht, was ich gegen das „ “-Problem tun soll.
Andere Tipps
- Die Arrays von Bash sind nicht portierbar, aber die einzig vernünftige Möglichkeit, Argumentlisten in der Shell zu verarbeiten
- Die Anzahl der Argumente ist in ${#}
- Wenn im aktuellen Verzeichnis Dateinamen mit einem Bindestrich beginnen, kann es zu schlimmen Problemen mit Ihrem Skript kommen
- Wenn die letzte Zeile Ihres Skripts nur ein Programm ausführt und es beim Beenden keine Traps gibt, sollten Sie es ausführen
In diesem Sinne
#! /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[@]}"
Ersetzen Sie die letzte Zeile von $CMD durch just
wine '$EXEC' $ARGS
Sie werden feststellen, dass der Fehler „/home/chris/.wine/drive_c/Program“ und nicht „/home/chris/.wine/drive_c/Program“ lautet.
Die einfachen Anführungszeichen werden nicht richtig interpoliert und die Zeichenfolge wird durch Leerzeichen aufgeteilt.
Sie können versuchen, den Leerzeichen ein \ voranzustellen, etwa so:
/home/chris/.wine/drive_c/Program Files/Microsoft\ Research/Z3-1.3.6/bin/z3.exe
Sie können das Gleiche auch mit Ihrem -Problem tun – ersetzen Sie es durch \ .