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
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 brugequotemeta
-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
\/
\\\/\/\/\\\\/
, 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.