Comment vérifier si $ PWD est un sous-répertoire d'un chemin donné

25

Par exemple, vérifiez si $PWDest un sous-répertoire de / home. En d'autres termes, je recherche une opération de chaîne bash pour vérifier si une chaîne commence par une autre.

Tobias Kienzler
la source

Réponses:

26

Si vous voulez tester de manière fiable si un répertoire est un sous-répertoire d'un autre, vous aurez besoin de plus qu'une simple vérification de préfixe de chaîne. La réponse de Gilles décrit en détail comment faire correctement ce test.

Mais si vous voulez une simple vérification du préfixe de chaîne (peut-être que vous avez déjà normalisé vos chemins?), C'est une bonne:

test "${PWD##/home/}" != "${PWD}"

Si $PWDcommence par "/ home /", il est supprimé du côté gauche, ce qui signifie qu'il ne correspond pas au côté droit, donc "! =" Renvoie true.

Jander
la source
1
+1 parfait, et pour un cas plus général: [ "${PWD##$VAR}" != "$PWD" ]Et s'il s'agissait d'un [code-golf] ( stackoverflow.com/questions/tagged/code-golf)question vous m'auriez battu de deux caractères ;-) plus lisible ...
Tobias Kienzler
FWIW, cela peut également être utile:${PWD/#$HOME/\~}
user1338062
D'accord, mais que diriez-vous de PWD = "/ home /../ etc /"
Alexis Peters
1
@AlexisPeters: Vous avez raison: je me concentrais sur la partie "préfixe de chaîne" de la question, et je l'ai modifiée pour que cela soit plus clair. Gilles a bien couvert la bonne façon de vérifier les sous-répertoires.
Jander
1
Permet "${PWD%%/subdir*}"de détecter si l'utilisateur se trouve actuellement dans subdirou dans un sous-répertoire de subdir, car %% capture à partir de la fin de la chaîne au lieu du début. Cela devrait être utile à tous ceux qui recherchent plus d'informations sur la substitution de paramètres: tldp.org/LDP/abs/html/parameter-substitution.html
George Pantazes
33

Pour tester si une chaîne est le préfixe d'une autre, dans n'importe quel shell de style Bourne:

case $PWD/ in
  /home/*) echo "home sweet home";;
  *) echo "away from home";;
esac

Le même principe fonctionne pour un test de suffixe ou de sous-chaîne. Notez que dans les caseconstructions, contrairement aux noms de fichiers, *correspond à n'importe quel caractère, y compris un /ou une initiale ..

Dans les shells qui implémentent la [[ … ]]syntaxe (ie bash, ksh et zsh), elle peut être utilisée pour faire correspondre une chaîne à un modèle. (Notez que la [commande peut uniquement tester l'égalité des chaînes.)

if [[ $PWD/ = /home/* ]]; then 

Si vous testez spécifiquement si le répertoire actuel est en dessous /home, un simple test de sous-chaîne ne suffit pas, à cause des liens symboliques.

S'il /homes'agit d'un système de fichiers qui lui est propre, testez si le répertoire en cours ( .) se trouve sur ce système de fichiers.

if [ "$(df -P . | awk 'NR==2 {print $6}')" = "/home" ]; then
  echo 'The current directory is on the /home filesystem'
fi

Si vous avez NetBSD, OpenBSD ou GNU (c'est-à-dire Linux) readlink, vous pouvez utiliser readlink -fpour supprimer les liens symboliques d'un chemin.

case $(readlink -f .)/ in $(readlink -f /home)/*) 

Sinon, vous pouvez utiliser pwdpour afficher le répertoire actuel. Mais vous devez faire attention à ne pas utiliser un shell intégré si votre shell suit les cdcommandes et conserve le nom que vous avez utilisé pour atteindre le répertoire plutôt que son emplacement «réel».

case $(pwd -P 2>/dev/null || env PWD= pwd)/ in
  "$(cd /home && { pwd -P 2>/dev/null || env PWD= pwd; })"/*) 
Gilles 'SO- arrête d'être méchant'
la source
+1 Merci pour cette réponse exhaustive. Je n'ai pas pris en compte les liens et les particularités du système de fichiers lorsque j'ai posé cette question, mais c'est vraiment bon à savoir
Tobias Kienzler
8

Version brute:

[ ${PWD:0:6} = "/home/" ]

A l'inconvénient que l'on doit d'abord compter les caractères et qu'on ne peut pas les remplacer /home/par quelque chose de général comme $1.

modifier (merci @Michael) pour la généralisation à comparer avec $VARon peut utiliser

[ "${PWD:0:${#VAR}}" = $VAR ]
Tobias Kienzler
la source
4
${#var}est la longueur de $var, vous pouvez donc la généraliser comme[ "${PWD:0:${#1}}" = "$1" ]
Michael Mrozek
@Michael Mrozek ♦: Merci, ça fonctionne parfaitement :)
Tobias Kienzler
2

Je ne comprends pas trop bien la question, mais pour trouver le parent de $ PWD , faites-le dirname $PWD. Pour trouver le parent du parent, exécutez dirname $(dirname $PWD), etc. ...

tshepang
la source
Cela ne fonctionnerait que si je connaissais la profondeur, sinon je me retrouve avec /. D'accord, je pourrais vérifier récursivement, mais n'y a-t-il pas quelque chose de plus facile?
Tobias Kienzler,
1
@tob Si seulement je connaissais la programmation shell ;-)
tshepang
1

En utilisant awk:

echo $PWD | awk -v h="/home" '$0 ~ h {print "MATCH"}'
dogbane
la source
+1 pour le travail, mais un peu plus lent qu'un test bash natif
Tobias Kienzler
0

Hm, c'est dommage de [ne pas avoir la possibilité de tester la STRING1 starts with STRING2condition.

Vous pouvez essayer echo $PWD | grep '^$VAR', mais il peut échouer de manière intéressante lorsqu'il VARcontient des symboles spéciaux.

awkLa indexfonction devrait être capable de faire l'affaire. Mais tout cela semble trop lourd pour une chose aussi simple à tester.

alex
la source
[[ne regexp donc vous commencez avec [[$ PWD = ~ ^ \ / home \ /]]
teknopaul
0

Si la partie recherchée du chemin est trouvée, je "vide" la variable:

[[ -z "${PWD//*\/home\/*/}" ]] && echo "toto"

la source