Je voudrais find et exécuter une command dans le $PATH
courant correspondant à ce libreoffice?.?
générique libreoffice?.?
(par exemple, libreoffice4.3
, libreoffice4.3
, etc.)
EDIT: si plusieurs correspondances sont trouvées, vous pouvez en choisir une au hasard.
Je préfère une solution compatible POSIX.
Définissez IFS
sur :
pour split la valeur de PATH
sur les deux-points. Si votre find
a l'action -quit
et la primitive -maxdepth
primaire (par exemple FreeBSD, OSX, GNU), vous savez que la command existera et que vous ne vous souciez pas du code de return de la command, vous pouvez utiliser cette seule ligne:
pattern='libreoffice?.?' IFS=:; find $PATH -maxdepth 1 -type f -name "$pattern" -exec {} \; -quit; unset IFS
Cela ne fournit pas un moyen facile de signaler si la command a été trouvée. En outre, pour être plus robuste, désactivez la fonction Globulation si la valeur de PATH
contient des caractères generics. En outre, il est possible d'avoir un composant vide dans PATH
pour signifier le directory courant (mais mon conseil est d'utiliser .
place). Le code ci-dessous prend en charge toutes ces complications.
pattern='libreoffice?.?' case $PATH in :*) directories=.$PATH;; *::*) directories=${PATH%%::*}:.:${PATH#*::};; *:) directories=$PATH.;; *) directories=$PATH;; esac set -f; IFS=: cmd= for d in $directories; do set +f for x in "$d"/$pattern; do if [ -x "$x" ] && ! [ -d "$x" ]; then cmd=$x break fi done if [ -n "$cmd" ]; then break; fi done set +f; unset IFS if [ -z "$cmd" ]; then echo 1>&2 "$pattern: not found in PATH" exit 127 else exec "$cmd" fi
Si vous utilisez zsh (contrairement à sh, bash, ksh, …), il est beaucoup plus simple de créer une solution robuste.
pattern='libreoffice?.?' matches=($^path/$~pattern(N.*[1])) if ((!#matches)); then $matches[1] else echo 1>&2 "$pattern: not found in PATH" exit 127 fi
Avec zsh
:
$commands[(i)libreoffice?.?]
Dans zsh
, $commands
est un tableau associatif spécial dont les keys sont des noms de commands et leur path.
i
ci-dessus est un indicateur d'indice de tableau qui indique à zsh
faire correspondre le model aux keys du tableau et de returnner la première key correspondante.
Les éléments du tableau associatif ne sont pas dans un ordre particulier, donc la première key correspondante ne sera pas nécessairement la première qui se produit dans $PATH
. Si vous voulez le libreoffice
avec le plus grand numéro de version, vous pourriez faire:
${${(nO)${commands[(I)libreoffice?.?]}}[1]}
L'indicateur I
indice est étendu à toutes les keys correspondantes. Nous utilisons les drapeaux d'extension de paramètre n
(sorting numérique) et O
(inverse) pour sortinger cette list du plus grand au plus petit numéro de version, puis [1]
pour sélectionner le premier.
Voir également:
whence -m 'libreoffice?.?'
pour find les paths des commands correspondantes.
Vous pouvez utiliser la command "find" comme suit
find ./ -name "libreoffice?.?"
Voici une alternative plus simple:
$(compgen -c libreoffice)
Il suppose bash, et suppose qu'il n'y a qu'un seul libreoffice*
installé.
Il émule ce que l'exécution de l'onglet bash ferait si vous tapez l' onglet libreoffice
.
Si vous tentiez délibérément d'exclure libreoffice
sans numéro de version et que vous vouliez gérer l'existence de plusieurs versions, essayez:
run_libreoffice() { compgen -c libreoffice | while read -r exe; do case "$exe" in libreoffice?.?) "$exe" "$@" return ;; esac done } run_libreoffice "$@"
La déclaration de case
fait correspondre seulement libreoffice?.?
, et nous bouclons les résultats, en exécutant seulement le premier.
POSIXly, lors de la search d'une command exécutable $PATH
'd, vous devez utiliser la command
:
command -v
Si elle est appelée sans commutateur, la command
invoquera nom_command , mais vous avez toujours besoin d'un path complètement résolu pour résoudre complètement un shell glob .
Donc, pour cela, vous devriez vérifier le glob par rapport à chacun des éléments séparés par deux-points dans $PATH
dans l'ordre, comme spécifié :
PATH
::
, en tant que premier côlon précédant le rest de la list ou en tant que deux-points consécutifs après le rest de la list. Une application ssortingctement conforme doit utiliser un path d'access réel (tel que .
) Pour représenter le directory de travail en cours dans PATH
. La list doit être recherchée du début à la fin, en appliquant le nom de file à chaque préfixe, jusqu'à ce qu'un file exécutable avec le nom spécifié et les permissions d'exécution appropriées soit trouvé. Si le nom de path recherché contient une barre oblique, la search à travers les préfixes de path ne doit pas être effectuée. Si le path commence par une barre oblique, le path spécifié est résolu (voir Résolution du path ) . Si PATH
est désactivé ou défini sur null, la search de path est définie par l'implémentation . Vous pouvez répartir $PATH
dans un champ par $PATH
sur $IFS
, mais vous ne pouvez le faire que si vous set -f
également set -f
extension nom_file comme étant désactivée, au cas où un composant de $PATH
contiendrait [?*
car la génération de nom de file (read: globbing) se produit après la division de champ dans l'ordre d'expansion du shell.
De cette façon, le seul moyen vraiment robuste de faire ce POSIXly est d'abord d'étendre la list de champs sur $IFS
tandis que la génération de nom de file est désactivée et d'save le résultat de tableau, puis de nouveau activer la génération de nom de file et désactiver le champ- splitting (afin que vous puissiez stocker et étendre votre model de glob non guidé dans une variable sans risque d'être affecté par $IFS
premier) et opérer sur les résultats à tour de rôle.
Heureusement, ce n'est pas si difficile:
g='libreoffice?.?' ( IFS=:; set-f #split on :; disable filename gen set +f -- $PATH #store split array, enable filename gen after IFS= #disable field splitting for p do for x in ${p:+"$p/"$g} do command -v "$x" done;done ) #done in subshell to avoid affecting current shell
Les derniers bits ne sont pas commentés car ils sont mieux expliqués ici.
for p do
– La première boucle for
associe de façon itérative $p
la valeur de chaque paramètre de position dans le tableau "$@"
du shell , qui est set
sur l'extension split-champ de $PATH
divisé par :
colons.
for x in ${p:+"$p/"$g}
– La boucle inner for
fixe itérativement $x
à chacune des expansions de ${p:+"$p/"$g}
c'est-à-dire qu'il ne réitère rien du tout à less que $p
soit à la fois défini et non nul. Ceci est important car ::
ou un leader :
splitait en champs nuls, et de cette façon, les deux loops seront ssortingctement conforms à la spécification $PATH
(comme indiqué ci-dessus) en ne reconnaissant pas les champs de longueur nulle .
Il est important de noter qu'à cause de la façon dont $PATH
est divisé alors que la génération de nom de file est désactivée et que "$p/"$g
est résolu alors qu'il est activé mais que le fractionnement de champ a été désactivé, aucun des éléments de $PATH
ne peut les champs sont divisés sur :
(quel que soit les autres caractères qu'ils peuvent contenir) et aucune valeur pour "$p/"$g
étendue à elle-même ou à n'importe quel nom de file (quel que soit le caractère de $p
ou autre les caractères de correspondance de motifs $g
peuvent contenir) .
command
– et la dernière command
imprime uniquement pour normaliser les valeurs de $x
qui sont des commands exécutables.
Voici tout ce qui est enveloppé dans une fonction shell, avec la possibilité supplémentaire de gérer plusieurs arguments $g
:
glob_path()( for g do IFS=:; set -f set +f -- ${PATH:-.}; IFS= for p do for x in ${p:+"$p/"$g} do command -v "$x" done; done; done )
Cela devrait vous procurer une list d'exécutables correspondant à chaque argument que vous avez imprimé, une par ligne commandée en premier par ordre d'arguments, puis par priorité $PATH
et enfin par ordre de sorting de vos parameters régionaux. Il ne devrait pas échouer sur les nouvelles lignes ou les caractères spéciaux dans les paths ou les templates de globes que vous le remettez. Il pourrait être modifié (voir l'historique des modifications ici) pour remplir fidèlement le tableau "$@"
du shell à un exécutable par paramètre de position.
Vous pouvez également le modifier pour exécuter la première correspondance de glob réussie et quitter la search de cet argument glob particulier et passer au suivant si la command se termine avec succès comme …
glob_path()( for g do IFS=:; set -f set +f -- ${PATH:-.}; IFS= for p do for x in ${p+:"$p/"$g} do command -v "$x" && command "$x" && break 2 done; done; done )
C'est un moyen POSIX portable de find votre match glob, mais certains shells n'ont même pas besoin de la seconde for g...
loop. En bash
, par exemple, cela fonctionnera aussi comme …
glob_path()( for g do IFS=:; set -f set +f -- ${PATH:-.}; IFS= for p do command -v ${p:+"$p/"$g} done; done )
… qui répertorie toujours toutes les correspondances globes exécutables pour tous les arguments et pour chaque élément de $PATH
que l'un de ces éléments ou glob contienne des lignes nouvelles ou des caractères spéciaux, etc.
Vous pouvez l'utiliser comme:
glob_path '?sh' '??sh' #I'd rather not install libreoffice
… qui pour moi imprime …
/usr/bin/ksh /usr/bin/rsh /usr/bin/ssh /usr/bin/zsh /usr/local/bin/dash /usr/local/bin/yash /usr/bin/bash /usr/bin/bssh /usr/bin/chsh /usr/bin/dash /usr/bin/mksh /usr/bin/posh /usr/bin/slsh /usr/bin/yash
Comme nous l' glob_path
vu, la fonction glob_path
interprète la variable d'environnement $PATH
comme spécifiant qu'une application ssortingctement conforme doit ignorer les occurrences de glob_path
, de fin ou consécutives dans la $PATH
de $PATH
, mais interprète un $PATH
vide ou non $PATH
pour signifier .
.
Si vous vouliez cependant cette fonctionnalité héritée, la fonction pourrait être écrite:
glob_path()( for g do IFS=:; set -f set +f -- ${0+$PATH:}; IFS= for p do for x in "${p:-.}/"$g do command -v "$x" done; done; done )
… qui interpréterait toutes les occurrences de l'interligne principal, du second interligne ou du deux-deux consécutives .
(et ainsi searchr autant de fois qu'un composant $PATH
longueur nulle apparaît dans la $PATH
de $PATH
) , mais ce n'est pas ssortingctement conforme.