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?

11
demandé sur Cesar 2011-03-10 02:47:38

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\"
14
répondu Erik 2011-03-10 00:26:36

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"
7
répondu Gordon Davisson 2011-03-10 07:44:12

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é

3
répondu nhed 2011-10-21 14:40:39

je sais probablement que ypu l'a déjà utilisé, mais, qu'en est-il des citations simples? (ce type de '' ) ?

0
répondu 7dr3am7 2011-03-09 23:51:38

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 :)

0
répondu Tom Hale 2018-09-29 05:45:08

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"

-1
répondu jcubic 2011-03-09 23:54:32