Befragtes Shell-Skript
Damit Sie es besser verstehen, möchte ich Ihnen anhand eines Beispiels erklären, was ich versuche. Nehmen wir an, ich habe 100 Torrent-Dateien in einem Verzeichnis. Wenn sie zu einem BitTorrent-Client hinzugefügt werden, laden 2 davon jeweils xxx.epub und yyy.epub herunter, aber ich weiß nicht, welche 2 der 100 das sind.
Mein Skript macht also Folgendes: (1) find
Durchsucht alle Torrent-Dateien pwd
und übergibt jede Torrent-Datei, sobald sie eintrifft, an transmission-show
das die Torrent-Datei analysiert und Metadaten in einem für Menschen lesbaren Format ausgibt. Anschließend verwenden wir, awk
um den Dateinamen abzurufen, den die Torrent-Datei herunterladen wird, und führen dies mit der Datei list.txt aus, die die gesuchten Dateinamen enthält, also xxx.epub und yyy.epub.
Datei: 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
Ich denke, der Vorgang ist einfach und ich mache es richtig (außer, dass es keine Prüfungen gibt, ich weiß). Aber irgendwie scheint die ganze Aufgabe zu viel für das Skript zu sein, denn nach dem Ausführen fängt es nach einer Weile an, diese Fehler kontinuierlich auszugeben, bis ich Ctrl+ Ces mache:
_: -c: line 0: unexpected EOF while looking for matching `"'
_: -c: line 1: syntax error: unexpected end of file
Handelt es sich hierbei um „Skalierungsprobleme“? Was übersehe ich und was kann ich tun, um das Problem zu beheben?
Antwort1
FILE_NAME
bash -c
wird direkt in der -exec
Option Ihres Befehls übergeben find
. Dies verursacht Probleme, wenn FILE_NAME
Anführungszeichen/Shell-Code enthalten sind. Tatsächlichbeliebiger Code könnte ausgeführt werdenBeispiel: In diesem speziellen Fall könnte die Eingabedatei eine Zeile enthalten'; echo "run commands";'
Übergeben Sie stattdessen die Schleifenvariable bash -c
als Positionsparameter. Beispiel:
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" {} \;
Außerdem erscheint es ineffizient, alle Suchbegriffe für jede Datei zu durchlaufen. Erwägen Sie, alle Dateien zu durchlaufen und mit folgendem zu suchen 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' _ {} "$@" \;
oder ohne 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
- Das Obige übergibt einfach alle Argumente an
grep
, die Verwendung besteht also darin,findtor -f ~/list.txt
Muster aus der Liste zu übernehmen,-F
für feste Zeichenfolgen-e expression
usw.
Antwort2
Basierend auf Vorschlägen von @Kusalananda, den Antworten (von @guest und @Jetchisel) unddiese ausführliche Antwort von Kevin, ist mir Folgendes eingefallen:
#! /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"
Ich habe das gerade ausgeführt und bisher scheint es großartig zu funktionieren. Also ein großes Dankeschön an alle Beteiligten!
Ich habe mein Bestes gegeben, freue mich jedoch über alle Korrekturen und weiteren Vorschläge.
Antwort3
Ich würde es so schreiben.
#!/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)
Damit sollte die Zitierhölle vermieden werden :-)
Ok, vielleicht nullglob
ist das nicht nötig.
BEARBEITEN:
Probieren Sie den Find-Befehl aus und verwenden Sie ihn für Ihr Originalskript.
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