수천 개의 파일을 처리할 수 없는 간단한 쉘 스크립트입니다. 정상적으로 시작되지만 시간이 지나면 일치하는 ''''을 찾는 동안 '예기치 않은 EOF'가 발생합니다.

수천 개의 파일을 처리할 수 없는 간단한 쉘 스크립트입니다. 정상적으로 시작되지만 시간이 지나면 일치하는 ''''을 찾는 동안 '예기치 않은 EOF'가 발생합니다.

문제의 쉘 스크립트

예를 들어 당신이 더 잘 이해할 수 있도록 내가 하려는 일을 설명하겠습니다. 디렉토리에 100개의 .torrent 파일이 있다고 가정해 보겠습니다. 비트토렌트 클라이언트에 추가하면 그 중 2개는 각각 xxx.epub 및 yyy.epub를 다운로드하지만 100개 중 어느 2개인지는 모르겠습니다.

따라서 내 스크립트가 수행하는 작업은 (1) find모든 .torrent 파일을 살펴 pwd보고 각 .torrent 파일을 전달하는 것입니다. 이 transmission-show파일은 .torrent 파일을 구문 분석하고 사람이 읽을 수 있는 형식으로 메타데이터를 출력합니다. 그런 다음 awk토렌트 파일이 다운로드할 파일 이름을 가져오고 찾고 있는 파일 이름이 있는 list.txt(예: xxx.epub 및 yyy.epub)에 대해 실행합니다.

파일: 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

나는 그 과정이 간단하고 올바르게 하고 있다고 생각합니다(검사가 없다는 것을 제외하고는요). 하지만 어쩐지 전체 작업이 스크립트에 너무 많은 것 같습니다. 왜냐하면 스크립트를 실행한 후 언젠가는 I Ctrl+ Cit까지 이러한 오류가 계속해서 발생하기 시작하기 때문입니다.

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

이러한 "스케일링" 문제가 있습니까? 내가 놓치고 있는 부분은 무엇이며 이를 해결하려면 어떻게 해야 합니까?

답변1

FILE_NAME명령 옵션 bash -c으로 직접 전달됩니다 . 따옴표/셸 코드가 포함되어 있으면 문제가 발생합니다 . 사실은,-execfindFILE_NAME임의의 코드가 실행될 수 있음. 예: 이 특별한 경우에는 입력 파일에 다음 줄이 포함될 수 있습니다.'; echo "run commands";'

대신 루프 var를 bash -c위치 매개변수로 전달하세요. 예:

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" {} \;

또한 각 파일에 대해 모든 검색어를 반복하는 것은 비효율적입니다. 파일을 반복하고 다음을 사용하여 검색하는 것을 고려하십시오 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' _ {} "$@" \;

또는 없이 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
  • 위의 내용은 단순히 모든 인수를 에 전달하므로 고정 문자열 등 의 경우 목록에서 패턴을 가져오는 것이 grep사용됩니다 .findtor -f ~/list.txt-F-e expression

답변2

@Kusalananda의 제안, 답변(@guest 및 @Jetchisel 제공) 및Kevin의 자세한 답변, 나는 이것을 생각해 냈습니다 :

#! /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"

방금 이것을 실행했는데 지금까지는 훌륭하게 작동하는 것 같습니다. 참여해주신 모든 분들께 진심으로 감사드립니다!

최선을 다했지만 수정 사항이나 추가 제안을 환영합니다.

답변3

나는 이렇게 쓸 것입니다.

#!/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)

그것은 인용 지옥을 피해야합니다 :-)

좋아 어쩌면 nullglob필요하지 않을 수도 있습니다.

편집하다:

find 명령을 시도하고 원본 스크립트에서 사용하십시오.

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

관련 정보