콘텐츠/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줄의 쉘 코드를 사용하고 있다는 것을 알고 있습니다.
참고: 파일 이름에 개행 문자가 포함되어 있으면 올바르게 작동하지 않습니다. \0
GNU 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
문자가 표시됩니다. ).
당신은 또한 볼 수 있습니다공백이나 기타 특수 문자 때문에 쉘 스크립트가 멈추는 이유는 무엇입니까?,언제 큰따옴표가 필요합니까?그리고bash/POSIX 쉘에서 변수를 인용하는 것을 잊어버리는 보안 위험
마지막으로, 이 질문은 find
출력에 관한 것이고, 찾기 관련 질문을 여러 개 했기 때문에 다음을 참조하세요.찾기 결과를 반복하는 것이 왜 나쁜 습관입니까?. 그리고 링크된 관련 질문을 꼭 읽어보세요.
답변3
지난 10분 동안 마지막으로 수정되었으며 마지막 경로 구성 요소가 한 줄을 구성하는 문자열이고 해당 경로에 , , , 목록의 디렉터리 구성 요소가 하나 /tmp/fefile
이상 포함되어 있는 일반 파일을 찾는 것이 초점인 경우에는 다음을 수행할 수 없습니다 . 와 일치 하고 완전히 완료되어야 합니다 .dir1
dir2
dir3
dir4
-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 구분 목록을 전달합니다(경로에서 임의의 파일 목록을 전달하는 안전한 방법). .