연습 중이야쉘 스크립트디렉터리를 매개 변수로 사용하고 디렉터리에 있는 각 파일을 반복하여 이름과 크기를 출력하는 간단한 스크립트를 만들려고 합니다.
#!/bin/bash
# A practice shell script to try and display a list of file names
# and their sizes using the output of ls -l and cut.
declare -i index
export index=0
export name=""
export size=0
for file in $1 ; do
index+=1
name=`basename $file`
size=`ls -l $file | cut -d " " -f 5`
echo "$index: $name, size: $size bytes"
done
매개변수로 제공 하면 ./*
파일에 대해 이 작업을 수행하고 그게 전부입니다. 그러나 위의 코드를 편집하여 ./*
위치 에 넣으면 $1
현재 디렉터리의 모든 파일이 작동하고 반복됩니다.
$1
와 같아야 하는데 왜 같은 일을 하지 않는 걸까요 ./*
?
답변1
그 이유는 스크립트를 호출하는 쉘이 와일드카드 패턴을 확장하기 때문입니다../*
앞으로스크립트로 전달됩니다. 즉, 와일드카드 패턴이 일치하면 file1.txt
스크립트 가 다음과 file4.txt
같이 호출됩니다 .
./my_script.sh ./*
실제로는 다음과 같이 해석됩니다.
./my_script.sh file1.txt file2.txt file3.txt file4.txt
이는 쉘 스크립트에 표시되는 매개변수입니다.
자세한 내용은 다음을 확인하세요.쉘 확장 순서에 관한 부분Bash 참조 매뉴얼에 있습니다.
이 문제를 해결하는 방법에는 두 가지가 있습니다.
- 특정 디렉터리의 모든 파일을 항상 반복하고 싶다면 해당 디렉터리를 매개변수로 전달한 다음 반복하세요.
for f in "$1"/* do # operations on "$f" done
- 또는 작업하려는 파일 이름만 전달하는 것이 확실하다면 다음과 같이 전체 인수 목록을 반복하십시오.
for f in "$@" do # operations on "$f" done
확실히 흥미로운 연습인 스크립트에 glob 패턴을 전달하여 이 작업을 수행하려는 경우에도 가능합니다(@ilkkachu의 설명 참조). @fra-san이 의견에서 언급했듯이 이 접근 방식에는 장점이 있습니다. 스크립트 사용에 더 많은 유연성을 추가하고 셸 명령줄 인수의 제한을 우회합니다("인수 목록이 너무 깁니다" 참조). RAM은 여전히 길이를 제한하지만 생성된 파일 이름 목록) - 하지만 각별히 주의해야 합니다.
- 매개변수를 따옴표(작은따옴표 또는 큰따옴표)로 묶거나 백슬래시로 glob 문자를 이스케이프하여 쉘이 glob을 확장하는 것을 방지할 수 있습니다.
./my_script.sh "./*" ./my_script.sh './*' ./my_script.sh ./\*
- 스크립트 내에서 위치 매개변수를 참조합니다.
$1
인용되지 않음이런 식으로 실제로 쉘에 의해 해석됩니다(우리가 종종 피하고 싶은 것). - "해석"에는 확장(위 참조)뿐만 아니라 단어 분할도 포함되므로입력 필드 구분 기호
IFS
토큰화가 발생하지 않도록 빈 문자열로 바꿉니다. - 루프는
for
다음과 같습니다IFS= for f in $1 do # Operations on "$f" done
스크립트에 대한 몇 가지 일반적인 참고 사항:
- 항상 쉘 변수를 인용하십시오., 특히 파일 이름이 포함된 경우 그렇지 않으면 스크립트에서 공백이나 기타 훨씬 더 이국적인 문자가 포함된 파일 이름을 발견하게 됩니다. 파일 이름에는 개행 문자도 허용된다는 점을 기억하세요(으악)!
ls
다음의 출력을 구문 분석합니다.매우 좌절비슷한 이유로. 특정 파일의 속성을 식별하려는 경우 이stat
도구가 더 나은 선택입니다 . 예를 들어, 파일 크기를 확인하려면 다음을 사용할 수 있습니다.size=$(stat --printf="%s" "$f")
- 이것은존경받는
$( ... )
명령 대체를 위해 이전 "백틱 스타일" 대신 "새" 스타일을 사용하십시오` ... `
. - 쉘 스크립트를 확인하는 것이 좋습니다
shellcheck
는 이러한 (및 기타) 가능한 오류 소스로부터 보호하기 위해 많은 Linux 배포판에서 독립 실행형 도구로도 사용할 수 있습니다.
답변2
Bash가 와일드카드와 매개변수를 해석하는 방법을 이해해야 합니다. 와일드카드가 있으면 bash는 이를 즉시 해석하고 일치하는 모든 파일로 바꿉니다. $1을 ./*로 바꾸면 이런 일이 발생합니다. 현재 디렉터리에서 모든 파일을 가져와서 반복합니다.
너가 가질 때
for file in $1 ; do
첫 번째 매개변수만 필요합니다. 그것은 당신이 원하는 것이 아닙니다. 모든 파일을 반복하려면 다음을 사용해야 합니다.
for file in $* ; do
(이것은 모든 매개변수를 허용합니다)
또는 -를 사용하여 반복할 수 있습니다. 이렇게 하면 shift
다른 인수가 있을 때마다 첫 번째 인수가 제거됩니다.
while [ $# -gt 0 ]
do
file=$1
shift
...
done