Puncte:6

Cum se verifică un anumit șir cu întreruperi de linie într-un fișier cu grep?

drapel cn

Am o variabilă șir într-un fișier script bash, după cum urmează:

șir="

test1

testul2

"

și vreau să verific dacă un fișier test.txt conține acest șir specific (inclusiv rupturile de linie, adică ar trebui să eșueze dacă conține doar următoarele:

acesta este un test:
test1

testul2
si inca una

deoarece întreruperile de linie deasupra test1 și sub test2 nu sunt prezente.

(Motivul pentru care vreau să verific acest lucru este pentru că vreau să verific dacă o anumită bucată de cod este într-un fișier sursă și, dacă nu, adăugați-o.)


Următoarele nu funcționează:

șir="
    
    test1
    
    testul2
    
    "
dacă ! grep -q șir „test.txt”; atunci
    echo "$șir" >> test.txt
fi

Acest lucru adaugă corect șirul la fișier, dar o face chiar dacă șirul a fost deja adăugat. De asemenea, funcționează corect atunci când schimb șirul pentru a nu avea întreruperi de linie.


EDITAȚI | ×:

Răspunsurile lui @terdon și @steeldriver de mai jos funcționează pentru exemplul de șir pe care l-am scris mai sus, dar din anumite motive se rup pentru acest exemplu mai realist:

șir="                                                                
                                                               
if [ -f ~/.script ]; atunci                            
        . ~/.script         
fi

"  
user56834 avatar
drapel cn
@Terrance, scuze, nu ține cont de comentariul meu anterior. De fapt, încă nu funcționează, dar eșecul este invers: acum nu ajustează niciodată fișierul, chiar dacă șirul nu este acolo în primul rând. (Deci, dacă îl execut de 5 ori, în loc să ajung cu 5 copii așa cum am făcut cu codul meu original, ajung cu 0, în timp ce ar trebui să ajung cu 1).
terdon avatar
drapel cn
Ei bine, da. Aceasta este o situație complet diferită, folosești tot felul de personaje speciale. Vă rugăm să [editați] întrebarea dvs. și adăugați i) exact ceea ce faceți, ce abordare utilizați; ii) cum apelezi scriptul și iii) ce eroare primești (să ne spui că se întrerupe nu ne ajută să înțelegem).
user56834 avatar
drapel cn
@terdon, scuze da, mesajul meu nu a fost foarte clar. i) am folosit atât abordarea dvs., cât și cea a lui @steeldiver. De exemplu.din abordarea dvs. am schimbat doar definiția `string` ii) îl numesc cu „bash substtest.sh” și iii) nu dă o eroare, mai degrabă adaugă textul șirului la nesfârșit dacă numesc bash substtest .sh din nou și din nou, mai degrabă decât să îl adăugați o dată.
terdon avatar
drapel cn
Ce comandă rulezi care eșuează? Cum mi-ați adaptat răspunsul pentru a se potrivi cu datele dvs. reale? Aceasta este o situație complet diferită de întrebarea dvs. inițială. „Șirul” pe care îl căutați conține caractere speciale. Ai nevoie de ceva de genul `string='\n\nif \[ -f ~/.script \]; apoi\s*\n\s*\. ~/\.script\s*\nfi\n\n'`.
terdon avatar
drapel cn
Vezi răspunsul actualizat.
Puncte:6
drapel cn

Problema este că grep va rula pe fiecare linie, nu pe întregul fișier. Atâta timp cât fișierul este suficient de mic pentru a se potrivi în memorie (ceea ce ar trebui să fie cazul în marea majoritate a situațiilor din zilele noastre), puteți folosi grep -z flag pentru a slurp întregul fișier:

-z, --null-data Tratați datele de intrare și de ieșire ca secvențe de linii, fiecare terminată cu un octet zero (ASCII NUL caracter) în loc de o nouă linie. Ca și opțiunea -Z sau --null, această opțiune poate fi folosită cu comenzi precum sort -z pentru a procesa nume de fișiere arbitrare.

Următoarea problemă, este că dacă treci grep ceva cu linii noi, îl va trata ca pe o listă de modele pentru care să se grepească:

$ string="1
> 2"

$ secv 10 | grep "$șir"
1
2
10
"

Ceea ce înseamnă că mă tem că va trebui să exprimi modelul ca o expresie regulată adecvată:

\n\ntest1\n\ntest2\n\n

Cu toate acestea, acest lucru înseamnă și că aveți nevoie de -P flag pentru a activa expresiile regulate compatibile cu perl, astfel încât \n va functiona.

Am creat aceste două fișiere pentru a demonstra:

$ fisier pisica1
acesta este un test:
test1

testul2
si inca una

$ fisier pisica2
acesta este un test:

test1

testul2

si inca una

Folosind aceste două fișiere și informațiile de mai sus, puteți face:

$ grep -Pz „\n\ntest1\n\ntest2\n\n” fișier1
$ 

$ grep -Pz „\n\ntest1\n\ntest2\n\n” fișier2
acesta este un test:

test1

testul2

si inca una

Punând toate acestea împreună, ne dă:

șir='\n\ntest1\n\ntest2\n\n'
dacă ! grep -Pzq "$șir" test.txt; atunci
    printf "$șir" >> test.txt
fi

Sau, așa cum sugerează @steeldriver într-un comentariu, puteți utiliza o variabilă și puteți converti liniile noi în \n pe fuga:

$ string="

    test1

    testul2

    "
$ daca ! grep -Pzq "${string//$'\n'/\n}" test.txt; atunci
    printf "$șir" >> test.txt
fi

Dacă șirul dvs. conține caractere speciale care au semnificații în expresiile regulate, așa cum arătați acum în întrebarea dvs. actualizată, atunci aceasta este o situație complet diferită. Pentru exemplul pe care îl arăți, ai avea nevoie de ceva mult mai complicat. Ca aceasta:

searchString='\n\nif \[ -f ~/.script \]; apoi\s*\n\s*\.\s+~/\.script\s*\nfi\n\n'
printString='
if [ -f ~/.script ]; atunci
   . ~/.script         
fi

'
dacă ! grep -Pzq "$searchString" test.txt; atunci     
    printf „%s” „$printString” >> test.txt 
fi
user56834 avatar
drapel cn
Mulțumiri! Presupun că vrei să spui `dacă! grep -q -z „$șir” „test.txt”; apoi`, adică cu -z adăugat?
user56834 avatar
drapel cn
De fapt, chiar și adăugând -z, aceeași problemă persistă pentru mine așa cum am afirmat în comentariul la întrebarea mea inițială: Adică, fie cu `dacă ! grep -q -z „$șir” „test.txt”; atunci` sau `daca ! grep -q "$șir" "test.txt"; atunci` sau `daca ! grep -q -z "$șir" test.txt; apoi`, eșuează într-un mod destul de ciudat:
terdon avatar
drapel cn
@user56834 hopa, da. Dar acest lucru nu va funcționa de fapt cu o variabilă. Dă-mi câteva minute, încerc să-mi dau seama care este problema.
Terrance avatar
drapel id
Coolio! +1 A avea spații în șir ca `șir='\n\n test1\n\n test2\n\n'` funcționează la fel de bine. :)
terdon avatar
drapel cn
@user56834 vă rugăm să vedeți răspunsul actualizat.
terdon avatar
drapel cn
@steeldriver, duh! Mulțumesc, aș fi putut jura că am făcut-o. Dar nu, tocmai l-am testat într-un terminal și am uitat. Remediat acum, mulțumesc.
user56834 avatar
drapel cn
Scuze pentru întârziere și mulțumesc! În ceea ce privește sugestia de la steeldriver, în mod ciudat primesc o eroare: „substtest.sh 12: Bad substitution”
user56834 avatar
drapel cn
Oh, nu contează, se pare că acest lucru se rezolvă prin executarea scriptului .sh cu bash în loc de sh (liniuță). Nu sunt sigur de ce. Funcționează! Grozav. (Deși nu înțeleg partea „//$'\n'/\n}”. Există o explicație bună pentru aceasta?)
terdon avatar
drapel cn
@user56834 `dash` și `sh` sunt _nu_ `bash` și nu ar trebui considerate sinonime. Dash este un shell POSIX minimal și îi lipsesc multe dintre caracteristicile shell-ului mai sofisticat „bash”. Același lucru este valabil și pentru `sh`. În ceea ce privește `"${string//$'\n'/\n}"`, aceasta este o înlocuire (specifică bash). Formatul general este `${var//old/new}` care va înlocui toate aparițiile lui `vechi` cu `nou` în variabila `$var`.Aici, „vechi” este `$'\n'` care este o modalitate de a trece o linie nouă către shell.
user56834 avatar
drapel cn
De fapt, tocmai am încercat același lucru pe un caz mai complicat și asta îl face să se spargă. Vezi întrebarea mea inițială.
Puncte:4
drapel hr

Poate doriți să luați în considerare utilizarea pcregrep cu -M sau --multilinie opțiune pentru a permite potrivirea liniilor noi literale:

   -M, --multilinie
             Permiteți modelelor să se potrivească mai mult de o linie. Când această opțiune
             este dat, modelele pot conține în mod util caracterul de tip newline literalâ
             actori și apariții interne ale caracterelor ^ și $.

Ex. dat

$ cat test.txt
acesta este un test:
test1

test2
si inca una


    test1

    test2
    
    

și

$ cat test2.txt
acesta este un test:
test1

test2
si inca una


    test3

    test4
    
    

cu

$ string="

    test1

    test2

    "

atunci

$ pcregrep -qM "$șir" test.txt && echo 'găsit' || ecou „nu a fost găsit”
găsite

$ pcregrep -qM "$șir" test2.txt && echo 'găsit' || ecou „nu a fost găsit”
nu a fost gasit
user56834 avatar
drapel cn
Mulțumesc, funcționează. Din păcate, eșuează pentru un exemplu mai realist, pe care l-am adăugat în întrebarea mea (doar răspunsul unui sterdon eșuează în acel exemplu)
drapel hr
@user56834 este probabil pentru că `[ ... ]` denotă un interval de caractere în PCRE. Încercați să înlocuiți `"$șir"` cu `"\Q${șir}\E"`
user56834 avatar
drapel cn
Răspundeți puțin mai târziu, dar: Îmi puteți indica un loc unde pot citi despre ce fac \Q și \E?
drapel hr
@user56834 încercați [quotemeta] de la perldoc (https://perldoc.perl.org/functions/quotemeta)
Puncte:2
drapel cn

Căutarea modelelor cu mai multe linii într-un fișier ar putea fi mai ușoară cu awk:

awk Nume fișier „/Start model/,/End pattern/”.

Verifica acest post pentru mai multe detalii

Postează un răspuns

Majoritatea oamenilor nu înțeleg că a pune multe întrebări deblochează învățarea și îmbunătățește legătura interpersonală. În studiile lui Alison, de exemplu, deși oamenii își puteau aminti cu exactitate câte întrebări au fost puse în conversațiile lor, ei nu au intuit legătura dintre întrebări și apreciere. În patru studii, în care participanții au fost implicați în conversații ei înșiși sau au citit transcrieri ale conversațiilor altora, oamenii au avut tendința să nu realizeze că întrebarea ar influența – sau ar fi influențat – nivelul de prietenie dintre conversatori.