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
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 dequotemeta
-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
grep '^$1'
? Of bedoelde je niet ‘ dat je wilt voorkomen dat$1
wordt uitgebreid door de shell?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