Comando de Linux: ¿Cómo 'buscar' solo archivos de texto?


100

Después de algunas búsquedas en Google, lo que se me ocurre es:

find my_folder -type f -exec grep -l "needle text" {} \; -exec file {} \; | grep text

que es muy poco práctico y genera textos innecesarios como información de tipo mimo. ¿Alguna mejor solución? Tengo muchas imágenes y otros archivos binarios en la misma carpeta con muchos archivos de texto que necesito buscar.

Respuestas:


184

Sé que este es un hilo antiguo, pero me encontré con él y pensé en compartir mi método, que he descubierto que es una forma muy rápida de usar findpara encontrar solo archivos no binarios:

find . -type f -exec grep -Iq . {} \; -print

La -Iopción grep le dice que ignore inmediatamente los archivos binarios y la .opción junto con -qhará que coincida inmediatamente con los archivos de texto, por lo que va muy rápido. Puede cambiar el -printa a -print0para la tubería en un xargs -0o algo si le preocupan los espacios (¡gracias por el consejo, @ lucas.werkmeister!)

Además, el primer punto solo es necesario para ciertas versiones de BSD find, como en OS X, pero no hace daño tenerlo allí todo el tiempo si quieres ponerlo en un alias o algo así.

EDITAR : Como @ruslan señaló correctamente, -andse puede omitir ya que está implícito.


16
En Mac OS X, necesito cambiar esto a find . -type f -exec grep -Il "" {} \;.
Alec Jacobson

3
Esto es mejor que la respuesta de peoro porque 1. En realidad responde la pregunta 2. No produce falsos positivos 3. Es mucho más
eficiente

3
También puede usar lo find -type f -exec grep -Iq . {} \; -and -printque tiene la ventaja de que mantiene los archivos find; puede sustituirlo -printpor otro -execque solo se ejecute para archivos de texto. (Si deja grepimprimir los nombres de los archivos, no podrá distinguir los nombres de los archivos con nuevas líneas).
Lucas Werkmeister

1
@ NathanS.Watson-Haigh No debería, porque debería coincidir inmediatamente con los archivos de texto. ¿Tiene un caso de uso específico que pueda compartir?
crudcore

2
find . -type f -exec grep -Il . {} +es mucho más rápido. El inconveniente es que no se puede extender por otro -execcomo sugirió @ lucas.werkmeister
Henning


10

¿Por qué es desagradable? Si necesita usarlo con frecuencia y no quiere escribirlo cada vez, simplemente defina una función bash para él:

function findTextInAsciiFiles {
    # usage: findTextInAsciiFiles DIRECTORY NEEDLE_TEXT
    find "$1" -type f -exec grep -l "$2" {} \; -exec file {} \; | grep text
}

ponlo en tu .bashrcy luego ejecuta:

findTextInAsciiFiles your_folder "needle text"

cuando quieras.


EDITAR para reflejar la edición de OP:

Si desea eliminar la información de mímica, puede simplemente agregar una etapa adicional a la tubería que filtra la información de mímica. Esto debería hacer el truco, tomando sólo lo que viene antes :: cut -d':' -f1:

function findTextInAsciiFiles {
    # usage: findTextInAsciiFiles DIRECTORY NEEDLE_TEXT
    find "$1" -type f -exec grep -l "$2" {} \; -exec file {} \; | grep text | cut -d ':' -f1
}

No estoy seguro de si "grep text" es lo suficientemente preciso para obtener exactamente todos los archivos de texto; quiero decir, ¿hay algún tipo de archivo de texto que no tenga "texto" en la cadena de su descripción de tipo mime?
datasn.io

@ kavoir.com: sí. Del filemanual: "Los usuarios dependen de saber que todos los archivos legibles en un directorio tienen impresa la palabra 'texto'".
peoro

2
¿No sería un poco más inteligente buscar archivos de texto antes de grepping, en lugar de grepping y luego filtrar los archivos de texto?
usuario desconocido

/proc/meminfo, /proc/cpuinfoetc. son archivos de texto, pero file /proc/meminfodice /proc/meminfo: empty. Me pregunto si debería probarse 'vacío' además de 'texto', pero no estoy seguro de si también otros tipos podrían informar 'vacío'.
Timo Kähkönen

"¿Por qué es desagradable?" - "salidas de textos innecesarios". Esta respuesta no soluciona eso.
user123444555621

4
find . -type f -print0 | xargs -0 file | grep -P text | cut -d: -f1 | xargs grep -Pil "search"

Desafortunadamente, esto no es un ahorro de espacio. Poner esto en el script bash lo hace un poco más fácil.

Esto es espacio seguro:

#!/bin/bash
#if [ ! "$1" ] ; then
    echo "Usage: $0 <search>";
    exit
fi

find . -type f -print0 \
  | xargs -0 file \
  | grep -P text \
  | cut -d: -f1 \
  | xargs -i% grep -Pil "$1" "%"

2
Hay un par de problemas en su script: 1. ¿Qué pasa si un archivo binario tiene un nombre text.bin? 2. ¿Qué pasa si un nombre de archivo contiene un :?
thkala

3

Otra forma de hacer esto:

# find . |xargs file {} \; |grep "ASCII text"

Si también quieres archivos vacíos:

#  find . |xargs file {} \; |egrep "ASCII text|empty"

2

Qué tal esto:

$ grep -rl "needle text" my_folder | tr '\n' '\0' | xargs -r -0 file | grep -e ':[^:]*text[^:]*$' | grep -v -e 'executable'

Si desea los nombres de archivo sin los tipos de archivo, simplemente agregue un sedfiltro final .

$ grep -rl "needle text" my_folder | tr '\n' '\0' | xargs -r -0 file | grep -e ':[^:]*text[^:]*$' | grep -v -e 'executable' | sed 's|:[^:]*$||'

Puede filtrar los tipos de archivos innecesarios agregando más -e 'type'opciones al último grepcomando.

EDITAR:

Si su xargsversión admite la -dopción, los comandos anteriores se vuelven más simples:

$ grep -rl "needle text" my_folder | xargs -d '\n' -r file | grep -e ':[^:]*text[^:]*$' | grep -v -e 'executable' | sed 's|:[^:]*$||'

tonto de mí. No noté grep recursivo. según entendí, en realidad es bastante rápido, aunque un poco limitado en muchas aplicaciones. +1 para ti.
Antti Rytsölä

2

Así es como lo he hecho ...

1. haga un pequeño script para probar si un archivo es texto plano istext:

#!/bin/bash
[[ "$(file -bi $1)" == *"file"* ]]

2. usa buscar como antes

find . -type f -exec istext {} \; -exec grep -nHi mystring {} \;

¿Supongo que te refieres == *"text"* ]]?
usuario desconocido

Puede utilizar el operador de coincidencia `= ~" text "]]` en su lugar.
usuario desconocido

2

Tengo dos problemas con la respuesta de histumness:

  • Solo enumera archivos de texto. En realidad, no los busca según lo solicitado. Para buscar realmente, use

    find . -type f -exec grep -Iq . {} \; -and -print0 | xargs -0 grep "needle text"
    
  • Genera un proceso grep para cada archivo, que es muy lento. Una mejor solución es entonces

    find . -type f -print0 | xargs -0 grep -IZl . | xargs -0 grep "needle text"
    

    o simplemente

    find . -type f -print0 | xargs -0 grep -I "needle text"
    

    Esto solo toma 0.2s en comparación con 4s para la solución anterior (2.5GB de datos / 7700 archivos), es decir, 20 veces más rápido .

Además, nadie citó a ag, Silver Searcher o ack-grep como alternativas. Si alguno de estos está disponible, son alternativas mucho mejores:

ag -t "needle text"    # Much faster than ack
ack -t "needle text"   # or ack-grep

Como última nota, tenga cuidado con los falsos positivos (archivos binarios tomados como archivos de texto). Ya tuve un falso positivo usando grep / ag / ack, así que es mejor enumerar los archivos coincidentes primero antes de editar los archivos.


1

Aunque es una pregunta antigua, creo que esta información a continuación se sumará a la calidad de las respuestas aquí.

Al ignorar archivos con el bit ejecutable configurado, solo uso este comando:

find . ! -perm -111

Para evitar que entre de forma recursiva en otros directorios:

find . -maxdepth 1 ! -perm -111

No es necesario que las tuberías mezclen muchos comandos, solo el poderoso comando de búsqueda simple .

  • Descargo de responsabilidad: no es exactamente lo que pidió OP, porque no verifica si el archivo es binario o no. Por ejemplo, filtrará los archivos de script de bash , que son texto en sí mismos pero tienen el bit ejecutable configurado .

Dicho esto, espero que esto sea útil para cualquiera.


0

Lo hago de esta manera: 1) dado que hay demasiados archivos (~ 30k) para buscar, genero la lista de archivos de texto diariamente para usar a través de crontab usando el siguiente comando:

find /to/src/folder -type f -exec file {} \; | grep text | cut -d: -f1 > ~/.src_list &

2) crea una función en .bashrc:

findex() {
    cat ~/.src_list | xargs grep "$*" 2>/dev/null
}

Entonces puedo usar el siguiente comando para hacer la búsqueda:

findex "needle text"

HTH :)


0

Yo prefiero xargs

find . -type f | xargs grep -I "needle text"

si sus nombres de archivo son raros, busque usando las opciones -0:

find . -type f -print0 | xargs -0 grep -I "needle text"

0
  • ejemplo de bash para buscar texto "eth0" en / etc en todos los archivos de texto / ascii

grep eth0 $ (buscar / etc / -type f -exec file {} \; | egrep -i "text | ascii" | cut -d ':' -f1)


0

Aquí hay una versión simplificada con una explicación extendida para principiantes como yo que están tratando de aprender a poner más de un comando en una línea.

Si tuviera que escribir el problema en pasos, se vería así:

// For every file in this directory
// Check the filetype
// If it's an ASCII file, then print out the filename

Para lograr esto, podemos utilizar tres comandos UNIX: find, file, y grep.

find comprobará todos los archivos del directorio.

filenos dará el tipo de archivo. En nuestro caso, estamos buscando un retorno de 'texto ASCII'

grep buscará la palabra clave 'ASCII' en la salida de file

Entonces, ¿cómo podemos unirlos en una sola línea? Hay varias formas de hacerlo, pero creo que hacerlo en el orden de nuestro pseudocódigo tiene más sentido (especialmente para un principiante como yo).

find ./ -exec file {} ";" | grep 'ASCII'

Parece complicado, pero no está mal cuando lo desglosamos:

find ./= revise todos los archivos de este directorio. El findcomando imprime el nombre de archivo de cualquier archivo que coincida con la 'expresión', o lo que venga después de la ruta, que en nuestro caso es el directorio actual o./

Lo más importante que debe comprender es que todo lo que esté después del primer bit se evaluará como Verdadero o Falso. Si es True, se imprimirá el nombre del archivo. Si no es así, el comando sigue adelante.

-exec= esta bandera es una opción dentro del comando de búsqueda que nos permite usar el resultado de algún otro comando como expresión de búsqueda. Es como llamar a una función dentro de una función.

file {}= el comando que se llama dentro de find. El filecomando devuelve una cadena que le indica el tipo de archivo de un archivo. Regularmente, se vería así: file mytextfile.txt. En nuestro caso, queremos que use cualquier archivo que esté siendo examinado por el findcomando, por lo que colocamos las llaves {}para que actúe como una variable vacía o parámetro. En otras palabras, solo estamos pidiendo que el sistema genere una cadena para cada archivo en el directorio.

";"= esto es requerido por findy es el signo de puntuación al final de nuestro -execcomando. Consulte el manual de 'buscar' para obtener más explicaciones si lo necesita ejecutándolo man find.

| grep 'ASCII'= |es una pipa. Pipe toma la salida de lo que está a la izquierda y la usa como entrada para lo que está a la derecha. Toma la salida del findcomando (una cadena que es el tipo de archivo de un solo archivo) y la prueba para ver si contiene la cadena 'ASCII'. Si lo hace, devuelve verdadero.

AHORA, la expresión a la derecha de find ./devolverá verdadero cuando el grepcomando devuelva verdadero. Voila.


0

Si está interesado en encontrar cualquier tipo de archivo por sus bytes mágicos usando la increíble fileutilidad combinada con el poder de find, esto puede ser útil:

$ # Let's make some test files
$ mkdir ASCII-finder
$ cd ASCII-finder
$ dd if=/dev/urandom of=binary.file bs=1M count=1
1+0 records in
1+0 records out
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.009023 s, 116 MB/s
$ file binary.file
binary.file: data
$ echo 123 > text.txt
$ # Let the magic begin
$ find -type f -print0 | \
    xargs -0 -I @@ bash -c 'file "$@" | grep ASCII &>/dev/null && echo "file is ASCII: $@"' -- @@

Salida:

file is ASCII: ./text.txt

Leyenda: $es el indicador de shell interactivo donde ingresamos nuestros comandos

Puede modificar la parte posterior &&para llamar a otro script o hacer otras cosas en línea también, es decir, si ese archivo contiene una cadena determinada, seleccione el archivo completo o busque una cadena secundaria en él.

Explicación:

  • find elementos que son archivos
  • Haga que xargscada elemento se alimente como una línea en un bash comando / script de línea
  • fileverifica el tipo de archivo por byte mágico, grepverifica si existe ASCII, si es así, luego de que se &&ejecute el siguiente comando.
  • findimprime los resultados nullseparados, esto es bueno para escapar de los nombres de archivo con espacios y metacaracteres en él.
  • xargs, usando la -0opción, los lee nullseparados, -I @@ toma cada registro y usa como parámetro posicional / argumentos para bash script.
  • --for bashasegura que todo lo que viene después es un argumento, incluso si comienza con -like, -cque de lo contrario podría interpretarse como una opción bash

Si necesita buscar tipos distintos de ASCII, simplemente reemplácelos grep ASCIIcon otro tipo, comogrep "PDF document, version 1.4"


-1
find . -type f | xargs file | grep "ASCII text" | awk -F: '{print $1}'

Use el comando de búsqueda para listar todos los archivos, use el comando de archivo para verificar que sean texto (no tar, clave), finalmente use el comando awk para filtrar e imprimir el resultado.


-4

Qué tal esto

 find . -type f|xargs grep "needle text"

Esto no busca"needle text"
peoro

@Navi: el OP de ejemplo proporcionado solo encuentra archivos que contienen"needl text"
peoro

3
@Navi: ahora ya no busca archivos de texto: si un archivo binario lo contiene "needle text", se encontrará
peoro

¿Por qué te estoy escuchando?
Navi

1
@Navi: su one-liner no verifica los tipos de archivos y también tiene problemas importantes con los espacios en blanco en los nombres de archivos ...
thkala
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.