Come inserire una ricerca di stringhe con il comando grep nell'istruzione if?


11

Voglio cercare più stringhe in due file. Se viene trovata una stringa in entrambi i file, crea qualcosa. Se una stringa viene trovata in un solo file, crea un'altra cosa.

I miei comandi sono i seguenti:

####This is for the affirmative sentence in both files
if grep -qw "$users" "$file1" && grep -qw "$users" "$file2"; then

####This is for the affirmative sentence in only one file, and negative for the other one
if grep -qw "$users" "$file1" ! grep -qw "$users" "$file2"; then

è un modo corretto di negare e affermare le dichiarazioni? pd sto usando la shell KSH.

Grazie in anticipo.

Risposte:


11

Prova questo:

if grep -wq -- "$user" "$file1" && grep -wq -- "$user" "$file2" ; then
   echo "string avail in both files"
elif grep -wq -- "$user" "$file1" "$file2"; then
   echo "string avail in only one file"
fi
  • grep può cercare modelli in più file, quindi non è necessario utilizzare un operatore OR / NOT.

C'è una differenza minima tra "-wq" e "-qw" in ogni comando?
Mareyes,

2
No, entrambi forniscono le opzioni -q e -w per grep.
Glenn Jackman,

3
Perché nessuna virgoletta sull'espansione variabile?
D. Ben Knoble,

@ D.BenKnoble è corretto: "$ user" "$ file1"?
Mareyes,

1
@Mareyes sì, è corretto
D. Ben Knoble

13

Un'altra opzione:

grep -qw -- "$users" "$file1"; in_file1=$?
grep -qw -- "$users" "$file2"; in_file2=$?

case "${in_file1},${in_file2}" in
    0,0) echo found in both files ;;
    0,*) echo only in file1 ;;
    *,0) echo only in file2 ;;
      *) echo in neither file ;;
esac

Ehi, grazie per il modo opzionale. Funziona abbastanza bene !! funziona perfettamente in un'altra sceneggiatura che ho.
Mareyes,

Questa è una grande idea! Sempre imparando.
Joe,

7
n=0

#Or if you have more files to check, you can put your while here. 
grep -qw -- "$users" "$file1" && ((n++))
grep -qw -- "$users" "$file2" && ((n++))

case $n in 
   1) 
       echo "Only one file with the string"
    ;;
   2)
       echo "The two files are with the string"
   ;;
   0)
       echo "No one file with the string"
   ;;
   *)
       echo "Strange..."
   ;;
esac 

Nota: ((n++))è un'estensione ksh (supportata anche da zshe bash). Nella shsintassi POSIX , avresti n=$((n + 1))invece bisogno .


Oh scusa stavo scrivendo $ ((n ++)) invece di ((n ++)). Dimenticare.
Luciano Andress Martini,

1
Nota che n=$((n++))non cambierà MAI il valore di n perché n++è post incremento: restituisce il valore corrente di n, quindi incrementa n; quindi si imposta la variabile sul valore restituito che era l'originale n.
Glenn Jackman,

Se puoi presumere che i tuoi nomi di file non contengano nuove righe local IFS=$'\n'; matches=( $(grep -lw "$users" "$file1" "$file2") )e utilizzali case "${#matches[@]" in. @glenn: questa idea si applica anche alla tua risposta, per evitare invocazioni multiple di grep. Anche il piping su grep -l | wc -lavrebbe funzionato.
Peter Cordes,

@Peter che varrebbe la pena scrivere come risposta separata IMO.
Stephen Kitt,

@StephenKitt: non lo avrei fatto perché non riuscivo a trovare un modo a prova di proiettile per gestire i caratteri di nomi di file arbitrari. Ma poiché pensi che ne valga la pena, l'ho scritto comunque.
Peter Cordes,

2

Se i nomi dei file non contengono nuove righe, è possibile evitare più invocazioni grepfacendo stampare a grep i nomi dei file corrispondenti e contare i risultati.

 local IFS=$'\n'    # inside a function.  Otherwise use some other way to save/restore IFS
 matches=( $(grep -lw "$users" "$file1" "$file2") )

Il numero di partite è "${#matches[@]}".

Potrebbe esserci un modo per utilizzare grep --null -lwqui, ma non sono sicuro di come analizzare l'output . Bash var=( array elements )non ha un modo di usare un \0delimitatore invece di \n. Forse l'integrato di Bash mapfilepuò farlo? Ma probabilmente no, perché si specifica il delimitatore con -d string.


Potresti count=$(grep -l | wc -l), ma poi hai due processi esterni, quindi potresti anche solo eseguire grepi due file separatamente. (La differenza tra grepvs. wcoverhead di avvio è piccola rispetto a roba fork + exec + Dynamic Linker per avviare un processo separato).

Inoltre, con wc -lte non scopri quale file corrisponde.


Con i risultati acquisiti in un array, potrebbe essere già quello che vuoi o se esiste esattamente 1 corrispondenza, puoi verificare se è stato il primo input o meno.

local IFS=$'\n'    # inside a function.  Otherwise use some other way to save/restore IFS
matches=( $(grep -lw "$users" "$file1" "$file2") )

# print the matching filenames
[[ -n $matches ]] && printf  'match in %s\n'  "${matches[@]}"

# figure out which input position the name came from, if there's exactly 1.
if [[ "${#matches[@]" -eq 1 ]]; then
    if [[ $matches == "$file1" ]];then
        echo "match in file1"
    else
        echo "match in file2"
    fi
fi

$matchesè una scorciatoia per ${matches[0]}, il primo elemento dell'array.


@StephenKitt: intendevo -z, no -0, oops. Sì, ottenere grep per stampare i nomi dei file separati da \0come ho già menzionato nella risposta, ma come si può ottenere bash per analizzarlo? Non puoi impostare IFS=$'\0', o fondamentalmente fare qualsiasi altra cosa con l' find -print0output di stile direttamente con bash.
Peter Cordes,

Sì, ho letto troppo in fretta, mancando quel paragrafo e non ho riflettuto bene (motivo per cui ho eliminato il mio commento). Potrebbe esserci un modo, ma non riesco a pensarne uno proprio ora, e come dici tu non vale la pena rendere la soluzione troppo complessa quando ci sono solo pochi grepsecondi da affrontare comunque.
Stephen Kitt,

1
mapfile -d $'\0'in qualche modo funziona, ma sostituisce le nuove righe con spazi (non ho provato a modificare IFS).
Stephen Kitt,
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.