폴더를 반복하면 어쨌든 루프가 발생합니다.

폴더를 반복하면 어쨌든 루프가 발생합니다.

폴더를 반복하는 경우

for f in $path 

와일드카드와 일치하는 파일이 없는데도 루프가 계속 입력되는 예기치 않은 동작이 발생했습니다. 저는 cygwin을 사용하고 있는데 이것이 제 코드입니다

#!/bin/sh
here=$(pwd)
release(){
    release="$1"
    targetdisk="$2"
    package="$3"
    searchPath="../$package/${package}Setup/*/*/*.msi"
    echo $searchPath
    ls $searchPath
    for f in $searchPath; do
        mode=$(echo $file | sed -e 's:.*/.*/\(.*\)/.*\.msi:\1:')
        version=$(echo $file | sed -e 's:.*/\(.*\)/.*/*.\.msi:\1:')
        PREVIOUSIFS=$IFS
        IFS=#
        targetPath="$targetdisk:\\Software and tools\\Install\\$release"
        newFile="$targetPath\\$package-$mode-$release.exe"
        if [ ! -d "$targetPath" ]; then
            mkdir -p "$targetPath"
        fi
        echo $newFile
        echo $f


        #cp $file $newFile
        IFS=$PREVIOUSIFS
    done;
}
release $1 $2 MyStuff

MyStuff를 일부러 넣었는데 존재하지 않습니다.

$ ./release.sh 1.0-dev G
../MyStuff/MyStuffSetup/*/*/*.msi
ls: ../MyStuff/MyStuffSetup/*/*/*.msi: No such file or directory
G:\Software and tools\Install\1.0-dev\MyStuff--1.0-dev.exe
../MyStuff/MyStuffSetup/*/*/*.msi

그러나 보시다시피 두 에코가 모두 실행됩니다. 일치하는 항목이 없는데 왜 이런 일이 발생합니까?

답변1

어떤 파일과도 일치하지 않는 와일드카드 패턴은 확장되지 않습니다. 그 목적은 와일드카드 문자를 포함하지만 패턴의 일부가 아닌 명령에 매개 변수를 입력할 수 있도록 허용하는 것입니다. 이는 명령줄에서 두 개의 따옴표 문자를 가끔 저장할 수 있으므로 어느 정도 의미가 있습니다. 스크립트에서 이것은 매우 짜증나는 일입니다. 안타깝게도 레거시 sh에서는 이 동작을 변경할 수 있는 방법이 없습니다. 패턴이 확장되지 않았는지 확인할 수 있습니다.

for f in "../$package/${package}Setup"/*/*/*.msi; do
  if [ "$f" = "../$package/${package}Setup"/*/*/*.msi" ]; then
    # No match
    break
  fi
done

일치하지 않는 와일드카드 패턴을 빈 목록으로 확장하도록 Bash에 지시할 수 있습니다. shebang 줄을 로 변경하면 다음을 #!/bin/bash수행할 수 있습니다.

shopt -s nullglob
for f in "../$package/${package}Setup"/*/*/*.msi; do

스크립트에 다른 문제가 있습니다. searchPath에 공백이나 와일드카드(백슬래시 포함)가 있으면 변수 사용이 중단됩니다 $package. 변수 대체를 따옴표 없이 그대로 두지 마십시오 $searchPath. 변수 대체를 공백으로 구분된 패턴 목록으로 처리하려는 경우가 아니면 항상 큰따옴표를 사용하십시오. 그러나 여기서는 그렇지 않습니다.  $package문자열(큰따옴표가 필요함)이므로 *배치를 와일드카드 패턴으로 만드는 것이 분명합니다. 위와 같이 패턴을 for루프에 직접 넣습니다.

로깅의 경우 이러한 명령을 사용하는 대신 echo(변수 대체에 큰따옴표가 누락되어 매개변수가 손상될 수 있음) 이를 사용하여 set -x실행될 때 stderr에 각 명령을 표시합니다.

스크립트의 어느 시점에서 설정 중이지만 IFS=#절대로 분할을 수행하지 않습니다 #. IFS사용할 계획이 없다면 설정하지 마세요.

몇 가지 질문이 있습니다 mode=$(echo $file | sed -e 's:.*/.*/\(.*\)/.*\.msi:\1:'). 위에서 언급했듯이 큰따옴표가 누락되었습니다 "$file". 또한 각 파일에 대한 추가 처리 호출이 sed느리고(Cygwin에서 외부 프로세스 실행이 느림) 지나치게 복잡합니다. 쉘에는 문자열 처리 구조가 있지만 여기에서는 충분합니다. 당신의

dir="${file%/*}"
mode="${dir##*/}"
dir="${dir%/*}"
version="${dir##*/}"

답변2

이 스크립트에는 많은 문제가 있지만 귀하가 묻는 동작(루프 이유)은 다음과 같습니다.

쉘 구조에서

for f in <expr>; do <commands>; done

파일 패턴은 일반적으로 "expr"에 사용되며, 이는 사용자가 수행하는 쉘 파일 이름 와일드카드로 대체됩니다. 그러나 표현식이 일치하는 항목이 없으므로 와일드카드는 표현식을 파일 경로 목록으로 확장하지 않습니다. 그러나 이는 다른 많은 언어처럼 길이가 0인(빈) 목록이 아니라 루프 위에 표시되는 별표가 있는 경로 표현식입니다. 따라서 루프는 한 번 실행되고 $f는 *.msi로 끝나는 확장되지 않은 문자열과 동일하게 설정됩니다. 루프 앞의 위치를 ​​에코하여 이를 확인할 수 있습니다. ls와 함께 사용하고 있기 때문에 이것이 어떤 것과도 일치하지 않는다는 것을 알고 있습니다. 이것이 바로 "해당 파일이나 디렉터리가 없습니다" 오류가 발생하는 이유입니다.

(또한 루프에서 $file을 사용했다는 점에 유의하십시오. 제 생각엔 거기에서 $f를 사용하려고 했던 것 같습니다.

관련 정보