아래 스크립트에서 첫 번째~을 위한루프는 예상대로 실행되지만 두 번째 루프는 실행되지 않습니다. 오류가 발생하지 않습니다. 스크립트가 중단된 것 같습니다.
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='/path/to/dir1 /path/to/dir2'
$ find
DIRWORK가 따옴표가 해제될 때 두 개의 다른 디렉토리를 보게 됩니다. 따옴표 사용 주제는 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
.