디렉토리의 처음 몇 개의 파일을 반복하는 방법은 무엇입니까?

디렉토리의 처음 몇 개의 파일을 반복하는 방법은 무엇입니까?

현재 다음 zsh-snippet을 사용하여 추가 처리를 위해 작은 파일 배치를 선택하고 있습니다.

for f in $(ls /some/path/*.txt | head -2) ; do
  echo unpacking $f
  ./prepare.sh $f && rm -v $f
done

$(ls ... | head -2)zsh보다 더 나은 대안이 있습니까 ?

내 임무에 대한 일반적인 개요입니다.신경망을 훈련하기 위한 데이터 세트를 만들고 있습니다. 여기서는 기계 학습 작업의 세부 사항이 중요하지 않습니다. 데이터 세트 생성 작업을 수행하려면 많은 수의 파일을 수동으로 처리해야 했습니다. 이를 위해 나는 그것들을 별도의 디렉토리에 복사했습니다. 그런 다음 몇 개의 파일(이 예의 출력에서 ​​처음 두 개 ls)을 무작위로 선택하고, 일부 전처리 루틴을 호출하고, 결과를 확인하고, 그 중 일부를 생성 중인 데이터세트로 이동하고, 나머지는 삭제합니다. 청소 후 위의 명령을 다시 실행했습니다.

또한, 쉘 프로그래밍 기술을 향상시키고 새로운 것을 배우고 싶습니다 :)

이러한 "첫 번째" 파일이 선택되는 순서는 중요하지 않습니다. 결국 모든 파일이 처리되기 때문입니다.

즉, 나는 for루프 내에서 PC를 사용하여 작업하고 있으며 몇 번의 반복 후에 PC가 일시 중지되고 기다리기를 원합니다.

의사코드.

for f in /some/path/*.txt ; do
  echo unpacking $f
  ./prepare $f
  
  if human wants to review ; then
     human is reviewing then cleans, and PC waits
  fi
done

이 이상한 프로세스가 발생하는 이유는 .txt하나의 "소스" 파일을 전처리하면 수십 개의 다른 파일이 생성되고, 그 모든 파일을 살펴보고 네트워크 교육에 적합한 몇 가지 샘플(보통 1-2)을 선택해야 하기 때문입니다.

실행할 수 있지만 for f in /some/path/*.txt ; do ./prepare $f ; done이 명령은 수백 개의 파일을 생성하는데 이는 압도적입니다.

답변1

글로벌 예선

Glob 한정자는 대부분의 파일 사용 ls또는 find열거 파일을 대체할 수 있습니다. 이는 zsh의 고유한 기능입니다.

예를 들어, (사전순으로 파일을 열거하고 처음 두 파일만 유지)는 zsh 의 1$(ls /some/path/*.txt | head -2) 과 같습니다 . 한정자는 일치 항목이 없는 경우 목록이 비어 있는지 확인하고 한정자는 일치 항목을 지정된 범위로 제한합니다./some/path/*.txt(N[1,2])N[from,to]

한정자가 없으면 N기본 옵션에서 일치하는 파일이 없으면 오류 메시지와 함께 스크립트가 종료됩니다.

o또는 O한정자를 사용하여 파일 순서를 제어 할 수 있습니다 . 예를 들어 /some/path/*.txt(Nom[1,2])두 개의 최신 파일을 가져옵니다.

1 에는 일반적으로 zsh에 유리한 약간의 차이가 있습니다. 공백이나 개행 문자 또는 유효하지 않은 바이트 시퀀스와 같은 특수 문자가 포함된 파일 이름 에서 문제가 발생하는 경향이 있는 ls반면, zsh의 내장 기능은 모든 파일 이름에서 안정적으로 작동합니다. 극단적인 경우에는 오류 관리가 다릅니다. 여기서 옵션을 잊어버렸기 때문에 -d이러한 파일 중 일부가 다음 유형인 경우 ls에도 문제가 발생합니다.*.txt목차ls내용 도 나열됩니다.


하지만 두 파일을 모두 가져오는 것이 전반적인 목표를 달성하는 데 어떻게 도움이 되는지 모르겠습니다. 모든 파일을 처리하지만 사람들이 처음 몇 개의 파일만 볼 수 있도록 허용하는 방법을 원할 경우 단계/계속/중단 프롬프트를 표시할 수 있습니다. 이 같은:

pause=1
for f in /some/path/*.txt ; do
  print -ru2 unpacking $f
  ./prepare $f
  
  if ((pause)); then
    print -ru2 -- "$f output is ready for review."
    c=
    while [[ $c != [anq] ]]; do
      read -k1 "c?Process (N)ext, (A)ll, (Q)uit? " && c=${c:l}
    done
    echo
    case $c in
      a) pause=0;;
      q) break;;
    esac
  fi
done

답변2

루프에서 카운터를 사용할 수 있습니다 for. 이는 POSIX 호환 셸에서 작동합니다.

이는 질문의 첫 번째 코드 조각과 동일합니다.

i=0
for f in /some/path/*.txt ; do
    if [ "$((i += 1))" -gt 2 ] ; then
        break
    fi
    echo "unpacking $f"
    ./prepare.sh "$f" && rm -v "$f"
done

쓰여진대로자일스의 대답에는 zsh이를 보다 간단한 방법으로 달성할 수 있는 기능이 있습니다. glob 한정자에 대한 설명은 이 답변을 참조하세요.

for f in /some/path/*.txt(N[1,2]) ; do
    echo "unpacking $f"
    ./prepare.sh "$f" && rm -v "$f"
done

루프를 중단하는 대신 일부 입력을 기다릴 수도 있습니다.

i=0
for f in /some/path/*.txt ; do
    if [ "$((i += 1))" -gt 2 ] ; then
        i=1
        printf "press Enter to continue"
        read dummy
    fi
    printf "unpacking %s\n" "$f"
    ./prepare.sh "$f" && rm -v "$f"
done

참고 1:조건부 분기에서는 스크립트가 이미 이 반복에서 다음 그룹의 첫 번째 파일을 처리하고 있기 때문에 i=1대신 사용합니다.i=0

노트 2:루프 끝이 아닌 시작 부분에 개수와 조건을 넣었습니다. 왜냐하면 이 시점에서 처리할 다른 파일이 있다는 것이 분명하기 때문입니다. 이렇게 하면 마지막 파일 이후에 일시 중지되는 것을 방지할 수 있습니다.

노트 3:스크립트를 편집하고 따옴표를 추가했습니다 $((i += 1)).스티븐 차제라스하나에서 언급된논평대부분의 POSIX 호환 쉘에서는 산술 확장의 결과에 대해 와일드카드 및 분할이 수행됩니다. 이는 특히 IFS십진수가 포함될 때(비정상적인 경우라고 생각합니다) 원하지 않는 결과를 초래할 수 있습니다. 또한 패턴이 어떤 파일과도 일치하지 않으면 루프 반복이 수행되어 f리터럴 파일 이름 패턴으로 설정됩니다. 이로 인해 오류 메시지나 예상치 못한 결과가 발생할 수 있습니다. 이를 방지하려면 if [ -f "$f" ] ...다음을 사용하여 루프 본문을 조건부로 만드십시오 . 이러한 특별한 경우가 의도된(대화형) 사용과 관련이 없는 경우 스크립트를 더 간단하게 유지할 수 있습니다.

답변3

Bash에서는 파일 이름을 배열에 넣고 배열 조각을 반복할 수 있습니다. 그리고 루프는 다음과 같이 반복됩니다.

#!/bin/bash
shopt -s nullglob
files=(/some/path/*.txt)
for (( i = 0; i < "${#files[@]}"; i += 2)); do
    for f in "${files[@]:i:2}"; do
        printf "processing %s\n" "$f";
    done
    read -p "press enter to continue with the next set (or end)..."
done 

또는 마지막에 가능한 프롬프트를 피하기 위해 재구성하지만 단순성을 잃는 대가를 치르게 됩니다.

#!/bin/bash
shopt -s nullglob
files=(/some/path/*.txt)
i=0 
while true; do
    for f in "${files[@]:i:2}"; do
        printf "processing %s\n" "$f";
    done
    (( i += 2 ))
    (( i >= "${#files[@]}" )) && break
    read -p "press enter to continue with the next set..."
done

답변4

나는 사용하는 것이 좋습니다gnu-parallel

  1. 작업을 스크립트에 넣기
#!/bin/bash
echo unpacking ${1}
/full/path/to/prepare.sh ${1} && rm -v ${1}
  1. 실행 방법 gnu-parallel(실행 파일의 실제 이름은 배포판 및 프로그램 설치 방법에 따라 다를 수 있음)
parallel --halt soon,success=5 /path/to/script {} ::: *.txt

parallel기본적으로 CPU당 하나의 작업이 실행됩니다. 즉, --halt soon,success=5"5개의 작업이 성공하면 처리를 중지하지만 실행 중인 작업은 완료되도록 합니다"를 의미합니다. {}파일 이름을 대체하며 :::인수 목록의 구분 기호입니다.

사용자 측에서 "계속"을 기다리지 않지만 원본 파일을 삭제했으므로 프로세스를 다시 시작할 수 있으며 이중 작업은 없습니다.

관련 정보