
다음 스크립트에서 첫 번째~을 위한루프는 예상대로 실행되지만 두 번째는 실행되지 않습니다. 오류가 발생하지 않습니다. 스크립트가 중단된 것 같습니다.
HOME=/root/mydir
DIR=$HOME/var
DIRWORK=$HOME/Local
for f in $(find $DIR -type f); do
lsof -n $f | grep [a-z] > /dev/null
if [ $? != 0 ]; then
echo "hi"
fi
done
for i in $(find $DIRWORK -type d -name work); do
echo "2"
done
답변1
귀하의 스크립트는 위험한 방식으로 코딩되었습니다.
먼저, '/bash' 및 '/for' 태그를 지정했기 때문에 Bash 쉘을 사용하고 있다고 가정합니다.
내 대답에서 나는 이 훌륭한 말을 인용하겠습니다.배쉬 가이드, 아마도 Bash를 배울 수 있는 가장 좋은 소스일 것입니다.
1)절대 사용하지 마세요명령 대체, 의어느 하나친절하고 따옴표 없이요. 여기에는 큰 문제가 있습니다. 따옴표가 없는 확장을 사용하여 출력을 인수로 분할하는 것입니다.
구체적으로 말하자면, 이 $(find $DIRWORK -type d -name work)
일을 $(find $DIR -type f)
겪게 될 것입니다.단어 분할따라서 find
이름에 공백이 있는 파일(예: "파일 이름")을 찾으면 Bash의 단어 분할 결과는 for
반복할 명령에 대해 2개의 인수(예: "파일"에 대해 하나, "이름"에 대해 하나)를 전달합니다. 이 경우 실제로 존재하는 경우 잠재적으로 손상을 입히는 대신 "파일: 해당 파일 또는 디렉터리 없음" 및 "이름: 해당 파일 또는 디렉터리 없음"을 얻을 수 있기를 바랍니다.
2)관례적으로 환경 변수(PATH, EDITOR, SHELL, ...)와 내부 셸 변수(BASH_VERSION, RANDOM, ...)는 모두 대문자로 표시됩니다. 다른 모든 변수 이름은 소문자여야 합니다. 변수 이름은 대소문자를 구분하므로 이 규칙은 실수로 환경 및 내부 변수를 재정의하는 것을 방지합니다.
$DIRWORK 디렉토리는 해당 규칙을 위반하고 따옴표도 해제되어 있습니다. 따라서 $DIRWORK가 따옴표가 해제 DIRWORK='/path/to/dir1 /path/to/dir2'
되면 find
두 개의 다른 디렉토리를 조사하게 됩니다. Bash에서는 따옴표 사용의 주제가 매우 중요하므로 "큰 따옴표"를 사용해야 합니다.모든확장 및 특수 문자를 포함할 수 있는 모든 항목(예: "$var", "$@", "${array[@]}", "$(command)"). Bash는 '작은 따옴표' 안의 모든 것을 리터럴로 처리합니다. ', ", `의 차이점을 알아보세요.인용 부호,인수다음 링크를 살펴보는 것도 좋습니다.http://wiki.bash-hackers.org/syntax/words
이는 보다 안전한 스크립트 버전이므로 대신 사용하는 것이 좋습니다.
my_home="/root/mydir"
my_dir="$my_home/var"
dir_work="$my_home/Local"
while IFS= read -r -d '' f; do
# I'm guessing that you also want to ignore stderr;
# this is where the 2>&1 came from.
if lsof -n "$f" | grep '[a-z]' > /dev/null 2>&1; then
echo "hey, I'm safer now!"
fi
done < <(find "$dir_work" -type f -print0)
while IFS= read -r -d '' f; do
echo "2"
done < <(find "$dir_work" -type d -name 'work' -print0)
보시 IFS
다시피 변수는 비어 있도록 설정되어 있으므로 read
줄의 선행 및 후행 공백이 잘리지 않습니다. 이 read
명령은 빈 문자열( -d ''
)을 구분 기호로 사용하여 \0에 도달할 때까지 읽습니다.
find
그에 따라 수정해야 하므로 -print0
새 줄 대신 \0으로 데이터를 구분하는 옵션을 사용합니다. 놀랍게도 악의적으로 파일 이름의 일부가 될 수 있습니다. 이러한 파일을 \n으로 두 조각으로 분할하면 코드가 손상됩니다.
당신은 다음에 대해 읽고 싶을 수도 있습니다프로세스 대체내 스크립트를 완전히 이해하지 못한다면.
출력을 find ... | while read name; do ...; done
읽는 데 사용해야한다고 명시한 이전 답변 도 나쁠 수 있습니다. 위의 find
루프 while
는 상위에서 복사된 자체 변수 복사본을 사용하여 새 하위 셸에서 실행됩니다. 그러면 이 사본은 원하는 대로 사용됩니다. 루프 가 끝나면 while
하위 쉘 복사본이 삭제되고 상위 변수의 원래 변수는 변경되지 않습니다.
이 루프 내에서 일부 변수를 수정하고 나중에 상위에서 사용하려는 경우 while
데이터 손실을 방지할 수 있는 위의 더 안전한 스크립트를 사용하는 것이 좋습니다.
답변2
이 코드
for i in $(find $DIRWORK -type d -name work); do
echo "2"
done
이 줄을 먼저 실행합니다
find $DIRWORK -type d -name work
실행이 끝날 때까지 기다린 find
다음 출력을 가져와 다시 for
루프 에 넣습니다.
for i in the output of find; do
echo "2"
done
그래야만 for 루프가 실행되기 시작합니다.
따라서 루프를 find
완료하는 데 오랜 시간이 걸리면 for
루프가 시작되기 전에 오랜 시간을 기다려야 합니다.
find
대화형 프롬프트에서 명령 타이밍을 맞춰보세요.
$ time find $DIRWORK -type d -name work
시간이 얼마나 걸리는지 확인해 보세요.
for
또한 참고: 파일 이름을 반복하기 위해 루프 를 사용해서는 안 됩니다 . 다음과 같이 while
루프를 사용하십시오 read
.
find $DIRWORK -type d -name work | while read name; do
echo "2"
done
읽다이것자세한 내용은.
보너스: 이는 while
루프를 와 병렬로 실행합니다 find
. 이는 루프가 한 줄을 인쇄 while
하자마자 한 번의 반복을 실행한다는 것을 의미합니다. 실행이 완료될 때 find
까지 기다릴 필요가 없습니다 find
.