Einfaches Shell-Skript kann Tausende von Dateien nicht durchsuchen; startet einwandfrei, gibt aber nach einiger Zeit „unerwartetes EOF bei der Suche nach übereinstimmenden `"`" aus

Einfaches Shell-Skript kann Tausende von Dateien nicht durchsuchen; startet einwandfrei, gibt aber nach einiger Zeit „unerwartetes EOF bei der Suche nach übereinstimmenden `"`" aus

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) findDurchsucht alle Torrent-Dateien pwdund übergibt jede Torrent-Datei, sobald sie eintrifft, an transmission-showdas die Torrent-Datei analysiert und Metadaten in einem für Menschen lesbaren Format ausgibt. Anschließend verwenden wir, awkum 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_NAMEbash -cwird direkt in der -execOption Ihres Befehls übergeben find. Dies verursacht Probleme, wenn FILE_NAMEAnfü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 -cals 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.txtMuster aus der Liste zu übernehmen, -Ffür feste Zeichenfolgen -e expressionusw.

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

verwandte Informationen