Un script de shell simple incapaz de revisar miles de archivos; comienza bien, pero arroja un "EOF inesperado mientras busca `"`" coincidente después de un tiempo

Un script de shell simple incapaz de revisar miles de archivos; comienza bien, pero arroja un "EOF inesperado mientras busca `"`" coincidente después de un tiempo

Script de Shell en cuestión

Permítanme explicarles lo que estoy tratando de hacer, por ejemplo, para que puedan comprenderlo mejor. Digamos que tengo 100 archivos .torrent en un directorio. 2 de ellos descargarán xxx.epub y yyy.epub respectivamente si se agregan a un cliente bittorrent, pero no sé cuáles de los 100.

Entonces, lo que hace mi secuencia de comandos es (1) utilizar findpara revisar todos los archivos .torrent pwdy pasar cada archivo .torrent, a medida que llega, para transmission-showanalizar el archivo .torrent y generar metadatos en formato legible por humanos. Luego lo usaremos awkpara obtener el nombre del archivo que descargará el archivo torrent y lo ejecutaremos en list.txt que tiene los nombres de archivo que estamos buscando, es decir, xxx.epub y yyy.epub.

Archivo: findtor-array.sh

#! /bin/bash
#
# Search .torrent file based on 'Name' field.
#
# USAGE:
# cd ~/myspace # location of .torrent files
# Run `findtor ~/list.txt` (if `findtor.sh` is placed in `~/bin` or `~/.local/bin`)

# Turn the list of file names from ~/list.txt (or any file passed as argument) into an array
readarray -t FILE_NAMES_TO_SEARCH < "$1"

# For each file name from the list...
for FILE_NAME in "${FILE_NAMES_TO_SEARCH[@]}"
do
    # In `pwd` and 1 directory-level under, look for .torrent files and search them for the file name
    find . -maxdepth 2 -name '*.torrent' -type f -exec bash -c "transmission-show \"\$1\" | awk '/^Name\: / || /^File\: /' | awk -F ': ' '\$2 ~ \"$FILE_NAME\" {getline; print}'" _ {} \; >> ~/torrents.txt

    # The `transmission-show` command included in `find`, on it own, for clarity:
    # transmission-show xxx.torrent | awk '/^Name: / || /^File: /' | awk -F ': ' '$2 ~ "SEARCH STRING" {getline; print}'
done

Creo que el proceso es sencillo y lo estoy haciendo bien (excepto que no hay controles, lo sé). Pero de alguna manera toda la tarea parece demasiado para el script, porque después de ejecutarlo, después de un tiempo comienza a arrojar estos errores continuamente hasta que hago Ctrl+ C:

_: -c: line 0: unexpected EOF while looking for matching `"'
_: -c: line 1: syntax error: unexpected end of file

¿Son estos problemas de "escala"? ¿Qué me falta y qué puedo hacer para solucionarlo?

Respuesta1

FILE_NAMEse pasa directamente a bash -cla -execopción de su findcomando. Esto causa problemas si FILE_NAMEcontiene comillas/código shell. De hecho,se podría ejecutar código arbitrario. Ejemplo: en este caso particular, el archivo de entrada podría contener una línea'; echo "run commands";'

En su lugar, pase el bucle var bash -ccomo parámetro posicional. p.ej:

find . -maxdepth 2 -name '*.torrent' -type f -exec sh -c '
transmission-show "$2" |
awk -v search="$1" '\''/^Name: / {name = substr($0,7)} /^File: / && name ~ search {print; exit}'\' \
_ "$FILE_NAME" {} \;

Además, parece ineficiente recorrer todos los términos de búsqueda para cada archivo. Considere recorrer archivos y buscar con grep -f file:

find . -maxdepth 2 -name '*.torrent' -type f -exec sh -c '
file=$1
shift
if transmission-show "$file" | head -n 1 | cut -d" " -f2- | grep -q "$@"; then
    printf "%s\n" "$file"
fi' _ {} "$@" \;

o sin find:

for file in *.torrent */*.torrent; do
    if transmission-show "$file" | head -n 1 | cut -d' ' -f2- | grep -q "$@"; then
        printf '%s\n' "$file"
    fi
done
  • Lo anterior simplemente pasa todos los argumentos a grep, por lo que el uso sería findtor -f ~/list.txttomar patrones de la lista, -Fpara cadenas fijas, -e expressionetc.

Respuesta2

Basado en sugerencias de @Kusalananda, las respuestas (de @guest y @Jetchisel), yesta respuesta detallada de Kevin, se me ocurrió esto:

#! /bin/bash
#
# Search for 'Name' field match in torrent metadata for all .torrent files in
# current directory and directories 1-level below.
#
# USAGE e.g.:
# cd ~/torrent-files # location of .torrent files
# Run `~/findtor.sh ~/list.txt`

# Get one file name at a time ($FILE_NAME_TO_SEARCH) to search for from list.txt
# provided as argument to this script.
while IFS= read -r FILE_NAME_TO_SEARCH; do

    # `find` .torrent files in current directory and directories 1-level under
    # it. `-print0` to print the full file name on the standard output, followed
    # by a null character (instead of the newline character that `-print` uses).
    #
    # While that's happening, we'll again use read, this time to pass one
    # .torrent file at a time (from output of `find`) to `transmission-show`
    # for the latter to output the metadata of the torrent file, followed by
    # `awk` commands to look for the file name match ($FILE_NAME_TO_SEARCH) from
    # list.txt.
    find . -maxdepth 2 -name '*.torrent' -type f -print0 |
        while IFS= read -r -d '' TORRENT_NAME; do
            transmission-show "$TORRENT_NAME" | awk '/^Name: / || /^File: /' | awk -F ': ' -v search_string="$FILE_NAME_TO_SEARCH" '$2 ~ search_string {getline; print}';
        done >> ~/torrents-found.txt

done < "$1"

Acabo de ejecutar esto y hasta ahora parece estar funcionando muy bien. ¡Muchas gracias a todos los involucrados!

Si bien hice lo mejor que pude, cualquier corrección y sugerencia adicional es bienvenida.

Respuesta3

Yo lo escribiría así.

#!/usr/bin/env bash

pattern_file="$1"

while IFS= read -r -d '' file; do
    transmission-show "$file" | awk .... "$pattern_file"   ##: Figure out how to do the awk with a file rather than looping through an array.
done < <(find . -maxdepth 2 -name '*.torrent' -type f -print0)

Eso debería evitar el infierno de las citas :-)

Ok, tal vez nullglobno sea necesario.

EDITAR:

Pruebe el comando de búsqueda y utilícelo en su script original.

find . -maxdepth 2 -name '*.torrent' -type f -exec bash -c 'transmission-show "$1" | awk "/^Name\: / || /^File\: /" | awk -F ": " "\$2 ~ \"$FILE_NAME\" {getline; print}"' _ {} + >> ~/torrents.txt

información relacionada