Grep per il motivo all'inizio o al centro di una linea


9

Inizierò dicendo che penso che questo problema sia un po 'meno innocente di quanto sembri.

Cosa devo fare: cerca una cartella all'interno della variabile d'ambiente PATH. Potrebbe essere all'inizio o da qualche parte dopo. Devo solo verificare che quella cartella sia lì.

Esempio del mio problema: usiamo /opt/gnome.


SCENARIO 1: la cartella non è all'inizio del PERCORSO

# echo "$PATH"
/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome

# echo "$PATH" | grep ":/opt/gnome"
/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome

Nota che il grep deve essere abbastanza specifico in modo da non catturare /var/opt/gnome. Da qui il colon.


SCENARIO 2: la cartella si trova all'inizio del PERCORSO.

# echo "$PATH"
/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome

# echo "$PATH" | grep "^/opt/gnome"
/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome

Questo è il mio problema: ho bisogno di cercare due punti o un inizio di linea con questa cartella. Quello che vorrei fare è una di queste due espressioni tra parentesi:

# echo $PATH | grep "[^:]/opt/gnome"
# echo $PATH | grep "[:^]/opt/gnome"

MA [^e [:hanno i loro significati. Pertanto, i due comandi sopra non funzionano.

C'è un modo in cui posso grep per questi due scenari in un solo comando?


Nota che il commento di Gilles sulla risposta di Costas si applica anche alla domanda: poiché non stai cercando /opt/gnome:o /opt/gnome$, troverai /opt/gnome-fooo /opt/gnome/bar.
Scott,

@Scott - Fintanto che includi nella tua corrispondenza lo spazio intermedio, puoi sempre ancorare qualsiasi stringa alla testa e alla coda della linea senza tali complicazioni. Proprio comegrep '^\(any number of other matches:*:\)*my match\(:.*\)*$'
mikeserv,

Risposte:


10

Se stai controllando il contenuto della PATHvariabile d'ambiente, invece di cercare qualcosa in un file, allora greplo strumento è sbagliato. È più facile (e più veloce e probabilmente più leggibile) farlo nella shell.

In bash, ksh e zsh:

if [[ :$PATH: = *:/opt/gnome:* ]]; then
 : # already there
else
  PATH=$PATH:/opt/gnome
fi

portabile:

case :$PATH: in
  *:/opt/gnome:*) :;; # already there
  *) PATH=$PATH:/opt/gnome;;
esac

Nota l'uso di :$PATH:piuttosto che $PATH; in questo modo, il componente è sempre circondato da due punti nella stringa di ricerca anche se era all'inizio o alla fine di $PATH.

Se stai cercando attraverso una riga di un file, puoi usare la regexp estesa (cioè richiedere grep -E) (^|:)/opt/gnome($|:)per abbinare /opt/gnomema solo se è all'inizio di una riga o seguendo i due punti, e solo se è alla fine di la linea o seguita da due punti.


8

Puoi usare espressioni regolari estese semplicemente usando grep -E

Devi abbinare l'inizio e la fine del percorso che stai cercando di trovare se vuoi evitare falsi positivi.

Corrisponde all'istanza all'inizio:

$ TEST=/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome
$ echo $TEST | grep -E "(:|^)/opt/gnome(:|$)"
/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome

Corrisponde anche all'istanza al centro:

$ TEST=/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome
$ echo $TEST | grep -E "(:|^)/opt/gnome(:|$)"
/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome

Evitare i falsi positivi:

$ TEST="/home/bob/opt/gnome:/opt/gnome/somethingelse:/opt/gnome-beta"
$ echo $TEST | grep -E "(:|^)/opt/gnome(:|$)"

Nessuna corrispondenza lì.

Compatto ed elegante. Testato su Debian 7.


1
egrepè l'uso deprecato grep -E(fonte: man grep)
Anthon

Grazie, funziona come un fascino! Non l'ho scelto come risposta, perché penso che l'opzione -w sia un po 'più semplice. Ancora più semplice di quanto avessi inizialmente immaginato!
JamesL,

3
Avvertimento. L' -wopzione ha alcuni problemi. Solo le cifre, le lettere e il carattere di sottolineatura sono considerati "parole". Quindi alcuni caratteri insoliti ma possibili lo faranno fallire. Esempio echo '/sbin:/usr/sbin:/var-/opt/gnome' | grep -w "/opt/gnome"e echo '/sbin:/usr/sbin:/var./opt/gnome' | grep -w "/opt/gnome". Quelli danno risultati sbagliati.
Luis Antolín Cano,

1
Sei sulla strada giusta, ma ci sono ancora falsi positivi: /opt/gnome/somethingelse.
Gilles 'SO- smetti di essere malvagio'

1
Totalmente giusto. Dovremmo preoccuparci esplicitamente della fine, non solo dell'inizio. Penso che questo risolva i problemi echo "/home/bob/opt/gnome:/opt/gnome/somethingelse:/opt/gnome-beta" | grep -E "(:|^)/opt/gnome(:|$)". Modifica risposta.
Luis Antolín Cano,

7

Se non sei sposato grep, puoi usare awke separare i record:

awk 'BEGIN {RS=":"} /^\/opt\/gnome$/'

5

Puoi anche usare

echo "$PATH" | tr ':' '\n' | grep -x "/opt/gnome"

che divide la variabile percorso in linee separate (una per percorso), quindi grep -xpuò cercare risultati esatti. Questo ha ovviamente lo svantaggio di cui necessita un ulteriore processo tr. E non funzionerà quando il nome di una cartella PATHcontiene caratteri di nuova riga.


2

Non so è abbastanza per la risposta ma

grep -w "/opt/gnome"

soddisferà le tue necessità.

echo '/sbin:/usr/sbin:/opt/gnome:/var/opt/gnome' | grep -w "/opt/gnome" -o
/opt/gnome
echo '/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome' | grep -w "/opt/gnome" -o
/opt/gnome

ma

echo '/opt/gnome:/sbin:/usr/sbin:/var/opt/gnome' | grep "/opt/gnome" -o
/opt/gnome
/opt/gnome

Funziona perfettamente perché i due punti sono caratteri non di parole. Grazie!
JamesL,

@ Sman865 C'è un altro motivo: perché /non fa parte della parola ma lo rè.
Costas,

2
Avvertimento. Come ho detto al commento sulla mia risposta. Esistono caratteri legali per un nome di directory che sono caratteri non di parole. Ciò porta a risultati errati. Non è normale terminare un nome di directory in - ma potrebbe succedere.
Luis Antolín Cano,

4
@ Sman865 falsi positivi: /opt/gnome-beta, /home/bob/opt/gnome, ...
Gilles 'SO-fermata essere malvagio'

Caso non funzionante: grep -w /usr/local -o <<< /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games------/usr/local /usr/local /usr/local
pabouk,

0

Per selezionare /opt/gnomecircondato da caratteri non-parola (nuove linee, :, /, ecc) provate questo:

grep '\B/opt/gnome'

0

Puoi farlo in modo affidabile e con poco sforzo grep. Puoi sfruttare le estensioni che sono ampiamente disponibili e tra le quali sono già state offerte molte soluzioni, ma anche con regex di base è facile da fare, anche se a prima vista potrebbe non essere intuitivo.

Con regex di base - e così via grep- hai sempre due ancore affidabili: la testa e la coda della linea. Puoi ancorare una corrispondenza a entrambi indipendentemente dalla sua posizione sulla linea come:

grep '^\(ignore case, delimiter\)*match\(delimiter, ignore case\)*$'

grepcorrisponderà dal capo della linea al numero di occorrenze delle \(grouped\)sottoespressioni necessario per incontrare successivamente il delimitatore, quindi la corrispondenza esplicita, e dalla coda della corrispondenza alla coda della linea nello stesso modo. Se la tua corrispondenza esplicita non viene abbinata in modo esplicito , fallirà e non stampa nulla.

E quindi potresti fare, ad esempio:

grep '^\(.*:\)*/opt/gnome\(:.*\)*$'

Vedi tu stesso:

grep '^\(.*:\)*/opt/gnome\(:.*\)*$
' <<\INPUT
/opt/gnome-beta
/opt/gnome
/home/bob/opt/gnome
:/opt/gnome:
/home/bob/opt/gnome:/opt/gnome:/opt/gnome-beta
/opt-gnome-beta
/opt/gnomenot::::/opt/gnome
INPUT

PRODUZIONE

/opt/gnome
:/opt/gnome:
/home/bob/opt/gnome:/opt/gnome:/opt/gnome-beta
/opt/gnomenot::::/opt/gnome

0

hai notato un caso limite ... potresti evitarlo forzando l'apparizione di un: all'inizio della riga:

 echo ":$PATH" | grep ":/opt/gnome"

o se il percorso è esatto aggiungine anche uno alla fine per assicurarti che sia limitato:

 echo ":${PATH}:" | grep ":/opt/gnome:"
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.