스크립트를 실행하기 위해 패턴과 일치하는 파일이 있는지 테스트


29

if특정 패턴과 일치하는 파일이 있는지 테스트하기 위해 명령문 을 작성하려고합니다 . 디렉토리에 텍스트 파일이 있으면 주어진 스크립트를 실행해야합니다.

내 코드는 현재 :

if [ -f /*.txt ]; then ./script fi

몇 가지 아이디어를주십시오. .txt디렉토리에 스크립트가있는 경우에만 스크립트를 실행하고 싶습니다 .


3
"디렉토리"가 확실해야 /합니까? 또한 전에 세미콜론이 누락되었습니다 fi.
depquid 2016 년

내가 만난 가장 깨끗하고 강력한 솔루션은 여기에find 설명 된대로 사용 하는 것입니다 .
Joshua Goldberg

답변:


39
[ -f /*.txt ]

있을 경우에만 true를 돌려 것 하나 (단 하나)에서 숨겨지지 않은 파일 /이름이 끝에서 .txt해당 파일이 일반 파일이나 일반 파일에 대한 심볼릭 링크와 경우를.

와일드 카드는 명령 (여기 [) 으로 전달되기 전에 쉘에 의해 확장되기 때문 입니다.

거기있어 경우에 따라서 /a.txt하고 /b.txt, [(5 개) 인수를 전달합니다 : [, -f, /a.txt, /b.txt]. [그런 다음 -f너무 많은 주장이 주어 졌다고 불평합니다 .

*.txt패턴이 숨겨지지 않은 하나 이상의 파일 (일반 또는 비 확장)로 확장되는지 확인하려면 다음을 수행하십시오 .

shopt -s nullglob
set -- *.txt
if [ "$#" -gt 0 ]; then
  ./script "$@" # call script with that list of files.
fi
# Or with bash arrays so you can keep the arguments:
files=( *.txt )
# apply C-style boolean on member count
(( ${#files[@]} )) && ./script "${files[@]}"

shopt -s nullglob이다 bash특정하지만, 쉘 좋아 ksh93, zsh, yash, tcsh해당 문장이있다.

참고는 디렉토리의 내용을 읽고 해당 파일을 발견 한, 그것은 시도하고 솔루션보다 더 효율적으로 만드는 전혀 그 파일에 액세스하지 않는 등의 호출 명령 ls또는 stat쉘에 의해 계산 된 파일의 목록.

sh이에 상응 하는 표준은 다음 과 같습니다.

set -- [*].txt *.txt
case "$1$2" in
  ('[*].txt*.txt') ;;
  (*) shift; script "$@"
esac

문제는 Bourne 또는 POSIX 쉘에서 패턴이 일치하지 않으면 자체로 확장된다는 것입니다. 따라서로 *.txt확장 하면 디렉토리에 파일 *.txt이 없거나이라는 .txt파일 이 있는지 여부를 알 수 없습니다 *.txt. 를 사용 [*].txt *.txt하면 둘을 구별 할 수 있습니다.


[ -f /*.txt ]에 비해 상당히 빠릅니다 compgen.
Daniel Böhmer

@ DanielBöhmer [ -f /*.txt ]는 잘못되었지만 숨겨진 테스트 3425파일 94이 아닌 파일 을 포함하는 디렉토리의 테스트에서 (10000 회 반복하면 20.5 초) compgen -G "*.txt" > /dev/null 2>&1빠릅니다 set -- *.txt; [ "$#" -gt 0 ].
Stéphane Chazelas

11

당신은 항상 사용할 수 있습니다 find:

find . -maxdepth 1 -type f -name "*.txt" 2>/dev/null | grep -q . && ./script

설명:

  • find . : 현재 디렉토리를 검색
  • -maxdepth 1: 하위 디렉토리를 검색하지 않습니다
  • -type f : 일반 파일 만 검색
  • name "*.txt" : 끝나는 파일 검색 .txt
  • 2>/dev/null : 오류 메시지를 /dev/null
  • | grep -q . : 문자에 대한 grep. 문자가 없으면 false를 반환합니다.
  • && ./script: ./script이전 명령이 성공한 경우에만 실행 ( &&)

2
find파일을 찾지 못하면 파일을 찾지 못하는 경우에만 false를 반환합니다. 결과물 grep -q .이 있는지 확인 하기 위해 출력을 파이프하려고합니다 .
Stéphane Chazelas 2016 년

@StephaneChazelas 물론입니다. 그러나 나는 그것을 테스트했고 작동하는 것처럼 보였다. 더 이상하지 않기 때문에 이상한 일을했을 것입니다. 언제 "파일을 찾는 데 문제가 있습니까?"
terdon

@terdon (일부 디렉토리에 액세스 할 수없는 경우 또는 I / O 오류 또는 시스템 호출에 의해 리턴 된 오류) 이 경우을 시도하십시오 chmod a-x ..
Stéphane Chazelas 2016 년

8

가능한 해결책은 Bash 내장 compgen입니다. 이 명령은 글 로빙 패턴에 대해 가능한 모든 일치 항목을 리턴하며 파일이 일치하는지 여부를 나타내는 종료 코드를 갖습니다.

compgen -G "/*.text" > /dev/null && ./script

그래도 더 빠른 솔루션을 찾는 동안이 질문을 찾았습니다.


1
잘 찾아라! 멀티 바이트 로케일 인 경우을 사용하여 약간 향상시킬 수 있습니다 LC_ALL=C compgen -G "*.txt" > /dev/null.
Stéphane Chazelas

7

여기에 하나의 라이너가 있습니다.

$ ls
file1.pl  file2.pl

파일이 존재

$ stat -t *.pl >/dev/null 2>&1 && echo "file exists" || echo "file doesn't exist"
file exists

파일이 존재하지 않습니다

$ stat -t -- *.txt >/dev/null 2>&1 && echo "file exists" || echo "file don't exist"
file don't exist

이 접근 방식은 bash 에서 ||and &&연산자를 사용합니다 . 이들은 "또는"및 "및"연산자입니다.

따라서 stat 명령 $?이 0과 같은 값을 반환 하면 첫 번째 echo가 호출 되고 1 이 반환되면 두 번째 echo가 호출됩니다.

통계에서 결과를 반환

# a failure
$ stat -t -- *.txt >/dev/null 2>&1
$ echo "$?"
1

# a success
$ stat -t -- *.pl >/dev/null 2>&1
$ echo "$?"
0

이 질문은 stackoverflow에서 광범위하게 다루어집니다.


1
왜 동일하게 statls -d수 있을 때 비표준을 사용 합니까?
Stéphane Chazelas

ls -d디렉토리가 있다고 생각 합니까? ls -d *.pl예를 들어 파일이있는 디렉토리를 나열하려고했을 때 작동하지 않는 것 같습니다 .
slm

&&by 왼쪽의 문을 바꿀 수 있으며 ls *.txt잘 작동합니다. /dev/null@slm에서 제안한대로 stdout 및 stderr을 보내십시오 .
unxnut

1
사용 ls *.txt하고 디렉토리에 파일이 없으면 이것은을 반환하며 $? = 2, 여전히 if와 함께 작동하지만 이것이 선택 stat을 위한 나의 이유 중 하나였습니다 ls. 나는 성공을 위해 0을, 실패를 위해 1을 원했다.
slm

ls -d내용 대신 디렉토리를 나열하는 것입니다. 그래서 ls -d단지를 수행 lstatGNU이처럼 파일에 stat않습니다. 실패시 0이 아닌 종료 상태 명령이 리턴하는 것은 시스템마다 다르며, 명령을 가정하는 것은 의미가 없습니다.
Stéphane Chazelas

4

Chazelas가 지적했듯이 와일드 카드 확장이 둘 이상의 파일과 일치하면 스크립트가 실패합니다.

그러나, 내가 사용하는 트릭이 있습니다 ( 심지어별로 싫어합니다 ).

PATTERN=(/*.txt)
if [ -f ${PATTERN[0]} ]; then
...
fi

어떻게 작동합니까?

와일드 카드 확장은 파일 이름 배열과 일치합니다. 파일 이름이 있으면 첫 번째 파일 이름을 얻습니다. 일치하지 않으면 null입니다.


IMO 이것은 가장 나쁜 대답입니다. 비록 기본 기능이 언어에서 누락 된 것처럼 그들은 모두 끔찍한 것처럼 보입니다.
plugwash

@plugwash 의도적입니다 ... * nix 셸 스크립트에는 기본적인 흐름 제어 기능과 몇 가지 다른 확률과 결과가 있지만 작업이 끝나면 다른 명령을 함께 붙여야합니다. 만약 bash가 짜증 나면 ... 그것은 당신이 사용하는 명령이
빠졌기

2
그것은 잘못된 논리입니다 (그리고 따옴표가 없습니다). 일치하는 첫 번째 파일이 일반 파일인지 확인합니다. 비정규 파일 일 수도 있지만 regular.txt 유형의 다른 파일이 있을 수도 있습니다 . 예를 들어 이후에보십시오 . mkdir a.txt; mkfifo b.txt; echo regular > c.txt
Stéphane Chazelas

1

다음과 같이 간단합니다.

cnt=`ls \*.txt 2>/dev/null | wc -l`
if [ "$cnt" != "0" ]; then ./script fi

wc -l 확장 된 와일드 카드의 행을 계산합니다.


1
Downvote : 적은 양의 코드로 놀라운 초급 반 패턴을 수집합니다. 출력을 구문 분석하지 말고ls 이미 $?직접 수행하므로 직접 검사하지 마십시오 if. 또한 어떤 일일어 났는지 확인하기 위해 사용 wc하는 것도 마찬가지로 잘못 지시됩니다.
tripleee

0

이전 배열 솔루션이 마음에 들지만 많은 파일로 인해 낭비 될 수 있습니다. 쉘은 많은 메모리를 사용하여 배열을 빌드하며 첫 번째 요소 만 테스트합니다.

어제 테스트 한 대체 구조는 다음과 같습니다.

$ cd /etc; if [[ $(echo * | grep passwd) ]];then echo yes;else echo no;fi yes $ cd /etc; if [[ $(echo * | grep password) ]];then echo yes;else echo no;fi no

grep의 종료 값이 제어 구조를 통한 경로를 결정하는 것으로 보입니다. 또한 쉘 패턴이 아닌 정규식으로 테스트합니다. 내 시스템 중 일부에는 "pcregrep"명령이있어 훨씬 더 복잡한 정규식 일치를 허용합니다.

(이 구문 분석에 대한 위의 비판을 읽은 후 명령 대체에서 "ls"를 제거하기 위해이 답변을 편집했습니다.)


-2

if 절을 사용하려면 개수를 평가하십시오.

if (( `ls *.txt 2> /dev/null|wc -l` ));then...
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.