Avec /bin/bash
, comment pourrais-je détecter si un utilisateur a un répertoire spécifique dans sa variable $ PATH?
Par exemple
if [ -p "$HOME/bin" ]; then
echo "Your path is missing ~/bin, you might want to add it."
else
echo "Your path is correctly set"
fi
Quelque chose de vraiment simple et naïf:
echo "$PATH"|grep -q whatever && echo "found it"
Où que soit ce que vous recherchez. Au lieu de &&
, vous pouvez mettre $?
dans une variable ou utiliser une instruction if
appropriée.
Les limitations incluent:
Ou en utilisant un Perl one-liner:
Perl -e 'exit(!(grep(m{^/usr/bin$},split(":", $ENV{PATH}))) > 0)' && echo "found it"
Cela a toujours la limitation qu'il ne fera aucune expansion du shell, mais n'échouera pas si une sous-chaîne correspond. (Ce qui précède correspond à "/usr/bin
", au cas où ce n'était pas clair).
Utiliser grep
est excessif et peut causer des problèmes si vous recherchez quelque chose qui inclut des métacaractères RE. Ce problème peut être résolu parfaitement avec la commande [[
intégrée de bash:
if [[ ":$PATH:" == *":$HOME/bin:"* ]]; then
echo "Your path is correctly set"
else
echo "Your path is missing ~/bin, you might want to add it."
fi
Notez que l'ajout de deux points avant le développement de $ PATH et le chemin à rechercher résolvent le problème de correspondance de sous-chaîne; la double cotation du chemin évite les problèmes de méta-caractères.
Voici comment le faire sans grep
:
if [[ $PATH == ?(*:)$HOME/bin?(:*) ]]
La clé ici est de rendre les deux-points et les caractères génériques facultatifs à l'aide de la construction ?()
. Il ne devrait y avoir aucun problème avec les métacaractères dans ce formulaire, mais si vous voulez inclure des guillemets, c'est ici qu'ils vont:
if [[ "$PATH" == ?(*:)"$HOME/bin"?(:*) ]]
C'est une autre façon de le faire en utilisant l'opérateur de correspondance (=~
), de sorte que la syntaxe ressemble davantage à celle de grep
:
if [[ "$PATH" =~ (^|:)"${HOME}/bin"(:|$) ]]
Il n’est absolument pas nécessaire d’utiliser des utilitaires externes tels que grep
pour cela. Voici ce que j'ai utilisé, qui devrait être portable, même pour les versions héritées du Bourne Shell.
case :$PATH: # notice colons around the value
in *:$HOME/bin:*) ;; # do nothing, it's there
*) echo "$HOME/bin not in $PATH" >&2;;
esac
J'ai écrit la fonction Shell suivante pour signaler si un répertoire est répertorié dans la PATH
actuelle. Cette fonction est compatible POSIX et s'exécutera dans des interpréteurs compatibles, tels que Dash et Bash (sans utiliser les fonctionnalités spécifiques à Bash).
Il inclut des fonctionnalités pour convertir un chemin relatif en un chemin absolu. Il utilise les utilitaires readlink
ou realpath
pour cela, mais ces outils ne sont pas nécessaires si le répertoire fourni n'a pas ..
ou d'autres liens en tant que composants de son chemin. En dehors de cela, la fonction ne nécessite aucun programme externe au shell.
# Check that the specified directory exists – and is in the PATH.
is_dir_in_path()
{
if [ -z "${1:-}" ]; then
printf "The path to a directory must be provided as an argument.\n" >&2
return 1
fi
# Check that the specified path is a directory that exists.
if ! [ -d "$1" ]; then
printf "Error: ‘%s’ is not a directory.\n" "$1" >&2
return 1
fi
# Use absolute path for the directory if a relative path was specified.
if command -v readlink >/dev/null ; then
dir="$(readlink -f "$1")"
Elif command -v realpath >/dev/null ; then
dir="$(realpath "$1")"
else
case "$1" in
/*)
# The path of the provided directory is already absolute.
dir="$1"
;;
*)
# Prepend the path of the current directory.
dir="$PWD/$1"
;;
esac
printf "Warning: neither ‘readlink’ nor ‘realpath’ are available.\n"
printf "Ensure that the specified directory does not contain ‘..’ in its path.\n"
fi
# Check that dir is in the user’s PATH.
case ":$PATH:" in
*:"$dir":*)
printf "‘%s’ is in the PATH.\n" "$dir"
return 0
;;
*)
printf "‘%s’ is not in the PATH.\n" "$dir"
return 1
;;
esac
}
La partie utilisant :$PATH:
garantit que le modèle correspond également si le chemin souhaité est la première ou la dernière entrée de la PATH
. Cette astuce est basée sur cette réponse de Glenn Jackman sous Unix & Linux .
$PATH
est une liste de chaînes séparées par :
décrivant une liste de répertoires. Un répertoire est une liste de chaînes séparées par /
. Deux chaînes différentes peuvent pointer vers le même répertoire (comme $HOME
et ~
, ou /usr/local/bin
et /usr/local/bin/
). Nous devons donc fixer les règles de ce que nous voulons comparer/vérifier. Je suggère de comparer/vérifier les chaînes entières, et non les répertoires physiques, mais supprimer les /
dupliqués et finaux.
Commencez par supprimer les /
en double et fin de $PATH
:
echo $ PATH | tr -s/| sed 's/\ /: /:/g; s /:/\ n/g'
Supposons maintenant que $d
contienne le répertoire que vous voulez vérifier. Ensuite, dirigez la commande précédente pour vérifier $d
dans $PATH
.
echo $ PATH | tr -s/| sed 's/\ /: /:/g; s /:/\ n/g' | grep -q "^ $ d $" || echo "missing $ d"
C'est une approche de force brute, mais cela fonctionne dans tous les cas, sauf lorsqu'une entrée de chemin contient deux points. Et aucun programme autre que Shell n’est utilisé.
previous_IFS=$IFS
dir_in_path='no'
export IFS=":"
for p in $PATH
do
[ "$p" = "/path/to/check" ] && dir_in_path='yes'
done
[ "$dir_in_path" = "no" ] && export PATH="$PATH:/path/to/check"
export IFS=$previous_IFS