grep "^$1" werkt, maar hoe ontsnap ik "$1" dus grep “interpreteert er geen speciale tekens in?

Of is er een betere manier?

Bewerken: Ik wil niet” zoeken naar "^$1" maar naar een dynamisch ingevoegde vaste string die alleen zou moeten worden vergeleken als het aan het begin van een regel staat. Dat is wat ik bedoelde met de $1.

Opmerkingen

  • Heeft u geprobeerd enkele aanhalingstekens te gebruiken in plaats van dubbele aanhalingstekens, bijv grep '^$1'? Of bedoelde je niet ‘ dat je wilt voorkomen dat $1 wordt uitgebreid door de shell?
  • @mnille Ik wil niet ‘ zoeken naar ‘ ^ $ 1 ‘ maar naar een dynamisch ingevoegde vaste string die alleen overeen moet komen als deze ‘ aan het begin van een regel staat. Dat ‘ is wat ik bedoelde met $ 1.
  • Je kunt het ook doen met grep maar jij ‘ Ik moet eerst ontsnappen aan een speciaal teken in uw string, bijv. printf %s ^;printf %s "$1" | sed 's/[][\.*^$]/\\&/g'; } | grep -f- infile
  • @don_crissti dat ‘ beter is dan sommige van de andere antwoorden. Wil je er een maken?
  • @roaima – ik weet het, maar er zijn al ‘ antwoorden hier en dit (ontsnappen aan de speciale tekens in vars) is iets waar ik (en een paar andere gebruikers hier) al geruime tijd mee aan het hameren ben … Je kunt het altijd aan je antwoord toevoegen als je wilt en ik ‘ zal de reageer hier (don ‘ t vergeet de ontbrekende voorloopaccolade toe te voegen).

Antwoord

Ik kan “geen manier bedenken om dit te doen met grep; ^ zelf is onderdeel van een reguliere expressie, dus het gebruik ervan vereist dat reguliere expressies worden geïnterpreteerd. Het is triviaal om subtekenreeksen te gebruiken in awk, perl of wat dan ook:

awk -v search="$1" "substr($0, 1, length(search)) == search { print }" 

Om zoekstrings af te handelen die \ bevatten, kun je dezelfde truc gebruiken als in 123 “s antwoord :

search="$1" awk "substr($0, 1, length(ENVIRON["search"])) == ENVIRON["search"] { print }" 

Reacties

  • Dit heeft ‘ t werken voor strings zoals als \/
  • @ 123 inderdaad, heb ik ‘ een variant toegevoegd om dat af te handelen.
  • Zal nog steeds niet slagen voor gecompliceerde strings zoals \\\/\/\/\\\\/ die in het programma wordt gezien als \\///\\/. Voor zover ik weet is er geen manier om correct te ontsnappen aan backslashes in awk, tenzij je van tevoren weet hoeveel er zullen worden gebruikt.
  • @ 123 bedankt, ik ‘ heb je truc om door de omgeving te gaan aangepast om ontsnapping aan de verwerking te voorkomen.
  • Ik vind deze oplossing nog steeds het beste. Efficiënt (awk + geen tijd verspild met rondkijken), snel opstarten (awk + geen extra processen nodig om de status in te stellen) maakt gebruik van standaardtools en is vrij beknopt. Bij alle andere antwoorden ontbreken in ieder geval enkele van deze. (Efficiëntie is hier een sterk punt, aangezien grep bekend staat om zijn ongeëvenaarde snelheid.)

Antwoord

Als u alleen moeten controleren of er een overeenkomst is gevonden, knip alle invoerregels af op de lengte van het gewenste voorvoegsel ($1) en gebruik vervolgens grep met een vast patroon:

if cut -c 1-"${#1}" | grep -qF "$1"; then echo "found" else echo "not found" fi 

Het is ook gemakkelijk om het aantal overeenkomende regels te achterhalen:

cut -c 1-"${#1}" | grep -cF "$1" 

Of de regelnummers van alle overeenkomende regels (regelnummers beginnen bij 1):

cut -c 1-"${#1}" | grep -nF "$1" | cut -d : -f 1 

U kunt de regelnummers invoeren in head en tail om de volledige tekst van de overeenkomende regels te krijgen, maar op dat moment is het gemakkelijker om gewoon een moderne scripttaal zoals Python of Ruby te vinden.

(De bovenstaande voorbeelden gaan uit van Posix grep en cut. Ze gaan ervan uit dat het te zoeken bestand afkomstig is van standaardinvoer, maar gemakkelijk kan worden aangepast om in plaats daarvan een bestandsnaam aan te nemen.)

Bewerken: Je moet er ook voor zorgen dat het patroon ( $1) is geen tekenreeks met lengte nul. Anders kan cut niet values may not include zero zeggen. Als je Bash gebruikt, gebruik dan set -o pipefail om foutuitgangen op te vangen met cut.

Antwoord

Een manier om perl te gebruiken die backslashes respecteert

v="$1" perl -ne "print if index($_, $ENV{"v"} )==0" file 

Dit stelt de omgevingsvariabele v in voor de commando, en drukt vervolgens af als de index van de variabele 0 is, dwz het begin van de regel.

Je kunt ook identiek doen in awk

v="$1" awk "index($0, ENVIRON["v"])==1" file 

Answer

Hier is een all-bash-optie, niet dat ik bash aanbeveel voor tekstverwerking, maar het werkt.

#!/usr/bin/env bash # searches for $1 at the beginning of the line of its input len=${#1} while IFS= read -r line do [[ "${line:0:len}" = "$1" ]] && printf "%s\n" "$line" done 

Het script berekent de lengte len van de ingevoerde parameter $ 1, gebruikt vervolgens parameteruitbreiding op elke regel om te zien of de eerste len -tekens overeenkomen met $ 1. Zo ja, dan drukt de regel af.

Answer

Als uw $1 puur ASCII is en uw grep heeft de -P optie (om PCRE in te schakelen), je kunt dit doen:

 #!/bin/bash line_start="$1" line_start_raw=$(printf "%s" "$line_start" | od -v -t x1 -An) line_start_hex=$(printf "\\x%s" $line_start_raw) grep -P "^$line_start_hex"  

Het idee hier is dat grep -P reguliere expressies toestaat met \xXX om letterlijke tekens op te geven, waarbij XX de hexadecimale ASCII-waarde van dat teken is. er komt letterlijk overeen, zelfs als het anders een speciaal regex-teken is.

od wordt gebruikt om de verwachte regelstart om te zetten in een lijst met hexadecimale waarden, die worden vervolgens aan elkaar geregen, elk voorafgegaan door \x door printf. ^ wordt vervolgens aan deze string toegevoegd om de vereiste regex samen te stellen.


Als uw $1 unicode is, dan wordt dit een stuk moeilijker, omdat er geen 1: 1 correspondentie is van karakters met hex bytes zoals uitgevoerd door od.

Antwoord

Als uw grep de optie -P heeft, wat betekent PCRE , u kunt dit doen:

grep -P "^\Q$1\E" 

Verwijs naar deze vraag , en zie PCRE-document voor details als je wilt.

Antwoord

Als filter:

perl -ne "BEGIN {$pat = shift} print if /^\Q$pat/" search-pattern 

Draaien op een of meer bestanden:

perl -ne "BEGIN {$pat = shift} print if /^\Q$pat/" search-pattern file.. 

De “Metatekens aanhalen” sectie van de perlre documentatie legt uit:

Citeren metatekens

Backslashed metatekens in P erl zijn alfanumeriek, zoals \b, \w, \n. In tegenstelling tot sommige andere talen met reguliere expressies, zijn er geen backslash-symbolen die niet alfanumeriek zijn. Dus alles wat eruitziet als \\, \(, \), \[, \], \{, of \} wordt altijd geïnterpreteerd als een letterlijk karakter, geen metakarakter. Dit werd ooit in een algemeen idioom gebruikt om de speciale betekenissen van metatekens van reguliere expressies in een tekenreeks die u voor een patroon wilt gebruiken, uit te schakelen of aan te halen. Citeer eenvoudig alle niet-“woord” -tekens:

 $pattern =~ s/(\W)/\\$1/g; 

(Als use locale is ingesteld, hangt dit af van de huidige landinstelling.) Tegenwoordig is het gebruikelijker om de quotemeta -functie of de \Q metaquoting escape-reeks te gebruiken om de speciale betekenissen van alle metatekens uit te schakelen zoals dit:

 /$unquoted\Q$quoted\E$unquoted/ 

Pas op dat als je letterlijke backslashes plaatst (die niet binnen geïnterpoleerde variabelen) tussen \Q en \E, backslash-interpolatie met dubbele aanhalingstekens kan tot verwarrende resultaten leiden. Als u letterlijke backslashes moet gebruiken binnen \Q...\E, raadpleeg dan “Bloederige details van het parseren van aanhalingstekens” in perlop .

quotemeta en \Q worden volledig beschreven in quotemeta .

Antwoord

Als er een teken is dat u niet doet “t gebruiken, zou je dat kunnen gebruiken om het begin van de regel te markeren. Bijvoorbeeld $"\a" (ASCII 007). Het is lelijk, maar het zal werken:

{ echo "this is a line to match"; echo "but this is not"; } >file.txt stuffing=$"\a" # Guaranteed never to appear in your source text required="this" # What we want to match that beginning of a line match=$(sed "s/^/$stuffing/" file.txt | grep -F "$stuffing$required" | sed "s/^$stuffing//") if [[ -n "$match" ]] then echo "Yay. We have a match: $match" fi 

Als je de overeenkomende regel (s) niet nodig hebt, kun je de afsluitende sed verwijderen en grep -qF. Maar het “is veel gemakkelijker met awk (of perl) …

Antwoord

Als u in een bestand wilt zoeken zonder lus, kunt u het volgende gebruiken:
Knip het bestand met de lengte van de zoekopdracht string

Zoek naar vaste strings en retourneer regelnummers

 grep -Fn "$1" <(cut -c1-${#1} < file) 

Gebruik de regelnummers voor iets als sed -n "3p;11p" file

 sed -n "$(grep -Fn "$1" <(cut -c1-${#1} < file) | sed "s/:.*/p;/" | tr -d "\n")" file 

Als je deze regels wilt verwijderen, gebruik je

 sed "$(grep -Fn "$1" <(cut -c1-${#1} < file) | sed "s/:.*/d;/" | tr -d "\n")" file 

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *