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 find
para revisar todos los archivos .torrent pwd
y pasar cada archivo .torrent, a medida que llega, para transmission-show
analizar el archivo .torrent y generar metadatos en formato legible por humanos. Luego lo usaremos awk
para 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_NAME
se pasa directamente a bash -c
la -exec
opción de su find
comando. Esto causa problemas si FILE_NAME
contiene 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 -c
como 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íafindtor -f ~/list.txt
tomar patrones de la lista,-F
para cadenas fijas,-e expression
etc.
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 nullglob
no 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