grep "^$1"
typ av verk, men hur flyr jag "$1"
så grep tolkar inte några tecken i det speciellt?
Eller finns det ett bättre sätt?
Redigera: Jag vill inte söka efter "^$1"
men efter en dynamiskt införd fast sträng som bara ska matchas om det står i början av en rad. Det är vad jag menade med $1
.
Kommentarer
- Försökte du använda enkla citat istället för dubbla citat, t.ex.
grep '^$1'
? Eller menade du ’ att du vill förhindra att$1
utvidgas av skalet? - @mnille Jag vill ’ inte söka efter ’ ^ $ 1 ’ men efter en dynamiskt införd fast sträng som endast ska matchas om den ’ är i början av en rad. Att ’ är vad jag menade med $ 1.
- Du kan göra det med
grep
men du ’ måste undvika alla specialtecken i din sträng först t.ex.printf %s ^;printf %s "$1" | sed 's/[][\.*^$]/\\&/g'; } | grep -f- infile
- @don_crissti att ’ är bättre än några av de andra svaren. Vill du göra det till?
- @roaima – Jag vet men det finns ’ redan en massa svar här och detta (att undkomma de speciella karaktärerna inom vars) är något jag (och ett par andra användare här) har slagit hem under ganska lång tid … Du kan alltid lägga till det i ditt svar om du vill och jag ’ tar bort kommentera här (don ’ glöm inte att lägga till den saknade ledningen).
Svar
Jag kan inte tänka mig ett sätt att göra detta med grep
; ^
själv är en del av en reguljärt uttryck så att använda det kräver att reguljära uttryck tolkas. Det är trivialt med hjälp av substringmatchning i awk
, perl
eller vad som helst:
awk -v search="$1" "substr($0, 1, length(search)) == search { print }"
För att hantera söksträngar som innehåller \
kan du använda samma trick som i 123 ”svar :
search="$1" awk "substr($0, 1, length(ENVIRON["search"])) == ENVIRON["search"] { print }"
Kommentarer
Svar
Om du bara måste kontrollera om en matchning hittas eller inte, klipp alla inmatningsrader till längden på önskat prefix ($1
) och använd sedan grep med fast mönster:
if cut -c 1-"${#1}" | grep -qF "$1"; then echo "found" else echo "not found" fi
Det är också lätt att räkna med matchande rader:
cut -c 1-"${#1}" | grep -cF "$1"
Eller radnumren på alla matchande rader (radnummer börjar vid 1):
cut -c 1-"${#1}" | grep -nF "$1" | cut -d : -f 1
Du kan mata radnumren till head
och tail
för att få den fullständiga texten för matchande rader, men då är det lättare att bara nå ett modernt skriptspråk som Python eller Ruby.
(Exemplen ovan antar Posix grep och klipp. De antar att filen som ska sökas kommer från standardinmatning, men kan enkelt anpassas för att ta ett filnamn istället.)
Redigera: Du bör också se till att mönstret ( $1
) är inte en sträng med noll längd. Annars misslyckas cut
med att säga values may not include zero
. Om du använder Bash använder du också set -o pipefail
för att fånga felutgångar med cut
.
Svar
Ett sätt att använda perl som respekterar backslash
v="$1" perl -ne "print if index($_, $ENV{"v"} )==0" file
Detta ställer in miljövariabeln v för kommandot och skriver sedan ut om variabelns index är 0, dvs. början på raden.
Du kan också göra identiskt i awk
v="$1" awk "index($0, ENVIRON["v"])==1" file
Svar
Här är ett alternativ för alla, inte att jag rekommenderar bash för textbehandling, men det fungerar.
#!/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 beräknar längden len
för den inmatade parametern $ 1 och använder sedan parameterutvidgning på varje rad för att se om de första len
-tecknen matchar $ 1. Om så är fallet skriver ut linjen.
Svar
Om din $1
är ren ASCII och din grep
har -P
-alternativet (för att aktivera PCRE), du kan göra detta:
#!/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 här är att grep -P
tillåter regelbundna uttryck med \xXX
för att ange bokstavstecken, där XX
är hex ASCII-värdet för det tecknet. er matchas bokstavligen, även om det annars är ett speciellt regex-tecken.
od
används för att konvertera den förväntade radstarten till en lista med hex-värden, som strängas sedan ihop, var och en med prefix \x
av printf. ^
läggs sedan fram den här strängen för att bygga den nödvändiga regexen.
Om din $1
är unicode, då blir detta ganska svårare, eftersom det inte finns en 1: 1-korrespondens mellan tecken och hex-byte som utdata av od
.
Svar
Om din grep har alternativet -P, vilket betyder PCRE , du kan göra detta:
grep -P "^\Q$1\E"
Se denna fråga , och se PCRE doc för mer information om du vill.
Svara
Som ett filter:
perl -ne "BEGIN {$pat = shift} print if /^\Q$pat/" search-pattern
Kör på en eller flera filer:
perl -ne "BEGIN {$pat = shift} print if /^\Q$pat/" search-pattern file..
Avsnittet ”Citera metatecken” i perlerdokumentationen förklarar:
Citat metatecken
Backslashed metatecken i P erl är alfanumeriska, till exempel
\b
,\w
,\n
. Till skillnad från andra språk med vanligt uttryck finns det inga bakåtstreckade symboler som inte är alfanumeriska. Så allt som ser ut som\\
,\(
,\)
,\[
,\]
,\{
eller\}
tolkas alltid som en bokstavlig karaktär, inte en metatecken. Detta användes en gång i ett vanligt språk för att inaktivera eller citera de speciella betydelserna av metatecken för reguljära uttryck i en sträng som du vill använda för ett mönster. Citera bara alla tecken som inte är “ord”:$pattern =~ s/(\W)/\\$1/g;
(Om
use locale
är inställt beror detta på den aktuella platsen.) Idag är det vanligare att användaquotemeta
-funktionen eller\Q
metakvoteringsföljden för att inaktivera alla metateckenens speciella betydelser så här:/$unquoted\Q$quoted\E$unquoted/
Var uppmärksam på att om du lägger bokstavliga bakåtvända snedstreck (de som inte finns i interpolerade variabler) mellan
\Q
och\E
, interpolering av backslash med dubbelt citat kan leda till förvirrande resultat. Om du behöver använda bokstavliga snedstreck inom\Q...\E
, se ”Gory detaljer för att analysera citerade konstruktioner” i perlop .
quotemeta
och\Q
beskrivs fullständigt i quotemeta .
Svar
Om det finns ett tecken som du inte ”t använd, du kan använda det för att markera början på raden. Till exempel $"\a"
(ASCII 007). Det är ful men det fungerar:
{ 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
Om du inte behöver matchade rader kan du släppa efterföljande sed
och använda grep -qF
. Men det är mycket lättare med awk
(eller perl
) …
Svar
När du vill titta i en fil utan en loop kan du använda:
Klipp ut filen med längden på sökningen sträng
Leta efter fasta strängar och returradnummer
grep -Fn "$1" <(cut -c1-${#1} < file)
Använd radnumren för något 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 vill radera dessa rader använder du
sed "$(grep -Fn "$1" <(cut -c1-${#1} < file) | sed "s/:.*/d;/" | tr -d "\n")" file
\/
\\\/\/\/\\\\/
som ses som\\///\\/
i programmet. Så vitt jag känner till finns det inget sätt att undvika bakåtvända snedstreck i awk, såvida du inte vet hur många som kommer att användas i förväg.