grep "^$1" slags værker, men hvordan slipper jeg "$1" så grep fortolker ikke nogen tegn i det specielt?

Eller er der en bedre måde?

Rediger: Jeg vil ikke søge efter "^$1" men efter en dynamisk indsat fast streng, der kun skal blive matchet, hvis det er i begyndelsen af en linje. Det er hvad jeg mente med $1.

Kommentarer

  • Forsøgte du at bruge enkelt anførselstegn i stedet for dobbelt anførselstegn, f.eks grep '^$1'? Eller mente du ‘ at du vil forhindre, at $1 udvides af skallen?
  • @mnille Jeg vil ikke ‘ ikke søge efter ‘ ^ $ 1 ‘ men efter en dynamisk indsat fast streng, som kun skal matches, hvis den ‘ er i begyndelsen af en linje. At ‘ er hvad jeg mente med $ 1.
  • Du kan også gøre det med grep men du ‘ Jeg skal undslippe ethvert specialtegn i din streng først f.eks. printf %s ^;printf %s "$1" | sed 's/[][\.*^$]/\\&/g'; } | grep -f- infile
  • @don_crissti at ‘ er bedre end nogle af de andre svar. Har du lyst til at gøre det til?
  • @roaima – jeg ved, men der er ‘ allerede en masse svar her, og dette (undslippe de specielle tegn inde i vars) er noget jeg (og et par andre brugere her) har hamret hjem i nogen tid … Du kan altid tilføje det til dit svar, hvis du ønsker det, og jeg ‘ fjerner kommenter her (don ‘ glem ikke at tilføje den manglende førende seler).

Svar

Jeg kan ikke tænke på en måde at gøre dette på ved hjælp af grep; ^ selv er en del af en regelmæssigt udtryk, så brug af det kræver, at regelmæssige udtryk fortolkes. Det er trivielt ved hjælp af substring matching i awk, perl eller hvad som helst:

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

For at håndtere søgestrenge, der indeholder \, kan du bruge det samme trick som i 123 “svar :

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

Kommentarer

  • Dette vandt ‘ t arbejde for strenge sådan som \/
  • @ 123 faktisk, har jeg ‘ tilføjet en variant til at håndtere det.
  • Mislykkes stadig ved komplicerede strenge som \\\/\/\/\\\\/, der ses som \\///\\/ i programmet. Så vidt jeg ved, er der ingen måde at undslippe tilbageslag korrekt, medmindre du ved, hvor mange der vil blive brugt på forhånd.
  • @ 123 tak, jeg ‘ Vi har tilpasset dit trick med at gå gennem miljøet for at undgå at undslippe behandling.
  • Jeg kan stadig godt lide denne løsning. Effektiv (awk + ingen spildt tid på at kigge rundt), hurtig opstart (awk + ingen yderligere processer nødvendige til opsætningstilstand) bruger standardværktøjer og er ret kortfattet. Alle de andre svar mangler i det mindste nogle af disse. (Effektivitet er et stærkt punkt her, da grep er kendt for uovertruffen hastighed.)

Svar

Hvis du kun skal kontrollere, om der findes et match eller ej, klip alle inputlinjer til længden af det ønskede præfiks ($1) og brug derefter grep med fast mønster:

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

Det er også let at få antallet af matchende linjer:

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

Eller linienumrene på alle matchende linjer (linjenumre starter ved 1):

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

Du kan føje linjenumrene til head og tail for at få den fulde tekst af de matchende linjer, men på det tidspunkt er det lettere at nå ud til et moderne script-sprog som Python eller Ruby.

(Ovenstående eksempler antager Posix grep og cut. De antager, at filen, der skal søges, kommer fra standardinput, men kan let tilpasses til at tage et filnavn i stedet.)

Rediger: Du skal også sikre, at mønsteret ( $1) er ikke en streng med nul længde. Ellers siger cut ikke values may not include zero. Hvis du bruger Bash, skal du også bruge set -o pipefail til at fange fejludgange med cut.

Svar

En måde at bruge perl på, som respekterer tilbageslag

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

Dette indstiller miljøvariablen v for kommandoen, og udskriver derefter, hvis variabelens indeks er 0, dvs. begyndelsen på linjen.

Du kan også gøre det samme i awk

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

Svar

Her er en all-bash-mulighed, ikke at jeg anbefaler bash til tekstbehandling, men det fungerer.

#!/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 

Scriptet beregner længden len for den indlæste parameter $ 1 og bruger derefter parameterudvidelse på hver linje for at se, om de første len tegn matcher $ 1. Hvis ja, er det udskriver linjen.

Svar

Hvis din $1 er ren ASCII og din grep har -P mulighed (for at aktivere PCRE), du kan gøre dette:

 #!/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"  

Ideen her er, at grep -P tillader regelmæssige udtryk med \xXX for at specificere bogstavelige tegn, hvor XX er den hex ASCII-værdi for dette tegn. er matches bogstaveligt, selvom det ellers er et specielt regex-tegn.

od bruges til at konvertere den forventede linjestart til en liste med hex-værdier, som strækkes derefter sammen, hver præfiks med \x af printf. ^ forudså derefter denne streng for at opbygge den krævede regex.


Hvis din $1 er unicode, så bliver dette en anelse sværere, fordi der ikke er en 1: 1-korrespondance mellem tegn og hex-bytes som output af od.

Svar

Hvis din grep har indstillingen -P, hvilket betyder PCRE , du kan gøre dette:

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

Se dette spørgsmål , og se PCRE doc for detaljer, hvis du vil.

Svar

Som filter:

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

Kør på en eller flere filer:

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

Afsnittet “Citering af metategn” i perlerdokumentationen forklarer:

Citering metategn

Backslashed metategn i P erl er alfanumeriske, såsom \b, \w, \n. I modsætning til nogle andre sprog med almindeligt udtryk er der ingen tilbageslagssymboler, der ikke er alfanumeriske. Så alt, hvad der ligner \\, \(, \), \[, \], \{ eller \} fortolkes altid som en bogstavelig karakter, ikke en metakarakter. Dette blev engang brugt i et almindeligt udtryk til at deaktivere eller citere de specielle betydninger af metategn med regulært udtryk i en streng, som du vil bruge til et mønster. Citér blot alle ikke-“word” -tegn:

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

(Hvis use locale er indstillet, afhænger dette af det aktuelle landestandard.) I dag er det mere almindeligt at bruge quotemeta -funktionen eller \Q metaquoting escape-sekvensen for at deaktivere alle metategnens særlige betydninger sådan her:

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

Pas på, at hvis du sætter bogstavelige tilbageslag (dem, der ikke er inde i interpolerede variabler) mellem \Q og \E, dobbelt-citeret backslash-interpolering kan føre til forvirrende resultater. Hvis du har brug for bogstavelige tilbageslag inden for \Q...\E, skal du se “Gory detaljer om parsing af citerede konstruktioner” i perlop .

quotemeta og \Q er fuldt beskrevet i quotemeta .

Svar

Hvis der er et tegn, som du ikke har “for ikke at bruge det, kan du bruge det til at markere begyndelsen af linjen. For eksempel $"\a" (ASCII 007). Det er grimt, men det fungerer:

{ 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 

Hvis du ikke har brug for den matchede linje (r), kan du slippe den efterfølgende sed og bruge grep -qF. Men det er meget lettere med awk (eller perl) …

Svar

Når du vil se i en fil uden en loop, kan du bruge:
Klip filen med søgningens længde streng

Se efter faste strenge og returner linjenumre

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

Brug linjenumrene til noget som sed -n "3p;11p" file

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

Når du vil slette disse linjer, skal du bruge

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

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *