Comment imprimer le contenu d'un fichier uniquement si la première ligne correspond à un certain motif?

11

J'écris un script, je veux vérifier si la première ligne du fichier correspond à un certain modèle et si c'est le cas, imprimer le fichier. Comment puis-je atteindre cet objectif?

Comment vérifier le motif? Existe-t-il un moyen de vérifier le modèle et en fonction de la sortie, faites quelque chose ..

EDIT: veuillez jeter un oeil à cette question: /programming/5536018/how-to-get-match-regex-pattern-using-awk-from-file

Je veux quelque chose comme ça, mais aucun d'eux n'a fonctionné pour moi. Je veux essentiellement vérifier si la première ligne correspond ou non à un motif d'expression régulière et en fonction de cela, imprimer les lignes de fichier.

Mathew
la source
1
Quelle est la sortie que vous attendez? Quel est le motif que vous recherchez? Qu'est-ce que tu as essayé jusque-là?
tachomi
@tachomi édité s'il vous plaît jetez un oeil
Mathew

Réponses:

17

Vous pouvez le faire avec ed:

ed -s infile <<\IN 2>/dev/null
1s/PATTERN/&/
,p
q
IN

l'astuce ici est d'essayer de remplacer PATTERNen 1stligne avec lui-même. edaffichera une erreur s'il ne peut pas trouver le modèle spécifié, donc ,p(imprimer tout le fichier) ne sera exécuté qu'en cas de 1s/PATTERN/&/succès.

Ou avec sed:

sed -n '1{
/PATTERN/!q
}
p' infile

cela se qproduit si la première ligne ne correspond pas ( !) PATTERN, sinon elle pimprime toutes les lignes.
Ou, comme l'a souligné Toby Speight , avec GNU sed:

sed '1{/PATTERN/!Q}' infile

Qest identique qmais n'imprime pas l'espace de motif.

don_crissti
la source
Vous pourriez Qau lieu de qpour GNU sed, ou davant q(portable) afin de ne pas exiger le -ndrapeau et la pcommande: sed '1{/PATTERN/!Q}' infileou sed -e '1{' -e '/PATTERN/!{' -e 'd' -e 'q' -e '}' -e '}' infile, respectivement.
Toby Speight
dredémarre le cycle de commande Cela me rattrape toujours! : - |
Toby Speight
Avec GNU, sedla première sedcommande se plaint sed: -e expression #1, char 10: extra characters after command(à cause de p), mais les eddernières sedsuggestions fonctionnent bien.
Skippy le Grand Gourou
NB: Les solutions apportées par cette réponse ont le mérite, par rapport aux autres réponses, de pouvoir être appliquées sur une canalisation.
Skippy le Grand Gourou
1
@SkippyleGrandGourou - vous avez essayé de le transformer en une ligne sans séparer les commandes par des points-virgules - c'est la bonne façon de le fairesed -n '1{/PATTERN/!q};p'
don_crissti
15

Avec coffre à outils POSIX:

{ head -n 1 | grep pattern && cat; } <file
cuonglm
la source
1
{double} <doux.
mikeserv
@mikeserv: J'ai l'intention de l'utiliser pour empêcher toute nouvelle personne de confondre, mais Stéphane édité est plus clair.
cuonglm
8
 awk '/pattern/{print FILENAME}; {nextfile}' ./*.txt

afficherait le nom des txtfichiers non cachés dans le répertoire actuel dont la première ligne correspond à l'expression régulière étendue patternavec les awkinplémentations qui le prennent en chargenextfile .

Si au lieu d'imprimer le nom du fichier, vous souhaitez imprimer tout le contenu du fichier, vous pouvez faire:

 awk 'FNR == 1 && ! /pattern/ {nextfile}; {print}' ./*.txt

C'est efficace car il n'exécute qu'une seule commande, mais awkn'étant pas la commande la plus efficace pour vider le contenu d'un fichier, avec des fichiers volumineux, vous pouvez éventuellement obtenir de meilleures performances en faisant quelque chose comme:

 awk '/pattern/{printf "%s\0", FILENAME}; {nextfile}' ./*.txt |
   xargs -r0 cat

Autrement dit, utilisez uniquement awkpour imprimer la liste des fichiers qui correspondent (délimité par 0) et comptez sur catpour vider leur contenu.

Stéphane Chazelas
la source
6

Si vous écrivez un script shell, vous pourriez donc quelque chose comme

for file in ./*; do head -n 1 "$file" | grep -q 'PATTERN' && cat "$file"; done

Ou, en Perl:

perl -Tlne '$f = /PATTERN/ if $. == 1; print if $f; $. = 0 if eof' ./*
terdon
la source
@ Stéphane Chazelas: Peut close ARGV- être est-ce plus idiomatique que d'assigner $..
cuonglm
@terdon Yours ressemble au code golf, le tout sur une seule ligne, sans crochets autour des noms de variables et n'encourage pas une structure propre. Et vous aviez un signe dollar manquant lorsque j'ai posté, ce n'est tout simplement pas la façon d'enseigner bash. Je suppose que ces facteurs proviennent de l'arrière-plan de Perl que vous semblez également avoir, vous serez donc pardonné! ;)
@guest salut et bienvenue sur le site! J'ai converti votre réponse en commentaire, car les réponses ne doivent être publiées que si elles répondent à la question réelle. Ce n'est pas un forum au sens classique du terme et nous ne voulons ici que des questions / réponses pures. Vous voudrez peut-être consulter le centre d'aide ou faire le tour pour mieux comprendre le site. Cela dit, mon expérience est en fait en biologie alors oui, mon code est loin d'être propre :) Cependant, je ne vois pas comment les crochets aideraient ici, les guillemets protègent déjà la variable. Qu'est-ce qui briserait ce que les crochets protégeraient?
terdon
@guest ah, désolé, j'ai oublié que vous ne pouvez pas commenter. N'hésitez pas à venir m'expliquer dans le chat , je suis sûr que je pourrais apprendre quelque chose.
terdon
5

Oldschool, traduisez simplement votre phrase en commandes standard:

for file in *; do
    if head -n 1 "${file}" | grep -q 'PATTERN'; then
        cat "${file}"
    fi
done

Pour apprendre bash, c'est un bon début. Si vous avez juste besoin d'une solution rapide, essayez les réponses sed-, awk- ou perl. Les deux sont agréables, mais ce sont des langues que vous avez besoin (et que vous voulez probablement) apprendre.

C'est un exemple assez simple, donc si vous voulez en savoir plus, vous pouvez également essayer la même chose en ruby, php, js (par exemple dans nodejs) ou dans toute autre langue qui permet l'accès aux fichiers. Même C / C ++ ou Java devrait être facile à gérer avec une petite tâche.

client
la source
1
C'est fondamentalement le même que le mien, sauf que vous l'utilisez à la if/elseplace de [ ] &&.
terdon