web-dev-qa-db-fra.com

Trouver des fichiers avec un motif spécifique de 2 lignes en utilisant awk

J'ai quelques centaines de fichiers qui ont ce modèle

@<TRIPOS>ATOM
  2 H18 65.2220 Du 1 RES1 0.0000
@<TRIPOS>BOND
 1  3  5  ar
@<TRIPOS>SUBSTRUCTURE

parmi eux, certains fichiers ne contiennent pas la ligne après le @<TRIPOS>BOND et ils ressemblent à

@<TRIPOS>ATOM
  2 H18 65.2220 Du 1 RES1 0.0000
@<TRIPOS>BOND
@<TRIPOS>SUBSTRUCTURE

J'essaie de trouver tous les fichiers de mon répertoire de travail qui ne contiennent pas la ligne numérique après le @<TRIPOS>BOND et de les déplacer dans un autre répertoire. Je sais que la tâche est simple, mais je suis assez nouveau pour Linux.

Remarque: les fichiers varient en longueur et en nombre de lignes. C’est pourquoi je "grepse" la ligne après la chaîne @<TRIPOS>BOND.

Voici l'un de mes codes, que je prévoyais d'écrire dans une boucle for. Cela ne fait pas le travail, mais je le montre pour montrer l'un de mes essais.

cat file | grep -A1 '@<TRIPOS>BOND' | awk 'FNR == 2 {print}'

Je vous remercie

3
Error404

Si votre version de grep prend en charge le mode PCRE (-P), vous pouvez essayer une correspondance sur plusieurs lignes permettant de trouver des instances de @<TRIPOS>BOND suivies (après seulement une nouvelle ligne) par @<TRIPOS>SUBSTRUCTURE e.g.

grep -lzP '\Q@<TRIPOS>BOND\E\n\Q@<TRIPOS>SUBSTRUCTURE\E' *

Les \Q et \E peuvent être inutiles dans ce cas, mais sont destinés à forcer la correspondance littérale (dans le cas où @, >, < ont une signification particulière Syntaxe de regex Perl). -l indique à grep de répertorier les fichiers correspondants au lieu d'imprimer la correspondance. Vous pouvez ensuite utiliser la liste de fichiers comme entrée de la commande mv, par exemple.

grep -lzP '\Q@<TRIPOS>BOND\E\n\Q@<TRIPOS>SUBSTRUCTURE\E' * | xargs mv -t /path/to/newdir/


Information additionnelle

Vous pouvez exprimer la seconde partie du match sous forme de lookahead mais je ne pense pas que cela présente un avantage dans ce cas.

grep -lzP '\Q@<TRIPOS>BOND\E\n(?=\Q@<TRIPOS>SUBSTRUCTURE\E)' *

Les expressions équivalentes dans pcregrep (qui ne fait pas partie du système Ubuntu standard, mais peut être obtenu à partir du référentiel) ressemblent à quelque chose comme:

pcregrep -lM '\Q@<TRIPOS>BOND\E\n\Q@<TRIPOS>SUBSTRUCTURE\E' *

et

pcregrep -lM '\Q@<TRIPOS>BOND\E\n(?=\Q@<TRIPOS>SUBSTRUCTURE\E)' *
6
steeldriver

Que diriez-vous

for file in *.txt; do 
    grep -A1 "@<TRIPOS>BOND" "$file" | grep -q SUBSTR && mv "$file" bad_files/
done

Explication:

Cela va parcourir tous les fichiers .txt du répertoire en cours (changer le blob en fonction de ce qui correspond à vos fichiers) et enregistrer chacun sous le nom $file. Il recherchera ensuite $file pour @<TRIPOS>BOND et l’imprimera ainsi que la ligne suivante. Ceci est passé par la prochaine grep qui silencieusement (-q) cherche SUBSTR, si elle le trouve, cela signifie que la ligne après BOND est SUBSTRUCTURE et non la ligne numérique souhaitée, le fichier en cours sera déplacé vers le dossier bad_files.

3
terdon

La tâche est assez simple avec awk. Voici mon exemple. J'ai créé deux fichiers file-nm (pour les fichiers manquants) et file-m (pour les fichiers manquants) et le répertoire moved pour les fichiers à déplacer.

awk '/@<TRIPOS>BOND/ {getline; if ($0 == "@<TRIPOS>SUBSTRUCTURE" ) system("mv \""FILENAME"\" moved")}' file-nm file-m

Ici nous trouvons la chaîne @<TRIPOS>BOND, sautons dans la ligne suivante et vérifions si cette ligne est @<TRIPOS>SUBSTRUCTURE. Si c'est le cas, nous appelons système avec "mv" le nom du fichier qui a été trouvé et "déplacé" comme destination. Voici le résultat:

$ ls
file-m  file-nm  moved


$ awk '/@<TRIPOS>BOND/ {getline; if ($0 == "@<TRIPOS>SUBSTRUCTURE" ) system("mv \""FILENAME"\" moved")}' file-nm file-m      


$ ls                                                                                                                     
file-nm  moved


$ ls moved                                                                                                               
file-m
0
nawk '/^@<TRIPOS>BOND/{getline;if( $0 ~ /^@/){print "mv", FILENAME, "../NewLoc/"}}' * | bash
0
Richard Romanus

Utilisation de awk ou gawk

awk '/@<TRIPOS>BOND/,/@/ {getline; if ($_ ~ /^@/) {printf "%s:%s\n",$_,FILENAME; system ("mv \""FILENAME"\" <bad_files>/$(basename \""FILENAME"\")")} exit}' <file_name>

Explication

  • /@<TRIPOS>BOND/,/@/

    Nous n'avons besoin que du bloc entre @<TRIPOS>BOND et la ligne suivante commençant par @

  • getline

    Lire la ligne suivante après @<TRIPOS>BOND

  • if ($_ ~ /^@/)

    Vérifiez si la ligne commence par un @

    • true

      Imprimer un message

      printf "%s:%s\n",$_,FILENAME
      

      Déplacer le fichier

      system ("mv \""FILENAME"\" <bad_files>/$(basename \""FILENAME"\")")
      
    • false

      Laisser le script

      exit
      

Exemple

$ cat foo
@<TRIPOS>ATOM
  2 H18 65.2220 Du 1 RES1 0.0000
@<TRIPOS>BOND
@<TRIPOS>SUBSTRUCTURE

$ cat bar
@<TRIPOS>ATOM
  2 H18 65.2220 Du 1 RES1 0.0000
@<TRIPOS>BOND
 1  3  5  ar
@<TRIPOS>SUBSTRUCTURE

$ awk '/@<TRIPOS>BOND/,/@/ {getline; if ($_ ~ /^@/) {printf "%s:%s\n",$_,FILENAME; system ("mv \""FILENAME"\" \""FILENAME"\"_moved")} exit}' foo
@<TRIPOS>SUBSTRUCTURE:foo

$ awk '/@<TRIPOS>BOND/,/@/ {getline; if ($_ ~ /^@/) {printf "%s:%s\n",$_,FILENAME; system ("mv \""FILENAME"\" \""FILENAME"\"_moved")} exit}' bar

$ cat foo_moved 
@<TRIPOS>ATOM
  2 H18 65.2220 Du 1 RES1 0.0000
@<TRIPOS>BOND
@<TRIPOS>SUBSTRUCTURE

$ cat bar_moved
cat: bar_moved: No such file or directory
0
A.B.

Ce n'est pas aussi simple:

find -type f -exec \
 awk '/@<TRIPOS>BOND/{getline; \
  if ($0 !~ /1  3  5  ar/){\
  printf "mv %s /path/to/move/%s\n", FILENAME, FILENAME}}' {} \; \
| bash

Explication:

  • find -type f: Trouver tous les fichiers du répertoire de travail actuel
  • awk '/@<TRIPOS>BOND/{getline; \: trouve la ligne à l'intérieur du fichier et passe à la ligne suivante
  • if ($0 !~ /1 3 5 ar/){\: Si la ligne suivante n'est PAS (!~] votre "ligne numérique" souhaitée
  • printf "mv %s /path/to/move/%s\n", FILENAME, FILENAME}}' {} \; \: construit une commande mv et la redirige vers ...
  • | bash: ... bash et l'exécuter.

Ainsi, la commande mvera tous les fichiers ne contenant pas la ligne numérique dans un répertoire appelé /path/to/move/.

0
chaos