비어있는 경우를 제외하고 bash 반복 파일 목록


33

나는 이것이 간단하다고 생각했지만 예상보다 더 복잡하다는 것을 증명하고 있습니다.

디렉토리에서 특정 유형의 모든 파일을 반복하고 싶으므로 다음과 같이 작성하십시오.

#!/bin/bash

for fname in *.zip ; do
   echo current file is ${fname}
done

디렉토리에 일치하는 파일하나 이상있는 한 작동합니다 . 그러나 일치하는 파일이 없으면 다음과 같은 결과를 얻습니다.

current file is *.zip

그런 다음 시도했습니다.

#!/bin/bash

FILES=`ls *.zip`
for fname in "${FILES}" ; do
    echo current file is ${fname}
done

파일이 없을 때 루프 본문이 실행되지 않지만 ls에서 오류가 발생합니다.

ls: *.zip: No such file or directory

일치하는 파일을 깔끔하게 처리하는 루프는 어떻게 작성합니까?


7
shopt -s nullglobfor 루프를 실행하기 전에 추가하십시오 .
cuonglm

@cuolnglm이 RHEL5 상자의 빈 목록이 아니라 실행중인 스크립트의 이름을 반환 LS의 무시하게이 결과 (bash는 3.2.25) 내가 할 경우 FILES=LS * .ZIP ; for fname in "${FILES}"...하지만 예상대로이 작업을 수행for fname in *.zip ; do....
symcbean

3
사용 for file in *.zip하지 말고 사용하십시오 `ls ...`. @cuonglm의 제안은 *.zip패턴이 파일과 일치하지 않을 때 아무것도 확장되지 않도록하는 것입니다. ls인수 없이는 현재 디렉토리를 나열합니다.
Stéphane Chazelas

1
이 질문에 나와있는 이유의 출력 구문 분석 ls: 일반적으로 피해야하는 이유는 없습니다 구문 분석을 ls? ; 또한 페이지 상단 부근의 BashGuide 's ParsingLs 기사 링크를 참조 하십시오.
PM 2Ring

답변:


34

에서 bash, 당신은 설정할 수 있습니다 nullglob리터럴 문자열로 취급하기보다는, 아무것도 일치하지 않는 패턴이 "사라"너무 옵션 :

shopt -s nullglob
for fname in *.zip ; do
   echo "current file is ${fname}"
done

POSIX 셸 스크립트에서 그냥 fname존재 하는지 확인하고 (와 동시에 [ -f ]파일이 일반 파일 (또는 일반 파일에 대한 심볼릭 링크)인지 확인하고 directory / fifo / device와 같은 다른 유형은 아닌지 확인하십시오) :

for fname in *.zip; do
    [ -f "$fname" ] || continue
    printf '%s\n' "current file is $fname"
done

교체 [ -f "$fname" ][ -e "$fname" ] || [ -L "$fname ]모든을 통해 루프에 이름이 끝나는 (숨겨지지 않은) 파일을 원하는 경우 .zip에 관계없이 유형을.

교체 *.zip.*.zip .zip *.zip당신은 또한 그의 이름 끝에서 숨겨진 파일을 고려할 경우 .zip.


1
shopt -s nullglob우분투 17.04에서 나를 위해 일하지는 않았지만 잘 작동 [ -f "$fname" ] || continue했습니다.
koppor

@koppor 실제로 사용하지 않는 것 같습니다 bash.
chepner

2
set ./*                               #set the arg array to glob results
${2+":"} [ -e "$1" ] &&               #if more than one result skip the stat "$1"
printf "current file is %s\n" "$@"    #print the whole array at once

###or###

${2+":"} [ -e "$1" ] &&               #same kind of test
for    fname                          #iterate singly on $fname var for array
do     printf "file is %s\n" "$fname" #print each $fname for each iteration
done                                  

여기 주석에서 함수 호출을 언급합니다 ...

file_fn()
    if     [ -e "$1" ] ||               #check if first argument exists
           [ -L "$1" ]                  #or else if it is at least a broken link
    then   for  f                       #if so iterate on "$f"
           do : something w/ "$f"
           done
    else   command <"${1-/dev/null}"    #only fail w/ error if at least one arg
    fi

 file_fn *

2

찾기 사용

export -f myshellfunc
find . -mindepth 1 -maxdepth 1 -type f -name '*.zip' -exec bash -c 'myshellfunc "$0"' {} \;

export -f이 기능을 사용하려면 쉘 기능을 내 보내야합니다 . 이제 find이 실행 bash은 현재 디렉토리 수준에서 쉘 함수를 실행하고 남아 있습니다.


하위 디렉토리를 통해 되풀이되고 일치하는 bash 함수 (스크립트가 아닌)를 호출하고 싶습니다.
symcbean

@symcbean 나는 단일 디렉토리로 제한하고 bash 기능을 처리하도록 편집했습니다
Dani_l

-3

대신에:

FILES=`ls *.zip`

시험:

FILES=`ls * | grep *.zip`

이 방법으로 ls가 실패하면 (귀하의 경우) 실패한 출력을 grep하고 빈 변수로 반환합니다.

current file is      <---Blank Here

여기에 논리를 추가하여 "파일을 찾을 수 없음"을 리턴 할 수 있습니다.

#!/bin/bash

FILES=`ls * | grep *.zip`
if [[ $? == "0" ]]; then
    for fname in "$FILES" ; do
        echo current file is $fname
    done
else
    echo "No Files Found"
fi

이 방법으로 이전 명령이 성공하면 (0 값으로 종료) 현재 파일을 인쇄하고 그렇지 않으면 "No Files Found"를 인쇄합니다


1
나는 하나 개 이상의 프로세스를 (추가하는 나쁜 아이디어라고 생각 grep보다는 더 나은 도구를 (사용하여 문제를 해결하는 것보다) find) 또는 (로 현재 솔루션에 대한 관련 설정 변경 shopt -s nullglob)
토마스 Baruchel

1
원래 게시물에 대한 OP의 의견에 따르면 shopt -s nullglob작동하지 않습니다. find내 대답을 확인하는 동안 시도했지만 계속 실패했습니다. 다니가 수출 한 것으로 생각합니다.
Kip K
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.