Bash problème avec eval, variables et guillemets
j'ai lu des citations à bash ici et partout ailleurs, mais je n'ai pas d'aide pour résoudre ce problème.
le truc c'est que j'ai un petit script pour faire des sauvegardes en boucle.
Si je n'utilise pas eval
alors j'ai des problèmes avec $OPTIONS
variable rsync
.
mais si j'utilise eval
alors le problème va à la variable $CURRENT_DIR
...
rsync renvoie le message suivant: "Inattendue local arg: /chemin/avec'
j'ai essayé toutes les façons de citer la variable $CURRENT_DIR
CURRENT_DIR="/path/with spaces/backup"
DIR="dir_by_project"
f=":/home/project_in_server"
OPTIONS="-avr --exclude 'public_html/cms/cache/**' --exclude 'public_html/cms/components/libraries/cmslib/cache/**' --delete"
eval rsync --delete-excluded -i $OPTIONS root@example.com$f $CURRENT_DIR/xxx/$DIR/files
Est-il possible que je peux utiliser la variable $CURRENT_DIR
sans problèmes causés par des espaces?
7 réponses
eval rsync --delete-excluded -i $OPTIONS root@example.com$f "\"$CURRENT_DIR/xxx/$DIR/files\""
command "some thing"
exécute la commande avec un argument some thing
. Les guillemets sont analysés par le shell et les arguments sont configurés comme un tableau lors de l'exécution de la commande. La commande verra un argument comme quelque chose sans les guillemets.
la commande eval traite ses arguments plus ou moins comme s'ils étaient tapés dans le shell. Donc, si vous eval command "some thing"
, bash execute eval
avec deux arguments: command
et some thing
(encore une fois les citations sont mangées tandis que bash installe le tableau des arguments). Alors eval agit comme si vous aviez tapé command some thing
dans la coquille, qui n'est pas ce que vous voulez.
ce que j'ai fait était simplement pour échapper aux citations, de sorte que bash passe littéralement "quelque chose" incluant les citations à eval. eval agit alors comme si vous aviez tapé command "some thing"
.
les citations externes dans mon ordre ne sont pas strictement nécessaires, elles sont juste une habitude. Vous pourriez tout aussi bien utiliser:
eval rsync --delete-excluded -i $OPTIONS root@example.com$f \"$CURRENT_DIR/xxx/$DIR/files\"
l'utilisation d'eval est dangereuse et devrait être évitée dans la mesure du possible. Dans ce cas, le problème principal est que vous essayez de définir des OPTIONS comme contenant plusieurs mots, et les variables de bash ne gèrent pas cela très bien. Il y a une solution: mettre des OPTIONS dans un tableau, au lieu d'une variable simple (et utiliser des guillemets doubles autour de toutes les références variables, pour empêcher les espaces d'être traités comme des séparateurs de mots).
CURRENT_DIR="/path/with spaces/backup"
DIR="dir_by_project"
f=":/home/project_in_server"
OPTIONS=(-avr --exclude 'public_html/cms/cache/**' --exclude 'public_html/cms/components/libraries/cmslib/cache/**' --delete)
rsync --delete-excluded -i "${OPTIONS[@]}" "root@example.com$f" "$CURRENT_DIR/xxx/$DIR/files"
je suis d'accord avec Gordon
vous n'avez pas besoin d'eval dans ce cas (vous ne formez pas un nom de variable à partir de variables, ou ne faites pas une expression à la volée)
et vous voulez citer deux fois toutes les références variables qui pourraient avoir des espaces que vous voudriez préserver
mais une autre bonne habitude est de toujours réfuter les variables avec {} ...
"${CURRENT_DIR}"
au lieu de
$CURRENT_DIR
ceci supprime tout nom l'ambiguïté
je sais probablement que ypu l'a déjà utilisé, mais, qu'en est-il des citations simples? (ce type de '' ) ?
Générique réutilisable solution
bien que comprendre comment citer correctement les choses est important, pour la facilité d'utilisation et pour prévenir les dérapages, je préfère utiliser une fonction:
ce qui suit maintient les espaces dans les arguments en citant chaque élément du tableau:
function token_quote {
local quoted=()
for token; do
quoted+=( "$(printf '%q' "$token")" )
done
printf '%s\n' "${quoted[*]}"
}
Exemple d'utilisation:
$ token_quote token 'single token' token
token single\ token token
au-dessus, notez le single token
'espace s est cité comme \
.
$ set $(token_quote token 'single token' token)
$ eval printf '%s\n' "$@"
token
single token
token
$
cela montre que les jetons sont effectivement conservés séparé.
compte tenu de certains non approuvé l'entrée d'utilisateur:
% input="Trying to hack you; date"
construisez une commande pour eval:
% cmd=(echo "User gave:" "$input")
Eval, avec apparemment corriger le cite:
% eval "$(echo "${cmd[@]}")"
User gave: Trying to hack you
Thu Sep 27 20:41:31 +07 2018
notez que vous avez été piraté. date
a été exécutée au lieu d'être imprimée littéralement.
token_quote()
:
% eval "$(token_quote "${cmd[@]}")"
User gave: Trying to hack you; date
%
eval
n'est pas mal - c'est juste mal compris :)
vous devez Vous échapper de l'espace dans CURRENT_DIR="/path/with\ spaces/backup"
si cela ne fonctionne pas alors mettez double backslash CURRENT_DIR="/path/with\ spaces/backup"