2009-04-27 12 views
5

Je me perds totalement dans la programmation shell, principalement parce que chaque site que j'utilise offre un outil différent pour faire la correspondance des modèles. Donc, ma question est de savoir quel outil utiliser pour effectuer un appariement simple dans un flux canalisé.texte correspondant entre guillemets (débutant)

context: J'ai un fichier named.conf, et j'ai besoin de tous les noms de zones dans un fichier simple pour un traitement ultérieur. Donc je fais ~ $ cat named.local | grep zone et se perdre totalement ici. Ma sortie est ~ une centaine de nouvelles lignes dans la forme "zone" domain.tld "{'et j'ai besoin de texte entre guillemets.

Merci d'avoir montré un moyen de le faire.

J

Répondre

23

Je pense que ce que vous cherchez est sed ... c'est un de tream ed Itor qui vous permettra de faire des remplacements sur une base ligne par ligne.

Comme vous l'expliquez, la commande `cat named.local | zone grep » vous donne une sortie un peu comme ceci:

zone "domain1.tld" { 
zone "domain2.tld" { 
zone "domain3.tld" { 
zone "domain4.tld" { 

Je devine que vous voulez que la sortie soit quelque chose comme ça, puisque vous avez dit que vous avez besoin le texte entre guillemets:

"domain1.tld" 
"domain2.tld" 
"domain3.tld" 
"domain4.tld" 

donc, en réalité, de chaque ligne, nous voulons que le texte entre les guillemets doubles (y compris les guillemets doubles eux-mêmes.)

Je ne suis pas sûr que vous êtes familier avec Regular Expressions, mais ils sont un outil précieux pour toute personne qui écrit des scripts shell. Par exemple, l'expression régulière /.o.e/ correspondrait à n'importe quelle ligne où il y a un mot avec la 2ème lettre était une minuscule o et la 4ème était e. Cela correspondrait à chaîne contenant des mots comme « zone », « tone », ou même « I am tone-deaf. »

L'astuce était d'utiliser le caractère . (point) pour signifier « toute lettre ». Il y a quelques autres caractères spéciaux, tels que * qui signifie "répéter le caractère précédent 0 fois ou plus". Ainsi, une expression régulière comme a* correspondrait à « a », « aaaaaaa », ou une chaîne vide: « »

vous pouvez donc correspondre à la chaîne dans les citations en utilisant: /".*"/

Il y a une autre chose que vous sauriez sed (et par les commentaires, vous le faites déjà!) - Il permet retour arrière. Une fois que vous lui avez dit comment reconnaître un mot, vous pouvez l'utiliser dans le cadre du remplacement. Par exemple, disons que vous vouliez tourner cette liste:

Billy "The Kid" Smith 
Jimmy "The Fish" Stuart 
Chuck "The Man" Norris 

Dans cette liste:

The Kid 
The Fish 
The Man 

D'abord, vous auriez l'air de la chaîne à l'intérieur des guillemets. Nous l'avons déjà vu, c'était /".*"/.

Ensuite, nous voulons utiliser ce qu'il y a dans les guillemets.Nous pouvons groupe à l'aide parens: /"(.*)"/

Si nous voulions remplacer le texte avec les guillemets avec un trait de soulignement, nous ferions un remplacement: s/"(.*)"/_/, et qui nous laisse avec:

Billy _ Smith 
Jimmy _ Stuart 
Chuck _ Norris 

Mais nous avons fait marche arrière! Cela nous permet de nous souvenir de ce qui se trouvait à l'intérieur des parens, en utilisant le symbole \1. Donc, si nous le faisons maintenant: s/"(.*)"/\1/ nous obtenons:

Billy The Kid Smith 
Jimmy The Fish Stuart 
Chuck The Man Norris 

Parce que les citations ne sont pas dans les parens, ils ne faisaient pas partie du contenu de \1!

Pour ne laisser que le contenu entre guillemets, nous devons faire correspondre toute la ligne. (. Ce qui signifie « fin de ligne ») Pour ce faire, nous avons ^ (qui signifie « début de la ligne »), et $

Alors maintenant, si nous utilisons s/^.*"(.*)".*$/\1/, nous obtenons:

The Kid 
The Fish 
The Man 

Pourquoi? Lisons l'expression régulière de s/^.*"(.*)".*$/\1/ à droite à gauche:

  • s/ - Débute une substitution expression régulière
  • ^ - Recherchez le début de la ligne. Commencez à partir de là.
  • .* - Continuez, lire tous les caractères, jusqu'à ce que ...
  • " - ... jusqu'à un guillemet.
  • ( - commencez par un groupe de caractères que nous pourrions souhaiter rappeler plus tard lors du retour arrière.
  • .* - Continuez, lire tous les caractères, jusqu'à ce que ...
  • ) - (! Pssst fermer le groupe)
  • " - ... jusqu'à un guillemet.
  • .* - Continuez, lire tous les caractères, jusqu'à ce que ...
  • $ - La fin de la ligne!

  • / - utiliser ce qui est après cela pour remplacer ce que vous

  • jumelé
  • \1 - coller le contenu du premier groupe (ce qui était dans les parens) apparié.
  • / - fin d'expression régulière

En clair:. « Lire toute la ligne, la copie de côté le texte entre les guillemets doubles remplacer ensuite toute la ligne avec le contenu entre les doubles qoutes."

Vous pouvez même ajouter des guillemets autour du texte remplaçant s/^.*"(.*)".*$/"\1"/, nous allons donc obtenir:

"The Kid" 
"The Fish" 
"The Man" 

et qui peut être utilisé par sed pour remplacer la ligne avec le contenu à l'intérieur des guillemets:

sed -e "s/^.*\"\(.*\)\".*$/\"\1\"/" 

(Ceci est juste coquille échappé à traiter les guillemets doubles et des barres obliques et d'autres choses.)

donc toute la commande wo ULD quelque chose comme:

cat named.local | grep zone | sed -e "s/^.*\"\(.*\)\".*$/\"\1\"/" 
+0

Eh oui, je l'utilise en ce moment, mais je pense qu'il devrait être plus facile de le faire, parce que maintenant j'utilise -e 's/zone « // g sed | sed -e' s /" { // g 'pour supprimer le début et la fin d'un fichier au lieu de simplement correspondre au milieu. – jpou

+1

Raser le début et la fin est parfaitement acceptable. Ce n'est pas un concours - si ça marche, ça va. Si vous voulez le faire en faisant correspondre le texte entre guillemets, jetez un oeil à 'capturer des groupes'. – zoul

+0

J'ai passé trop de temps à taper dessus, et ce n'est toujours pas fait ... il semble que tout le monde m'a battu. Mais je suis content que vous l'ayez déjà compris :-) – scraimer

1

1.

[email protected]:etc$ cat named.conf | grep zone 
zone "." IN { 
zone "localhost" IN { 
    file "localhost.zone"; 
zone "0.0.127.in-addr.arpa" IN { 

2.

[email protected]:etc$ cat named.conf | grep ^zone 
zone "." IN { 
zone "localhost" IN { 
zone "0.0.127.in-addr.arpa" IN { 

3.

[email protected]:etc$ cat named.conf | grep ^zone | sed 's/.*"\([^"]*\)".*/\1/' 
. 
localhost 
0.0.127.in-addr.arpa 

Le regexp est .*"\([^"]*\)".*, qui correspond:

  1. un nombre quelconque de caractères: .*
    • une citation: "
    • commence à se rappeler plus tard: \(
    • tous les personnages sauf citation: [^"]*
    • se termine groupe à retenir: \)
    • devis de clôture : "
    • et n'importe quel nombre de caractères: .*

Lorsque vous appelez sed, la syntaxe est 's/what_to_match/what_to_replace_it_with/'. Les guillemets simples sont là pour empêcher votre expansé d'être étendu par bash. Lorsque vous vous «souvenez» de quelque chose dans l'expression rationnelle en utilisant des parens, vous pouvez le rappeler comme \1, \2 etc .. Fiddle avec lui pendant un certain temps.

2

Eh bien, personne n'a mentionné cut encore, donc, de prouver qu'il ya plusieurs façons de faire quelque chose avec la coquille:

% grep '^zone' /etc/bind/named.conf | cut -d' ' -f2 
"gennic.net" 
"generic-nic.net" 
"dyn.generic-nic.net" 
"langtag.net" 
0

Tant que quelqu'un pointe sur sed/awk, je vais souligner que grep est redondant. Cela vous donne ce que vous cherchez sans les guillemets (déplacez les guillemets dans les parenthèses pour les conserver).En awk, il est encore plus simple avec les guillemets:

awk '/^zone/{print $2}' /etc/bind/named.conf 

J'essaie d'éviter les pipelines autant que possible (mais pas plus). Rappelez-vous, Don't pipe cat. Ce n'est pas nécessaire. Et, dans la mesure où awk et sed dupliquent le travail de grep, n'utilisez pas grep non plus. Au moins, pas en sed ou awk.

Personnellement, j'aurais probablement utilisé perl. Mais c'est parce que j'aurais probablement fait le reste de tout ce que vous faites en Perl, ce qui en fait un détail mineur (et être capable de slurper le fichier entier et regex contre tout simultanément, ignorer \ n serait un bonus pour les cas où Je ne contrôle pas/etc/bind, comme sur un hébergeur partagé). Mais, si je devais le faire en coquille, l'un des deux ci-dessus serait la façon dont je l'aborderais.

Questions connexes