grep "^$1"
slags verk, men hvordan slipper jeg "$1"
så grep tolker ikke noen tegn i den spesielt?
Eller er det en bedre måte?
Rediger: Jeg vil ikke søke etter "^$1"
men etter en dynamisk innsatt fast streng som bare skal bli matchet hvis det er i begynnelsen av en linje. Det er det jeg mente med $1
.
Kommentarer
Svar
Jeg kan ikke tenke meg en måte å gjøre dette ved å bruke grep
; ^
er en del av en regelmessig uttrykk, så bruk av det krever at regulære uttrykk tolkes. Det er trivielt ved å bruke underordnet samsvar i awk
, perl
eller hva som helst:
awk -v search="$1" "substr($0, 1, length(search)) == search { print }"
For å håndtere søkestrenger som inneholder \
, kan du bruke samme triks som i 123 «svar :
search="$1" awk "substr($0, 1, length(ENVIRON["search"])) == ENVIRON["search"] { print }"
Kommentarer
- Dette vant ‘ t fungerer for strenger som som
\/
- @ 123 faktisk, har jeg ‘ lagt til en variant for å håndtere det.
- Vil fortsatt mislykkes for kompliserte strenger som
\\\/\/\/\\\\/
som blir sett på som\\///\\/
i programmet. Så vidt jeg er klar over, er det ingen måte å unnslippe tilbakeslag i tverr, med mindre du vet hvor mange som vil bli brukt på forhånd. - @ 123 takk, jeg ‘ Vi har tilpasset trikset ditt med å gå gjennom miljøet for å unngå rømningsbehandling.
- Jeg liker fortsatt denne løsningen best. Effektiv (awk + ingen bortkastet tid på å se seg rundt), rask oppstart (awk + ingen ekstra prosesser som trengs for å sette opp tilstand) bruker standardverktøy, og er ganske kortfattet. Alle de andre svarene mangler i det minste noen av disse. (Effektivitet er et sterkt punkt her, da grep er kjent for uovertruffen hastighet.)
Svar
Hvis du bare må du sjekke om det ikke er funnet samsvar, klippe alle inngangslinjene til lengden på ønsket prefiks ($1
) og deretter bruke grep med fast mønster:
if cut -c 1-"${#1}" | grep -qF "$1"; then echo "found" else echo "not found" fi
Det er også enkelt å få tellingen av samsvarende linjer:
cut -c 1-"${#1}" | grep -cF "$1"
Eller linjenumrene til alle samsvarende linjer (linjenumre starter ved 1):
cut -c 1-"${#1}" | grep -nF "$1" | cut -d : -f 1
Du kan mate linjenumrene til head
og tail
for å få fullteksten til de matchende linjene, men på det tidspunktet er det lettere å bare nå et moderne skriptspråk som Python eller Ruby.
(Eksemplene ovenfor antar Posix grep og klipp. De antar at filen som skal søkes kommer fra standardinngang, men kan lett tilpasses til å ta et filnavn i stedet.)
Rediger: Du bør også sørge for at mønsteret ( $1
) er ikke en streng med null lengde. Ellers mislykkes cut
values may not include zero
. Hvis du bruker Bash, bruk set -o pipefail
for å fange feilutganger med cut
.
Svar
En måte å bruke perl som vil respektere tilbakeslag
v="$1" perl -ne "print if index($_, $ENV{"v"} )==0" file
Dette angir miljøvariabelen v for kommandoen, og skriver deretter ut hvis indeksen til variabelen er 0, dvs. begynnelsen på linjen.
Du kan også gjøre det samme i awk
v="$1" awk "index($0, ENVIRON["v"])==1" file
Svar
Her er et all-bash-alternativ, ikke det at jeg anbefaler bash for 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
Skriptet beregner lengden len
for den innlagte parameteren $ 1, bruker deretter parameterutvidelse på hver linje for å se om de første len
tegn samsvarer med $ 1. I så fall skriver ut linjen.
Svar
Hvis $1
er ren ASCII og din grep
har -P
-alternativet (for å aktivere PCRE), du kan gjø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"
Tanken her er at grep -P
tillater regelmessige uttrykk med \xXX
for å spesifisere bokstavlige tegn, der XX
er den hex-ASCII-verdien til det tegnet. er matches bokstavelig, selv om det ellers er et spesielt regex-tegn.
od
brukes til å konvertere forventet linjestart til en liste med hexverdier, som blir deretter strengt sammen, hver foran en \x
av printf. ^
blir deretter lagt denne strengen for å bygge den nødvendige regexen.
Hvis $1
er unicode, da blir dette ganske vanskeligere, fordi det ikke er en 1: 1-korrespondanse mellom tegn og heksebytes som utdata fra od
.
Svar
Hvis grep har alternativet -P, som betyr PCRE , kan du gjøre dette:
grep -P "^\Q$1\E"
Se dette spørsmålet , og se PCRE doc for detaljer hvis du vil.
Svar
Som filter:
perl -ne "BEGIN {$pat = shift} print if /^\Q$pat/" search-pattern
Kjør på en eller flere filer:
perl -ne "BEGIN {$pat = shift} print if /^\Q$pat/" search-pattern file..
Avsnittet «Siterer metategn» i perlerdokumentasjonen forklarer:
Sitat metategn
Snartegnet metategn i P erl er alfanumeriske, slik som
\b
,\w
,\n
. I motsetning til andre språk med vanlig uttrykk, er det ingen tilbakeslagstegnede symboler som ikke er alfanumeriske. Så alt som ser ut som\\
,\(
,\)
,\[
,\]
,\{
, eller\}
tolkes alltid som en bokstavelig karakter, ikke en metakarakter. Dette ble en gang brukt i et vanlig uttrykk for å deaktivere eller sitere de spesielle betydningene av metategn med regulært uttrykk i en streng du vil bruke til et mønster. Bare sitat alle tegn som ikke er “ord”:$pattern =~ s/(\W)/\\$1/g;
(Hvis
use locale
er angitt, avhenger dette av gjeldende sted.) I dag er det mer vanlig å brukequotemeta
-funksjonen eller\Q
metakvoterende rømningssekvens for å deaktivere alle metategnens spesielle betydninger slik:/$unquoted\Q$quoted\E$unquoted/
Vær oppmerksom på at hvis du setter bokstavelige tilbakeslag (de som ikke er i interpolerte variabler) mellom
\Q
og\E
, tilbakeslagsinterpolering med dobbelt anførselstegn kan føre til forvirrende resultater. Hvis du trenger å bruke bokstavelige tilbakeslag innen\Q...\E
, kan du gå til “Gory details of parsing sitated constructs” in perlop .
quotemeta
og\Q
er fullstendig beskrevet i quotemeta .
Svar
Hvis det er et tegn du ikke gir «t bruk, du kan bruke det til å markere begynnelsen på linjen. For eksempel, $"\a"
(ASCII 007). Det er stygt, men det vil fungere:
{ 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 trenger den eller de samsvarende linjene, kan du slippe den etterfølgende sed
og bruke grep -qF
. Men det er mye lettere med awk
(eller perl
) …
Svar
Når du vil se i en fil uten løkke, kan du bruke:
Klipp filen med lengden på søket streng
Se etter faste strenger og returlinjenumre
grep -Fn "$1" <(cut -c1-${#1} < file)
Bruk linjenumrene til noe 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 linjene, bruk
sed "$(grep -Fn "$1" <(cut -c1-${#1} < file) | sed "s/:.*/d;/" | tr -d "\n")" file
grep '^$1'
? Eller mente du ‘ at du vil forhindre at$1
utvides av skallet?grep
også, men du ‘ Jeg må unnslippe ethvert spesialtegn i strengen din, f.eksprintf %s ^;printf %s "$1" | sed 's/[][\.*^$]/\\&/g'; } | grep -f- infile