Grep pour motif au début ou au milieu d'une ligne

9

Je commencerai par dire que je pense que ce problème est un peu moins innocent qu'il n'y paraît.

Ce que je dois faire: rechercher un dossier dans la variable d'environnement PATH. Cela pourrait être au début ou quelque part après. J'ai juste besoin de vérifier que ce dossier est là.

Exemple de mon problème - utilisons-le /opt/gnome.


SCÉNARIO 1: le dossier n'est pas au début de PATH

# echo "$PATH"
/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome

# echo "$PATH" | grep ":/opt/gnome"
/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome

Notez que le grep doit être suffisamment spécifique pour ne pas se bloquer /var/opt/gnome. D'où le colon.


SCENARIO 2: le dossier est au début de PATH.

# echo "$PATH"
/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome

# echo "$PATH" | grep "^/opt/gnome"
/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome

C'est mon problème - je dois rechercher un deux-points ou un début de ligne avec ce dossier. Ce que je voudrais faire, c'est l'une de ces deux expressions entre crochets:

# echo $PATH | grep "[^:]/opt/gnome"
# echo $PATH | grep "[:^]/opt/gnome"

MAIS [^et [:ont leurs propres significations. Par conséquent, les deux commandes ci-dessus ne fonctionnent pas.

Existe-t-il un moyen de grep pour ces deux scénarios dans une seule commande?

JamesL
la source
Notez que le commentaire de Gilles sur la réponse de Costas s'applique également à la question: puisque vous n'attendez pas pour /opt/gnome:ou /opt/gnome$, vous trouverez /opt/gnome-fooou /opt/gnome/bar.
Scott
@Scott - Tant que vous incluez dans votre match l'espace intermédiaire, vous pouvez toujours ancrer n'importe quelle corde à la tête et à la queue de la ligne sans de telles complications. Tout commegrep '^\(any number of other matches:*:\)*my match\(:.*\)*$'
mikeserv

Réponses:

10

Si vous vérifiez le contenu de la PATHvariable d'environnement, au lieu de rechercher quelque chose dans un fichier, grepc'est le mauvais outil. Il est plus facile (et plus rapide et sans doute plus lisible) de le faire dans le shell.

En bash, ksh et zsh:

if [[ :$PATH: = *:/opt/gnome:* ]]; then
 : # already there
else
  PATH=$PATH:/opt/gnome
fi

Portablement:

case :$PATH: in
  *:/opt/gnome:*) :;; # already there
  *) PATH=$PATH:/opt/gnome;;
esac

Notez l'utilisation de :$PATH:plutôt que $PATH; de cette façon, le composant est toujours entouré de deux-points dans la chaîne de recherche, même s'il était au début ou à la fin de $PATH.

Si vous recherchez dans une ligne d'un fichier, vous pouvez utiliser l'expression rationnelle étendue (c'est-à-dire nécessitant grep -E) (^|:)/opt/gnome($|:)pour correspondre, /opt/gnomemais uniquement si elle se trouve soit au début d'une ligne, soit à la suite d'un signe deux-points, et uniquement si elle est soit à la fin de la ligne ou suivie par deux points.

Gilles 'SO- arrête d'être méchant'
la source
8

Vous pouvez utiliser des expressions régulières étendues en utilisant simplement grep -E

Vous devez faire correspondre le début et la fin du chemin que vous essayez de trouver si vous voulez éviter les faux positifs.

Correspond à l'instance au début:

$ TEST=/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome
$ echo $TEST | grep -E "(:|^)/opt/gnome(:|$)"
/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome

Correspond également à l'instance au milieu:

$ TEST=/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome
$ echo $TEST | grep -E "(:|^)/opt/gnome(:|$)"
/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome

Éviter les faux positifs:

$ TEST="/home/bob/opt/gnome:/opt/gnome/somethingelse:/opt/gnome-beta"
$ echo $TEST | grep -E "(:|^)/opt/gnome(:|$)"

Aucun match là-bas.

Compact et élégant. Testé sur Debian 7.

Luis Antolín Cano
la source
1
egrepest l' utilisation obsolète grep -E(source: man grep)
Anthon
Merci, fonctionne comme un charme! Je ne l'ai pas choisi comme réponse, car je pense que l'option -w est un peu plus simple. Encore plus simple que je ne l'avais imaginé à l'origine!
JamesL
3
Attention. L' -woption a quelques problèmes. Seuls les chiffres, les lettres et le trait de soulignement sont considérés comme des "mots". Donc, certains caractères inhabituels mais possibles feront échouer. Exemple echo '/sbin:/usr/sbin:/var-/opt/gnome' | grep -w "/opt/gnome"et echo '/sbin:/usr/sbin:/var./opt/gnome' | grep -w "/opt/gnome". Ceux-ci donnent de mauvais résultats.
Luis Antolín Cano
1
Vous êtes sur la bonne voie, mais il y a des points positifs encore faux: /opt/gnome/somethingelse.
Gilles 'SO- arrête d'être méchant'
1
Tout à fait raison. Nous devons nous préoccuper explicitement de la fin, pas seulement du début. Je pense que cela résout les problèmes echo "/home/bob/opt/gnome:/opt/gnome/somethingelse:/opt/gnome-beta" | grep -E "(:|^)/opt/gnome(:|$)". Modification de la réponse.
Luis Antolín Cano
7

Si vous n'êtes pas marié grep, vous pouvez utiliser awket séparer les enregistrements sur:

awk 'BEGIN {RS=":"} /^\/opt\/gnome$/'
jasonwryan
la source
5

Vous pouvez également utiliser

echo "$PATH" | tr ':' '\n' | grep -x "/opt/gnome"

qui divise la variable de chemin en lignes distinctes (une par chemin), donc grep -xpeut rechercher des résultats exacts. Cela présente bien sûr l'inconvénient de nécessiter un processus supplémentaire tr. Et cela ne fonctionnera pas lorsqu'un nom de dossier PATHcontient des caractères de nouvelle ligne.

TBrandt
la source
2

Je ne sais pas si c'est suffisant pour répondre mais

grep -w "/opt/gnome"

satisfera votre besoin.

echo '/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome' | grep -w "/opt/gnome" -o
/opt/gnome
echo '/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome' | grep -w "/opt/gnome" -o
/opt/gnome

mais

echo '/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome' | grep "/opt/gnome" -o
/opt/gnome
/opt/gnome
Costas
la source
Cela fonctionne parfaitement car les deux-points sont des caractères autres que des mots. Merci!
JamesL
@ Sman865 Il y a une autre raison: car /ne fait pas partie du mot mais l' rest.
Costas
2
Attention. Comme je l'ai dit lors du commentaire de ma réponse. Il existe des caractères légaux pour un nom de répertoire qui ne sont pas des caractères de mot. Cela conduit à de mauvais résultats. Il n'est pas habituel de terminer un nom de répertoire - mais cela pourrait arriver.
Luis Antolín Cano
4
@ Sman865 faux positifs: /opt/gnome-beta, /home/bob/opt/gnome, ...
Gilles de arrêt SO- être mal »
Cas non fonctionnel: grep -w /usr/local -o <<< /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games------/usr/local /usr/local /usr/local
pabouk
0

Pour sélectionner /opt/gnomeentouré par des caractères non-mot (nouvelles lignes, :, /, etc.) essayez celui - ci:

grep '\B/opt/gnome'
jimmij
la source
0

Vous pouvez le faire de manière fiable et avec peu d'effort grep. Vous pouvez profiter des extensions qui sont largement disponibles et parmi lesquelles de nombreuses solutions ont déjà été proposées, mais même avec des regex de base, cela se fait facilement, même si cela ne l'est pas intuitivement à première vue.

Avec regex de base - et donc avec grep- vous avez toujours deux ancres fiables - la tête et la queue de la ligne. Vous pouvez ancrer une correspondance à ces deux éléments indépendamment de son emplacement sur la ligne, comme:

grep '^\(ignore case, delimiter\)*match\(delimiter, ignore case\)*$'

grepcorrespondra de la tête de la ligne autant d'occurrences des \(grouped\)sous - expressions qu'il le faut pour rencontrer ensuite votre délimiteur puis votre correspondance explicite, et de la queue de votre correspondance à la queue de la ligne de la même manière. Si votre correspondance explicite ne correspond pas explicitement, elle échouera et n'imprimera rien.

Et vous pourriez donc faire, par exemple:

grep '^\(.*:\)*/opt/gnome\(:.*\)*$'

Voir par vous-même:

grep '^\(.*:\)*/opt/gnome\(:.*\)*$
' <<\INPUT
/opt/gnome-beta
/opt/gnome
/home/bob/opt/gnome
:/opt/gnome:
/home/bob/opt/gnome:/opt/gnome:/opt/gnome-beta
/opt-gnome-beta
/opt/gnomenot::::/opt/gnome
INPUT

PRODUCTION

/opt/gnome
:/opt/gnome:
/home/bob/opt/gnome:/opt/gnome:/opt/gnome-beta
/opt/gnomenot::::/opt/gnome
mikeserv
la source
0

vous avez remarqué un cas de bord ... vous pouvez l'éviter en forçant l'apparition d'un: au début de la ligne:

 echo ":$PATH" | grep ":/opt/gnome"

ou si le chemin est exact, ajoutez-en également un à la fin pour vous assurer qu'il est délimité:

 echo ":${PATH}:" | grep ":/opt/gnome:"
Olivier Dulac
la source