J'ai une fonction bash pour définir $PATH
comme ça -
assign-path()
{
str=$1
# if the $PATH is empty, assign it directly.
if [ -z $PATH ]; then
PATH=$str;
# if the $PATH does not contain the substring, append it with ':'.
elif [[ $PATH != *$str* ]]; then
PATH=$PATH:$str;
fi
}
Mais le problème est que je dois écrire une fonction différente pour différentes variables (par exemple, une autre fonction pour le $CLASSPATH
même, assign-classpath()
etc.). Je n'ai pas trouvé de moyen de passer l'argument à la fonction bash pour pouvoir y accéder par référence.
Ce serait mieux si j'avais quelque chose comme -
assign( bigstr, substr )
{
if [ -z bigstr ]; then
bigstr=substr;
elif [[ bigstr != *str* ]]; then
bigstr=bigstr:substr;
fi
}
Une idée, comment réaliser quelque chose comme ci-dessus en bash?
bash
bash-script
ramgorur
la source
la source
assign-path /abc
ne sera pas ajouter/abc
à PATH si $ PATH contient déjà/abc/def
,/abcd
,/def/abc
etc. Surtout vous ne pouvez pas ajouter/bin
si PATH contient déjà/usr/bin
.$PATH
et un test negate contre vos arguments tels que :add=/bin dir=/usr/bin ; [ -z "${dir%"$add"}" ] || dir="${dir}:${add}"
. Dans ma réponse, je le fais de cette façon avec autant d'arguments que vous le souhaitezIFS=:
.$PATH
? et Ajouter un répertoire$PATH
s'il n'est pas déjà là (sur Super User ).Réponses:
Dans
bash
vous pouvez utiliser${!varname}
pour développer la variable référencée par le contenu d'une autre. Par exemple:Depuis la page de manuel:
De plus, pour définir une variable référencée par le contenu (sans les dangers de
eval
), vous pouvez utiliserdeclare
. Par exemple:Ainsi, vous pourriez écrire votre fonction comme ceci (attention car si vous utilisez
declare
dans une fonction, vous devez donner-g
ou la variable sera locale):Et utilisez-le comme:
Notez que j'ai également corrigé un bogue où si
substr
est déjà une sous-chaîne d'un des membres séparés par deux points debigstr
, mais pas son propre membre, alors il ne serait pas ajouté. Par exemple, cela permettrait d'ajouter/bin
à unePATH
variable contenant déjà/usr/bin
. Il utilise lesextglob
ensembles pour correspondre soit au début / à la fin de la chaîne, soit à deux points, puis à toute autre chose. Sansextglob
, l'alternative serait:la source
-g
endeclare
n'est pas disponible dans l'ancienne version de bash, y a-t-il un moyen pour que je puisse rendre cette compatibilité descendante?export
pour le mettre dans votre environnement (au risque d'écraser quelque chose d'important) oueval
(divers problèmes dont la sécurité si vous ne faites pas attention). Sieval
vous utilisez, vous devriez être d'accord si vous le faites commeeval "$target=\$substr"
. Si vous oubliez le\
, il exécutera potentiellement une commande s'il y a un espace dans le contenu desubstr
.Nouveau dans bash 4.3, est l'
-n
option dedeclare
&local
:Cela s'imprime
hello, world
.la source
Vous pouvez utiliser
eval
pour définir un paramètre. Une description de cette commande peut être trouvée ici . L'utilisation suivante deeval
est incorrecte:En ce qui concerne l'évaluation supplémentaire,
eval
devez-vous utiliserVérifiez les résultats de l'utilisation de ces fonctions:
Mais vous pouvez atteindre votre objectif sans utiliser
eval
. Je préfère cette façon plus simple.La fonction suivante effectue la substitution de la bonne manière (j'espère)
Vérifiez la sortie suivante
Vous pouvez maintenant utiliser la
augment
fonction de la manière suivante pour définir une variable:la source
v='echo "OHNO!" ; var' ; l=val ; eval $v='$l'
- ferait écho à "OHNO! Avant d'affecter var. Vous pourriez" $ {v ## * [; "$ IFS"]} = '$ l' "pour vous assurer que la chaîne ne peut pas s'étendre à tout ce qui ne sera pas évalué avec le =.=
instruction d'affectation. Vous pouvez affirmer que mon script ne vérifie pas son argument. C'est vrai. Je ne vérifie même pas s'il y a un argument ou si le nombre d'arguments est valide. Mais c'était par intention. Le PO peut ajouter de tels contrôles s'il le souhaite.v
(mieux sa valeur) comme deuxième argument de la fonction assign. Par conséquent, sa valeur doit se trouver du côté droit d'une affectation. Il est nécessaire de citer l'argument de la fonctionassign
. J'ai ajouté cette subtilité à mon message.Avec quelques astuces, vous pouvez réellement transmettre des paramètres nommés aux fonctions, ainsi que des tableaux (testés dans bash 3 et 4).
La méthode que j'ai développée vous permet d'accéder aux paramètres passés à une fonction comme celle-ci:
En d'autres termes, non seulement vous pouvez appeler vos paramètres par leurs noms (ce qui constitue un noyau plus lisible), vous pouvez en fait passer des tableaux (et des références à des variables - cette fonctionnalité ne fonctionne que dans bash 4.3 cependant)! De plus, les variables mappées sont toutes dans la portée locale, tout comme $ 1 (et autres).
Le code qui fait ce travail est assez léger et fonctionne à la fois en bash 3 et bash 4 (ce sont les seules versions avec lesquelles je l'ai testé). Si vous êtes intéressé par plus de trucs comme celui-ci qui rendent le développement avec bash beaucoup plus agréable et plus facile, vous pouvez jeter un œil à mon framework Bash Infinity , le code ci-dessous a été développé à cet effet.
la source
Cela correspond-il?
la source
eval
est susceptible d'exécuter des commandes arbitraires.eval
lignes zhe plus sûres? Normalement, quand je pense avoir besoin d'eval, je décide de ne pas utiliser * sh et de passer à une autre langue à la place. D'un autre côté, en l'utilisant dans des scripts pour ajouter des entrées à certaines variables de type PATH, il s'exécutera avec des constantes de chaîne et non des entrées utilisateur ...eval
toute sécurité - mais cela demande beaucoup de réflexion. Si vous essayez simplement de référencer un paramètre, vous voudriez faire quelque chose comme ceci: deeval "$1=\"\$2\""
cette façon, lors de laeval's
première passe, il n'évalue que 1 $ et la seconde, il vaut = "$ 2". Mais vous devez faire autre chose - ce n'est pas nécessaire ici."${1##*[;"$IFS"]}=\"\$2\""
- et même cela n'est accompagné d'aucune garantie. Oueval "$(set -- $1 ; shift $(($#-1)) ; echo $1)=\"\$2\""
. Ce n'est pas facile.Les arguments nommés ne sont tout simplement pas comment la syntaxe de Bash a été conçue. Bash a été conçu pour être une amélioration itérative du shell Bourne. En tant que tel, il doit s'assurer que certaines choses fonctionnent autant que possible entre les deux coques. Donc, il n'est pas censé être plus facile de créer un script dans l'ensemble, il est juste censé être meilleur que Bourne tout en garantissant que si vous prenez un script d'un environnement Bourne,
bash
c'est aussi simple que possible. Ce n'est pas anodin car de nombreux obus traitent toujours Bourne comme une norme de facto. Étant donné que les gens écrivent leurs scripts pour être compatibles avec Bourne (pour cette portabilité), le besoin reste en vigueur et ne changera probablement jamais.Vous feriez probablement mieux de regarder un script shell différent (comme
python
ou quelque chose) entièrement si c'est possible. Si vous rencontrez les limites d'une langue, vous devez commencer à utiliser une nouvelle langue.la source
bash
la vie, c'était vrai. Mais maintenant, des dispositions spécifiques sont prises. Les variables de référence complètes sont désormais disponibles dansbash 4.3
- voir la réponse de Derobert .Avec la
sh
syntaxe standard (fonctionnerait dansbash
, et pas seulement dansbash
), vous pourriez faire:Comme pour les solutions utilisant
bash
desdeclare
, c'est sûr tant qu'il$1
contient un nom de variable valide.la source
SUR LES ARGS NOMMÉES:
Cela se fait très simplement et
bash
n'est pas du tout requis - c'est le comportement d'affectation spécifié par POSIX de base via l'expansion des paramètres:Pour faire une démonstration dans le même esprit que @Graeme, mais de manière portable:
Et là, je ne fais que
str=
pour m'assurer qu'il a une valeur nulle, car l'expansion des paramètres a la protection intégrée contre la réaffectation de l'environnement shell en ligne si elle est déjà définie.SOLUTION:
Pour votre problème spécifique, je ne pense pas que des arguments nommés soient nécessaires, bien qu'ils soient certainement possibles. Utilisez
$IFS
plutôt:Voici ce que j'obtiens lorsque je l'exécute:
Remarquez qu'il n'a ajouté que les arguments qui n'étaient pas déjà inclus
$PATH
ou qui ont précédé? Ou même qu'il a fallu plus d'un argument du tout?$IFS
est pratique.la source
assign
qu'ici. Si vous avez des questions sur son fonctionnement, je serai ravi d'y répondre. Et au fait, si vous voulez vraiment des arguments nommés, vous voudrez peut-être regarder cette autre réponse de moi dans laquelle je montre comment déclarer une fonction nommée pour les arguments d'une autre fonction: unix.stackexchange.com/a/120531/52934Je ne trouve rien de tel que le rubis, le python, etc. mais cela me semble plus proche
La lisibilité est meilleure à mon avis, 4 lignes sont excessives pour déclarer vos noms de paramètres. Ressemble également aux langues modernes.
la source
a=$1 b=$2 ...
fonctionne aussi bien.