Dans mon répertoire personnel, j'ai un dossier drupal-6.14 qui contient la plate-forme Drupal.
À partir de ce répertoire, j'utilise la commande suivante:
find drupal-6.14 -type f -iname '*' | grep -P 'drupal-6.14/(?!sites(?!/all|/default)).*' | xargs tar -czf drupal-6.14.tar.gz
Ce que cette commande fait est gzips le dossier drupal-6.14, excluant tous les sous-dossiers de drupal-6.14/sites / sauf sites/all et sites/default , qu'il inclut.
Ma question porte sur l'expression régulière:
grep -P 'drupal-6.14/(?!sites(?!/all|/default)).*'
L'expression fonctionne pour exclure tous les dossiers que je veux exclure, mais je ne comprends pas très bien pourquoi.
Il s'agit d'une tâche courante utilisant des expressions régulières pour
Correspond à toutes les chaînes, sauf celles qui ne contiennent pas le sous-modèle x. Ou en d'autres termes, nier un sous-modèle.
Je (pense) que je comprends que la stratégie générale pour résoudre ces problèmes est l'utilisation d'anticipations négatives, mais je n'ai jamais compris à un niveau satisfaisant le fonctionnement des regards positifs et négatifs (avant/arrière).
Au fil des ans, j'ai lu de nombreux sites Web dessus. Les manuels PHP et Python regex, d'autres pages comme http://www.regular-expressions.info/lookaround.html et ainsi de suite, mais je n'ai jamais vraiment vraiment compris cela.
Quelqu'un pourrait-il expliquer comment cela fonctionne et peut-être fournir des exemples similaires qui feraient des choses similaires?
- Mise à jour un:
En ce qui concerne la réponse d'Andomar: une double anticipation négative peut-elle être exprimée plus succinctement en une seule affirmation positive:
c'est-à-dire:
'drupal-6.14/(?!sites(?!/all|/default)).*'
équivalent à:
'drupal-6.14/(?=sites(?:/all|/default)).*'
???
- Deuxième mise à jour:
Selon @andomar et @alan moore - vous ne pouvez pas échanger la double recherche négative pour la recherche positive.
Une anticipation négative indique qu'à cette position, l'expression régulière suivante ne peut pas correspondre.
Prenons un exemple simplifié:
a(?!b(?!c))
a Match: (?!b) succeeds
ac Match: (?!b) succeeds
ab No match: (?!b(?!c)) fails
abe No match: (?!b(?!c)) fails
abc Match: (?!b(?!c)) succeeds
Le dernier exemple est un double négation: il permet un b
suivi de c
. La tête de lecture négative imbriquée devient une tête de lecture positive: le c
doit être présent.
Dans chaque exemple, seul le a
est mis en correspondance. Le lookahead n'est qu'une condition et n'ajoute rien au texte correspondant.
Les contournements peuvent être imbriqués.
Donc, cette expression régulière correspond à "drupal-6.14 /" qui est pas suivi de "sites" qui est pas suivi de "/ all" ou "/ default".
Déroutant? En utilisant des mots différents, nous pouvons dire qu'il correspond à "drupal-6.14 /" qui est pas suivi de "sites" à moins que qui soit suivi par "/ all" ou "/ défaut"
Si vous révisez votre expression régulière comme ceci:
drupal-6.14/(?=sites(?!/all|/default)).*
^^
... alors il correspondra à toutes les entrées qui contiennent drupal-6.14/
suivi de sites
suivi de autre chose que /all
ou /default
. Par exemple:
drupal-6.14/sites/foo
drupal-6.14/sites/bar
drupal-6.14/sitesfoo42
drupal-6.14/sitesall
En changeant ?=
à ?!
pour correspondre à votre regex d'origine annule simplement ces correspondances:
drupal-6.14/(?!sites(?!/all|/default)).*
^^
Donc, cela signifie simplement que drupal-6.14/
maintenant ne peut pas être suivi par sites
suivi par autre chose que /all
ou /default
. Alors maintenant, ces entrées satisferont l'expression régulière:
drupal-6.14/sites/all
drupal-6.14/sites/default
drupal-6.14/sites/all42
Mais, ce qui peut ne pas être évident à partir de certaines des autres réponses (et peut-être de votre question), c'est que votre expression régulière permettra également d'autres entrées où drupal-6.14/
est également suivi par autre chose que sites
. Par exemple:
drupal-6.14/foo
drupal-6.14/xsites
Conclusion: Donc, votre expression rationnelle dit essentiellement d'inclure tous les sous-répertoires de drupal-6.14
sauf les sous-répertoires de sites
dont le nom commence par autre chose que all
ou default
.