inoltra una stringa fissa all'inizio di una riga


20

grep "^$1"in qualche modo funziona, ma come posso scappare "$1"così grep non interpreta alcun personaggio in esso appositamente?

O c'è un modo migliore?

Modifica: non voglio cercare '^$1'ma per una stringa fissa inserita dinamicamente che dovrebbe essere abbinata solo se è all'inizio di una riga. Questo è ciò che intendevo per $1.


Hai provato a usare virgolette singole anziché doppie, ad esempio grep '^$1'? O non volevi dire che vuoi impedire l' $1espansione della shell?
mnille,

@mnille Non voglio cercare '^ $ 1' ma una stringa fissa inserita dinamicamente che dovrebbe essere abbinata solo se è all'inizio di una riga. Questo è ciò che intendevo per $ 1.
PSkocik,

3
Puoi farlo grepanche tu ma prima dovrai scappare da qualsiasi carattere speciale nella tua stringa, ad es.printf %s ^;printf %s "$1" | sed 's/[][\.*^$]/\\&/g'; } | grep -f- infile
don_crissti,

@don_crissti è meglio di alcune delle altre risposte. Ti interessa farlo?
roaima,

@roaima - Lo so ma ci sono già un sacco di risposte qui e questo (sfuggendo ai caratteri speciali all'interno di vari) è qualcosa che io (e un paio di altri utenti qui) ho martellato a casa da un po 'di tempo ... Puoi sempre aggiungere alla tua risposta se lo desideri e rimuoverò il commento qui (non dimenticare di aggiungere la parentesi graffa mancante).
don_crissti,

Risposte:


7

Non riesco a pensare a un modo per farlo usando grep; ^stesso fa parte di un'espressione regolare, quindi usarla richiede che le espressioni regolari siano interpretate. E 'banale utilizzando sottostringa corrispondente a awk, perlo qualsiasi altra cosa:

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

Per gestire le stringhe di ricerca contenenti \, puoi usare lo stesso trucco della risposta 123 :

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

Questo non funzionerà con stringhe come\/
123

@ 123 in effetti, ho aggiunto una variante per gestirlo.
Stephen Kitt,

Fallirà comunque per stringhe complicate come quella \\\/\/\/\\\\/che si vede \\///\\/nel programma. Per quanto ne so, non c'è modo di sfuggire correttamente alle barre rovesciate in awk, a meno che non si sappia quante ne verranno utilizzate in precedenza.
123,

1
@ 123 grazie, ho adattato il tuo trucco per attraversare l'ambiente per evitare l'elaborazione di escape.
Stephen Kitt,

Mi piace ancora questa soluzione la migliore. Efficiente (awk + senza perdere tempo a guardarsi intorno), l'avvio rapido (awk + non richiede processi aggiuntivi per impostare lo stato) utilizza strumenti standard ed è abbastanza conciso. Tutte le altre risposte mancano almeno alcune di queste. (L'efficienza è un punto di forza qui perché grep è noto per una velocità senza pari.)
PSkocik,

14

Se devi solo verificare se viene trovata o meno una corrispondenza, taglia tutte le righe di input alla lunghezza del prefisso desiderato ( $1) e quindi utilizza grep a schema fisso:

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

È anche facile ottenere il conteggio delle righe corrispondenti:

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

Oppure i numeri di riga di tutte le righe corrispondenti (i numeri di riga iniziano da 1):

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

È possibile alimentare i numeri di riga heade tailottenere il testo completo delle righe corrispondenti, ma a quel punto è più semplice raggiungere un linguaggio di script moderno come Python o Ruby.

(Gli esempi sopra riportati presuppongono Posix grep e cut. Presumono che il file da cercare provenga dall'input standard, ma possono essere facilmente adattati per prendere un nome file invece.)

Modifica: dovresti anche assicurarti che pattern ( $1) non sia una stringa di lunghezza zero. Altrimenti cutnon riesce a dire values may not include zero. Inoltre, se si utilizza Bash, utilizzare set -o pipefailper rilevare le uscite di errore cut.


10

Un modo di usare perl che rispetterà le barre rovesciate

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

Questo imposta la variabile d'ambiente v per il comando, quindi stampa se l'indice della variabile è 0, ovvero l'inizio della riga.

Puoi anche fare lo stesso in awk

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

7

Ecco un'opzione all-bash, non che raccomando bash per l'elaborazione del testo, ma funziona.

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

Lo script calcola la lunghezza lendel parametro immesso $ 1, quindi utilizza l'espansione dei parametri su ciascuna riga per vedere se i primi lencaratteri corrispondono a $ 1. In tal caso, stampa la linea.


4

Se sei $1ASCII puro e hai grepl' -Popzione (per abilitare PCRE), puoi farlo:

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

L'idea qui è che grep -Pconsente alle espressioni regolari con \xXXdi specificare caratteri letterali, dove si XXtrova il valore ASCII esadecimale di quel carattere. Il personaggio è abbinato letteralmente, anche se altrimenti è un personaggio regex speciale.

odviene utilizzato per convertire l'inizio della riga prevista in un elenco di valori esadecimali, che vengono quindi uniti insieme, ognuno con il prefisso \xprintf. ^viene quindi anteposta questa stringa per creare il regex richiesto.


Se il tuo $1è unicode, allora questo diventa un po 'più difficile, perché non c'è una corrispondenza 1: 1 tra caratteri e byte esadecimali come output di od.


3

Come filtro:

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

Esegui su uno o più file:

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

La sezione "Citando i metacaratteri" della documentazione del perlre spiega:

Citando i metacaratteri

Metacaratteri backslash in Perl sono alfanumerici, come ad esempio \b, \w, \n. A differenza di altri linguaggi di espressione regolare, non ci sono simboli retroilluminati che non sono alfanumerici. Quindi tutto ciò che assomiglia \\, \(, \), \[, \], \{, o \}è sempre interpretata come un carattere letterale, non è un metacarattere. Una volta veniva usato in un linguaggio comune per disabilitare o citare i significati speciali dei metacaratteri delle espressioni regolari in una stringa che si desidera utilizzare per un modello. Cita semplicemente tutti i caratteri non di "parola":

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

(Se use localeimpostato, dipende dalle impostazioni locali correnti.) Oggi è più comune utilizzare la quotemetafunzione o la \Q sequenza di escape della metaquotazione per disabilitare tutti i significati speciali di tutti i metacaratteri come questo:

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

Attenzione che se si inseriscono barre rovesciate letterali (quelle non all'interno di variabili interpolate) tra \Qe \E, l'interpolazione della barra rovesciata con virgolette doppie può portare a risultati confusi. Se è necessario utilizzare barre rovesciate letterali all'interno \Q...\E, consultare "Dettagli Gory dell'analisi dei costrutti quotati" in perlop .

quotemetae \Qsono completamente descritti tra virgolette .


3

Se il tuo grep ha l'opzione -P, che significa PCRE , puoi farlo:

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

Fare riferimento a questa domanda e consultare il documento PCRE per i dettagli, se lo si desidera.


2

Se c'è un carattere che non usi, puoi usarlo per segnare l'inizio della linea. Ad esempio, $'\a'(ASCII 007). È brutto ma funzionerà:

{ 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

Se non hai bisogno delle linee corrispondenti, puoi eliminare il trailing sede usarlo grep -qF. Ma è molto più facile con awk(o perl) ...


0

Quando vuoi cercare un file senza un ciclo puoi usare:
Taglia il file con la lunghezza della stringa di ricerca

  cut -c1-${#1} < file

Cerca stringhe fisse e numeri di riga di ritorno

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

Usa i numeri di riga per qualcosa del genere sed -n '3p;11p' file

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

Quando si desidera eliminare queste righe, utilizzare

  sed "$(grep -Fn "$1" <(cut -c1-${#1} < file) | sed 's/:.*/d;/' | tr -d '\n')" file
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.