¿Cómo poner una búsqueda de cadena con el comando grep en la instrucción if?

11

Quiero buscar múltiples cadenas en dos archivos. Si se encuentra una cadena en ambos archivos, haga algo. Si se encuentra una cadena en un solo archivo, haga otra cosa.

Mis comandos son los siguientes:

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

¿Es correcta la forma de negar y afirmar las declaraciones? pd Estoy usando KSH shell.

Gracias de antemano.

Mareyes
fuente

Respuestas:

11

Prueba esto:

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 puede buscar patrones en varios archivos, por lo que no es necesario usar un operador OR / NOT.
msp9011
fuente
¿Hay alguna diferencia mínima entre "-wq" y "-qw" en cada comando?
Mareyes
2
No, ambos proporcionan las opciones -q y -w a grep.
Glenn Jackman
3
¿Por qué no hay comillas en la expansión variable?
D. Ben Knoble
@ D.BenKnoble, ¿es correcto: "$ user" "$ file1"?
Mareyes
1
@Mareyes sí, eso es correcto
D. Ben Knoble
13

Otra opción:

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
Glenn Jackman
fuente
Hola, gracias por la forma opcional. ¡Funciona bastante bien! funciona perfectamente en otro script que tengo.
Mareyes
¡Es una gran idea! Siempre aprendiendo.
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++))es una extensión ksh (también compatible con zshy bash). En la shsintaxis POSIX , necesitaría en su n=$((n + 1))lugar.

Luciano Andress Martini
fuente
Oh, perdón, estaba escribiendo $ ((n ++)) en lugar de ((n ++)). Olvidar.
Luciano Andress Martini
1
Tenga en cuenta que n=$((n++))NUNCA cambiará el valor de n porque n++es un incremento posterior : devuelve el valor actual de n, luego incrementa n; luego establece la variable al valor devuelto que era el n original.
Glenn Jackman
Si puede asumir que sus nombres de archivo no contienen nuevas líneas local IFS=$'\n'; matches=( $(grep -lw "$users" "$file1" "$file2") ), y use case "${#matches[@]" in. @glenn: esta idea también se aplica a su respuesta, para evitar múltiples invocaciones de grep. Instalarlo grep -l | wc -ltambién funcionaría.
Peter Cordes
@Peter que valdría la pena escribir como una respuesta separada de la OMI.
Stephen Kitt el
@StephenKitt: no iba a hacerlo porque no pude encontrar una forma a prueba de balas para manejar caracteres arbitrarios de nombre de archivo. Pero como crees que vale la pena, lo escribí de todos modos.
Peter Cordes
2

Si sus nombres de archivo no contienen nuevas líneas, puede evitar múltiples invocaciones grephaciendo que grep imprima los nombres de los archivos coincidentes y cuente los resultados.

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

El número de partidos es "${#matches[@]}".

Puede haber una forma de usar grep --null -lwaquí, pero no estoy seguro de cómo analizar la salida . Bash var=( array elements )no tiene una forma de usar un \0delimitador en lugar de \n. Tal vez bashtin mapfileincorporado puede hacerlo? Pero probablemente no, porque especificas el delimitador con -d string.


Podría count=$(grep -l | wc -l), pero luego tiene dos procesos externos, por lo que podría ejecutar greplos dos archivos por separado. (La diferencia entre la sobrecarga de inicio grepy la de wcinicio es pequeña en comparación con las cosas fork + exec + dynamic linker para iniciar un proceso por separado).

Además, con wc -lusted no descubre qué archivo coincide.


Con los resultados capturados en una matriz, eso podría ser lo que desea, o si hay exactamente 1 coincidencia, puede verificar si fue la primera entrada o no.

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

$matcheses la abreviatura de ${matches[0]}, el primer elemento de matriz.

Peter Cordes
fuente
@StephenKitt: Quise decir -z, no -0, vaya. Sí, eso obtendría grep para imprimir los nombres de archivo separados por \0como ya mencioné en la respuesta, pero ¿cómo puede hacer que bash analice eso? No puede establecer IFS=$'\0', o básicamente hacer cualquier otra cosa con find -print0salida de estilo directamente con bash.
Peter Cordes
Sí, leí demasiado rápido, me perdí ese párrafo y no pensé las cosas detenidamente (por eso eliminé mi comentario). Puede que haya una manera, pero no puedo pensar en una por el momento, y como usted dice, no vale la pena hacer que la solución sea demasiado compleja cuando de greptodos modos solo hay algunas cosas que tratar.
Stephen Kitt el
1
mapfile -d $'\0'funciona, pero reemplaza las nuevas líneas con espacios (no he intentado ajustar IFS).
Stephen Kitt el