Script Shell: création d'une variable avec des options à l'intérieur

11

J'ai une commande rsync avec les paramètres suivants:

rsync -avz --{partial,stats,delete,exclude=".*"}

Je veux mettre ces paramètres dans une variable pour la réutiliser ensuite dans le script. Quelque chose comme ça:

#!/bin/bash
VAR=rsync -avz --{partial,stats,delete,exclude=".*"}
$VAR /dir1 /dir2

J'ai essayé avec des guillemets, des guillemets simples, des crochets, sans succès.

Kellyson
la source
Question similaire sur SO: stackoverflow.com/questions/13365553/…
Barmar
Après avoir suivi cette route auparavant: assurez-vous que votre chaîne de commande résultante finale ne contient aucune chaîne vide. Si c'est le cas, rsync peut les utiliser comme paramètres et comme ils sont invisibles, il est assez difficile à déboguer. J'avais un premier paramètre vide et rsync l'a interprété comme incluant le répertoire courant dans les sources des choses à copier.
Joe

Réponses:

12

Placer une commande complexe dans une variable n'est jamais une approche recommandée. Voir BashFAQ / 050 - J'essaie de mettre une commande dans une variable, mais les cas complexes échouent toujours!

Votre exigence devient vraiment simple, si vous décidez simplement d'utiliser une fonction au lieu d'une variable et de lui passer des arguments.

Quelque chose comme

rsync_custom() {
    [ "$#" -eq 0 ] && { printf 'no arguments supplied' >&2; exit 1 ; }
    rsync -avz --{partial,stats,delete,exclude=".*"} "$@"
}

et lui passer maintenant les arguments requis

rsync_custom /dir1 /dir2

La définition de la fonction est assez simple en quelque sorte, nous vérifions d'abord le nombre d'arguments d'entrée en utilisant la variable $#qui ne doit pas être nulle. Nous lançons un message d'erreur indiquant qu'aucun argument n'est fourni. S'il existe des arguments valides, "$@"représente alors les arguments réels fournis à la fonction.

S'il s'agit d'une fonction que vous utiliserez assez fréquemment, c'est-à-dire également dans les scripts / ligne de commande, ajoutez-la aux fichiers de démarrage du shell .bashrc, .bash_profilepar exemple.

Ou comme indiqué, il peut être utile d'étendre l'expansion de l'accolade pour séparer les arguments pour une meilleure lisibilité car

rsync_custom() {
    [ "$#" -eq 0 ] && { printf 'no arguments supplied' >&2; exit 1 ; }
    rsync -avz --partial --stats --delete --exclude=".*" "$@"
}
Inian
la source
5
VAR=rsync -avz --{partial,stats,delete,exclude=".*"}

Cela essaie d'exécuter la commande -avzavec des arguments --partial, --statsetc. et avec VARset to rsyncdans l'environnement.

VAR='rsync -avz --{partial,stats,delete,exclude=".*"}'

Le formulaire entre guillemets ne fonctionne pas car les accolades ne sont pas développées entre guillemets, et non à l'intérieur des affectations, et elles ne sont pas non plus développées après le développement d'une variable.

Si vous devez stocker des arguments de ligne de commande dans une variable, utilisez un tableau:

args=(rsync -avz --{partial,stats,delete,exclude=".*"})

Maintenant , "${args[@]}"va étendre à rsync, -avz, --partial, etc. comme des mots distincts.

Les tableaux vous permettent également d'ajouter des options à la liste, conditionnellement si nécessaire, afin que vous puissiez par exemple:

args=(this that)
if something ; then
    args+=(another_arg)
fi
"$cmd" "${args[@]}"
ilkkachu
la source
1

Vous pouvez au moins enregistrer les options partiellement dans une variable:

opts=$(echo --{ignore-case,word-regexp,count,exclude='"sys*.*"'})

Les tests sont importants, car le masquage peut être difficile:

echo $opts
--ignore-case --word-regexp --count --exclude="sys*"

grep $opts bytes *.log 

Puisqu'il existe plusieurs alternatives, comme l'utilisation de l'historique, l'utilisation d'un alias, l'utilisation d'une fonction, il n'y a pas de cas d'utilisation évident auquel je puisse penser. Il y a rarement une option complexe à partager entre différents programmes, donc pour une solution ad-hoc pour le shell interactif, l'aliasing semble une meilleure façon:

alias cgrep='grep --ignore-case --word-regexp --count --exclude="sys*"'
cgrep bytes *.log

Votre échantillon

VAR=rsync -avz --{partial,stats,delete,exclude=".*"}

ne peut pas fonctionner, car l'affectation est endet au premier blanc. Vous devez masquer les blancs:

VAR='rsync -avz --{partial,stats,delete,exclude=".*"}'

une chose assez dangereuse pour les tests, avec cette option --delete, n'est-ce pas? Étant donné que les options peuvent contenir à nouveau "," et des guillemets simples, le masquage peut devenir difficile très rapidement. Je choisirais un alias ou me fierais à l'histoire.

Un alias peut être stocké dans le fichier ~ / .bashrc pour une utilisation continue sur plusieurs sessions. Les fonctions peuvent également être stockées dans le bashrc, mais vous n'en avez besoin, si vous voulez gérer les paramètres, passés dans la fonction pour y être évalué.

Utilisateur inconnu
la source