for 루프에서 파일 이름을 읽으려면 find 명령을 실행하는 데 도움이 필요합니다.

for 루프에서 파일 이름을 읽으려면 find 명령을 실행하는 데 도움이 필요합니다.

콘텐츠/tmp/fefile

amx/eng/prf.amx
amx/eng/det.amx
bmb/menu.bmb
bmx/eng/menu.bmx
dll/tlnt.dll
dlx/eng/dlx
for file in `cat /tmp/fefile`
do
    if [ -f $file ]
    then
        echo "File '${file}' found in $(pwd) path."
        echo " Now i need to check if the  that file modified in last 10 mins with the below find command "
        find . -mmin -10 -type f -name ${file} -regextype posix-egrep -regex ".*/(dir1|dir1|dir3|dir4)/.+" -printf "%P\n" > /tmp/base
        echo "The files that are modified recently are below"
        File=(cat /tmp/base)

        echo " now i am verifying that $file is matched with $File "
        if[ $file == $File ]
        then
            echo " tmp file matched with base file."
        else
            echo " file doesn't match"
        fi # Originally missing
    else
        echo "File '{file} not found."
    fi
done

find 명령에서 위 스크립트를 수정하여 파일 이름을 읽고 지난 10분 동안 수정되었는지 확인하도록 도와주세요.

파일이 수정되면 두 파일이 일치하는지 확인합니다.

답변1

내부에찾다당신이 사용하는 명령정규식. 이 정규식은 나열된 파일 이름을 살펴봅니다.페이페이파일이 있지만 이 정규식과 일치하는 항목이 없습니다.참고: 파일 내부가 아닌 파일 이름 자체를 확인합니다..

find . -mmin -10 -type f -name ${file} -regextype posix-egrep -regex ".*/(dir1|dir1|dir3|dir4)/.+" -printf "%P\n" > /tmp/base

그들 중 아무도 없습니다:

  • anything in any amount추가하다
  • dir1또는 dir1(다시?! 아마도 dir2) 또는 dir3또는 dir4플러스
  • anything in any amount but at least one

또 다른 문제는 정규식 자체입니다.

".*/(dir1|dir1|dir3|dir4)/.+"

아마도 다음과 같아야 할 것입니다:

".*\/(dir1|dir2|dir3|dir4)\/.+"

/다음과 같이 이스케이프 를 사용해야 합니다 \.\/

반품:

File=(cat /tmp/base)

해야 한다:

File=$(cat /tmp/base)

또는

File=`cat /tmp/base`

또 다른 요점은 줄 끝입니다 find.

(...) -printf "%P\n" > /tmp/base

>를 >>로 변경하는 것이 좋습니다.

(...) -printf "%P\n" >> /tmp/base

그렇지 않으면 하나를 제외하고 발견된 모든 파일을 덮어씁니다.

답변2

스크립트 조각의 주요 문제점은 루프에서 find를 여러 번 실행한다는 것입니다( 의 각 파일 이름에 대해 한 번씩 /tmp/fefile).

이는 매우 느리고 비효율적입니다 find."값비싼"다른 선택의 여지가 없는 한 루프에서 반복적으로 실행해야 하는 작업(모든 도구를 사용하여 디렉터리 트리를 반복하는 것은 시간과 디스크 I/O 측면에서 비용이 많이 듭니다)보다 더 나은 선택이 거의 항상 있습니다.

한 번만 실행하고 출력을 처리하는 것이 더 좋습니다 find(예: grep, awk, sed 등을 사용하여).

다음과 같은 것을 더 시도해 보세요.

find ./dir[1234]/ -type f -mmin -10 -printf '%P\n' | grep -F -f /tmp/fefile

그러면 a) 지난 10분 동안 수정되었고 b) 와 관련된 dir1..dir4의 모든 파일 목록이 출력됩니다 /tmp/fefile.

그런데, 여기에는 /tmp/base임시 파일이 필요하지 않습니다. (그런데 임시 파일 이름을 스크립트로 하드코딩하거나 mktemp대신 사용하거나 이와 유사한 것을 사용하는 것은 일반적으로 나쁜 생각입니다. /tmp/fefile하드코딩하면 안 될 것 같지만 저는 그렇게 하지 않습니다. 스크립트의 나머지 부분이 수행하는 작업이나 이 스크립트 조각이 실행되는 방법을 알고 있어야 합니다.)

원하는 것을 얻으려면 약간의 조정 find및/또는 grep옵션이 필요할 수 있습니다. 원하는 작업을 파악하기 위해 스크립트 조각을 검토하는 데 몇 분을 보냈지만 여전히 100% 확실하지는 않습니다. 나는 당신이 find 단독으로 또는 find 및 grep(또는 sed, awk 또는 perl과 같은 다른 일반적인 도구)을 사용하여 훨씬 더 빠르게 수행할 수 있는 매우 비효율적인 작업을 수행하기 위해 약 20줄의 쉘 코드를 사용하고 있다는 것을 알고 있습니다.

참고: 파일 이름에 개행 문자가 포함되어 있으면 올바르게 작동하지 않습니다. \0GNU grep에 대한 옵션과 함께 형식 문자열에 not을 사용할 수 있습니다 .\n-printf-z

find ./dir[1234]/ -type f -mmin -10 -printf '%P\0' | grep -z -F -f /tmp/fefile

(터미널에서 출력을 보려면 출력을 tr '\0' '\n'. , 안전하지 않습니다)

그리고 파일 이름을 다룰 때 가장 좋고 안전한 방법 중 하나는 파일 이름을 배열에 저장하는 것입니다. 예를 들어 bash 내장 mapfile(AKA readarray) 을 사용하고프로세스 교체일치하는 모든 파일 이름으로 배열을 채웁니다.

declare -a found
mapfile -d '' -t found < <(find ./dir[1234]/ -type f -mmin -10 -printf '%P\0' |
                             grep -z -F -f /tmp/fefile)

$found일치하는 모든 파일 이름을 포함하는 배열입니다. 배열 보기를 사용하거나 declare -p found(디버깅 목적, 배열에 포함되어야 한다고 생각하는 내용이 배열에 포함되어 있는지 확인하는 데 가장 유용함)를 사용하거나 명령에 대한 인수로 사용하거나 루프에서 사용할 수 있습니다. 예를 들면 다음과 같습니다.

for f in "${found[@]}"; do
  echo "$f"
done

"$f"루프에서 원하는 것은 무엇이든 할 수 있지만 변수와 배열에는 NUL을 제외한 모든 문자가 포함될 수 있으므로 큰따옴표를 사용해야 합니다.

이는 명령 ${file}에서 . 대신 을 사용하고 있음을 상기시켜 줍니다 . 이는 매우 일반적인 실수입니다. 변수 주위의 중괄호는 다음과 같습니다.find"$file"아니요인용된 대안.

매개변수 대체( 헤더 실행 man bash및 검색 ) 에 사용됩니다 .Parameter Expansion문자열에 변수 이름을 삽입할 때 변수 이름을 명확하게 합니다.(예를 들어, 변수를 호출 $foo하고 유효한 변수 이름 문자 바로 옆에 있는 문자열로 인쇄해야 하는 경우 echo "$food"$food 값이 인쇄되고 echo "${foo}d"$foo 값이 인쇄된 다음 리터럴 d문자가 표시됩니다. ).

바라보다$VAR 대 ${VAR} 및 인용 여부.

당신은 또한 볼 수 있습니다공백이나 기타 특수 문자 때문에 쉘 스크립트가 멈추는 이유는 무엇입니까?,언제 큰따옴표가 필요합니까?그리고bash/POSIX 쉘에서 변수를 인용하는 것을 잊어버리는 보안 위험

마지막으로, 이 질문은 find출력에 관한 것이고, 찾기 관련 질문을 여러 개 했기 때문에 다음을 참조하세요.찾기 결과를 반복하는 것이 왜 나쁜 습관입니까?. 그리고 링크된 관련 질문을 꼭 읽어보세요.

답변3

지난 10분 동안 마지막으로 수정되었으며 마지막 경로 구성 요소가 한 줄을 구성하는 문자열이고 해당 경로에 , , , 목록의 디렉터리 구성 요소가 하나 /tmp/fefile이상 포함되어 있는 일반 파일을 찾는 것이 초점인 경우에는 다음을 수행할 수 없습니다 . 와 일치 하고 완전히 완료되어야 합니다 .dir1dir2dir3dir4-name-path

-path(원래는 BSD에서 왔지만 지금은 표준) 일부 구현에서는 -wholename( 와 동일 -path) , , 가 전체 경로 와 -ipath일치합니다 .-regex-iregex

그래서 몇 가지 옵션은

  • 각 줄에 대해 조건자를 사용하는 명령줄을 생성합니다 find.-path/tmp/fefile

    LC_ALL=C find . '(' -path '*/dir1/*' -o \
                        -path '*/dir2/*' -o \
                        -path '*/dir3/*' -o \
                        -path '*/dir4/*' \
                    ')' '(' \
                        -path '*/amx/eng/prf.amx' -o \
                        -path '*/amx/eng/det.amx' -o \
                        ... \
                    ')' -type f -mmin -10
    

    Bash를 사용하면 다음과 같은 작업을 수행할 수 있습니다.

    readarray -t args < <(
      </tmp/fefile LC_ALL=C sed '
    1!i\
    -o
    i\
    -path
    s|[*/?\\]|\\&|g; # escape glob operators
    s|.*|*/&|')
    LC_ALL=C find . '(' -path '*/dir1/*' -o \
                        -path '*/dir2/*' -o \
                        -path '*/dir3/*' -o \
                        -path '*/dir4/*' \
                    ')' '(' "${args[@]}" ')'
    

    (또는 */dir[1234]/*이것이 패턴으로 쉽게 분해될 수 없는 일부 실제 디렉토리 이름에 대한 자리 표시자라고 가정합니다.)

  • 후처리 명령과 일치하는 경로를 그대로 둡니다.@cas에서 볼 수 있듯이

  • 또는 find여기에서 구현한 것이 조건자를 find지원하는 GNU인 것 같으므로 -regex정규식을 동적으로 구성합니다.

    regex=".*/($(
      </tmp/fefile LC_ALL=C sed -e 's/[][$^*()+{}\\|.?]/\\&/g' |
      paste -sd '|' -))\$"
    LC_ALL=C find . -regextype posix-extended \
                    -regex '.*/(dir1|dir2|dir3|dir4)/.*' \
                    -regex "$regex" \
                    -type f -mmin -10
    

    ( /tmp/fefile비어 있지 않다고 가정).

-exec이러한 파일에 대해 명령을 실행해야 하는 경우 일부 조건자를 추가하거나 -print0선행 -printf '%P\0'을 제거하여 ./NUL 구분 목록을 처리할 수 있는 다른 명령에 NUL 구분 목록을 전달합니다(경로에서 임의의 파일 목록을 전달하는 안전한 방법). .

관련 정보