Bash 스크립트에 대한 매개 변수 유효성 검사


95

필요하지 않게되는 여러 폴더를 제거하는 프로세스를 자동화하는 데 도움이되는 기본 폴더를 생각해 냈습니다.

#!/bin/bash
rm -rf ~/myfolder1/$1/anotherfolder
rm -rf ~/myfolder2/$1/yetanotherfolder
rm -rf ~/myfolder3/$1/thisisafolder

이것은 다음과 같이 유발됩니다.

./myscript.sh <{id-number}>

문제는 id-number (그때처럼) 입력하는 것을 잊으면 정말로 삭제하고 싶지 않은 많은 것을 잠재적으로 삭제할 수 있다는 것입니다.

명령 줄 매개 변수에 모든 형태의 유효성 검사를 추가 할 수있는 방법이 있습니까? 제 경우에는 a) 하나의 매개 변수가 있는지, b) 숫자인지, c) 해당 폴더가 있는지 확인하는 것이 좋습니다. 스크립트를 계속하기 전에.

답변:


158
#!/bin/sh
die () {
    echo >&2 "$@"
    exit 1
}

[ "$#" -eq 1 ] || die "1 argument required, $# provided"
echo $1 | grep -E -q '^[0-9]+$' || die "Numeric argument required, $1 provided"

while read dir 
do
    [ -d "$dir" ] || die "Directory $dir does not exist"
    rm -rf "$dir"
done <<EOF
~/myfolder1/$1/anotherfolder 
~/myfolder2/$1/yetanotherfolder 
~/myfolder3/$1/thisisafolder
EOF

편집 : 처음에는 디렉토리가 존재하는지 확인하는 부분을 놓쳐서 스크립트를 완성했습니다. 또한 의견에서 제기 된 문제를 해결했습니다. 정규식 고정로부터 스위치 ==eq.

내가 말할 수있는 한 이것은 이식 가능한 POSIX 호환 스크립트 여야합니다. 이 때문에 실제로 중요하다 어떤 bashisms, 사용하지 않는 /bin/sh우분투에 실제로 dash,이 일하지 bash.


정수 비교를 위해 + e를 설정하고 '=='대신 '-eq'를 사용하는 것을 잊지 마십시오
guns

-eq로 변경했습니다. set + e가 여기서 당신을 사나요?
Brian Campbell

나는 내 대답에서 당신이 당신의 것에서도 고칠 수있는 두 가지를 발견했습니다 : 먼저 SO hilighter가 $ #에 열중합니다 (해석으로 취급). 나는 그것을 고치기 위해 "$ #"를했다. 둘째, 정규식은 "foo123bar"와도 일치합니다. ^ [0-9] + $를 수행하여 고쳤습니다. grep의 -x 옵션을 사용하여 수정할 수도 있습니다
Johannes Schaub-litb

1
@ojblass 그가 묻고 있던 테스트 중 하나가 누락되었습니다. 그것을 추가하는 것은 또한 테스트 할 디렉토리를 추가하는 것을 의미하며, 한 줄에 맞을 수 없기 때문에 답변의 크기가 크게 확장되었습니다. 각 디렉토리의 존재를 테스트하는 더 간단한 방법을 제안 할 수 있습니까?
Brian Campbell

1
아래 @Morten Nielsen의 답변 주석에 따르면 grep '$ [0-9] + ^'는 실제로 이상하게 보입니다. '^ [0-9] + $'가 아니겠습니까?
마틴은 jakubik

21

sh솔루션은 Brian Campbell고상하고 잘 실행되었지만 몇 가지 문제가 있으므로 나만의 bash솔루션을 제공 할 것이라고 생각했습니다 .

sh하나 의 문제 :

  • 의 물결표는 ~/fooheredocs 내부의 홈 디렉토리로 확장되지 않습니다. 그리고 read성명 에서 읽 거나 성명에서 인용 할 때도 마찬가지 rm입니다. 즉, No such file or directory오류가 발생합니다.
  • grep기본 작업을 위해 분기하는 등은 멍청합니다. 특히 bash의 "무거운"무게를 피하기 위해 엉뚱한 셸을 사용할 때.
  • 또한 몇 가지 인용 문제를 발견했습니다. 예를 들어 그의 echo .
  • 드물지만 솔루션은 줄 바꿈이 포함 된 파일 이름을 처리 할 수 ​​없습니다. (거의 어떤 솔루션도 sh그것들에 대처할 수 없습니다 -이것이 제가 거의 항상 선호하는 이유 bash입니다. 잘 사용하면 훨씬 더 방탄하고 악용하기 더 어렵습니다).

그렇습니다. /bin/sh해시 뱅에 사용한다는 것은 어떤 대가를 치르더라도 ISMS를 피해야한다는 것을 의미 하지만, 우분투 또는 정직하고 솔직한 경우에도 원하는 bash모든 ISMS 를 사용할 수 있습니다.bash#!/bin/bash 정상에 .

그래서 여기에 bash더 작고, 더 깨끗하고, 더 투명하고, 아마도 "더 빠르며"더 방탄 한 솔루션이 있습니다.

[[ -d $1 && $1 != *[^0-9]* ]] || { echo "Invalid input." >&2; exit 1; }
rm -rf ~/foo/"$1"/bar ...
  1. 주위의 따옴표에 주목하십시오 $1.rm 문!
  2. 다음의 -d경우에도 검사가 실패합니다.$1 비어 그 하나에 두 개의 검사를, 그래서.
  3. 나는 이유 때문에 정규 표현식을 피했습니다. =~bash에서 사용해야하는 경우 정규식을 변수에 넣어야합니다. 어쨌든 저와 같은 glob은 항상 선호되며 훨씬 더 많은 bash 버전에서 지원됩니다.

1
그렇다면 globbing 조각 $1 != *[^0-9]*bash는 구체적입니까?
grinch

15

나는 bash를 사용할 것입니다 [[.

if [[ ! ("$#" == 1 && $1 =~ ^[0-9]+$ && -d $1) ]]; then 
    echo 'Please pass a number that corresponds to a directory'
    exit 1
fi

이 FAQ 가 좋은 정보 소스 라는 것을 알았습니다 .


13

위의 답변만큼 방탄하지는 않지만 여전히 효과적입니다.

#!/bin/bash
if [ "$1" = "" ]
then
  echo "Usage: $0 <id number to be cleaned up>"
  exit
fi

# rm commands go here


9

test ( man test) 의 man 페이지 는 bash에서 부울 연산자로 사용할 수있는 모든 연산자를 제공합니다. 다른 프로그래밍 언어에서와 마찬가지로 입력 유효성 검사를 위해 스크립트 (또는 함수) 시작 부분에 이러한 플래그를 사용합니다. 예를 들면 :

if [ -z $1 ] ; then
  echo "First parameter needed!" && exit 1;
fi

if [ -z $2 ] ; then
  echo "Second parameter needed!" && exit 2;
fi

8

빈 문자열을 테스트하려면 '-z'를 사용하고 디렉토리를 확인하려면 '-d를 사용하십시오.

if [[ -z "$@" ]]; then
    echo >&2 "You must supply an argument!"
    exit 1
elif [[ ! -d "$@" ]]; then
    echo >&2 "$@ is not a valid directory!"
    exit 1
fi

2
이중 [[]]이 필요한 이유는 무엇입니까?
vehomzzz

5

다음과 같은 작업을 수행하여 점 a와 b를 간결하게 검증 할 수 있습니다.

#!/bin/sh
MYVAL=$(echo ${1} | awk '/^[0-9]+$/')
MYVAL=${MYVAL:?"Usage - testparms <number>"}
echo ${MYVAL}

우리에게 ...

$ ./testparams.sh 
Usage - testparms <number>

$ ./testparams.sh 1234
1234

$ ./testparams.sh abcd
Usage - testparms <number>

이 방법은 sh에서 잘 작동합니다.


2

하나의 라이너 Bash 인수 유효성 검사, 디렉터리 유효성 검사 포함 및 제외

나를 위해 일한 몇 가지 방법이 있습니다. 전역 스크립트 네임 스페이스에서 사용할 수 있습니다 (전역 네임 스페이스에있는 경우 함수 내장 변수를 참조 할 수 없음).

빠르고 더러운 한 라이너

: ${1?' You forgot to supply a directory name'}

산출:

./my_script: line 279: 1: You forgot to supply a directory name

Fancier-기능 이름 및 사용법 제공

${1? ERROR Function: ${FUNCNAME[0]}() Usage: " ${FUNCNAME[0]} directory_name"}

산출:

./my_script: line 288: 1:  ERROR Function: deleteFolders() Usage:  deleteFolders directory_name

현재 기능을 복잡하게하지 않고 복잡한 유효성 검사 로직 추가

인수를받는 함수 또는 스크립트 내에 다음 줄을 추가합니다.

: ${1?'forgot to supply a directory name'} && validate $1 || die 'Please supply a valid directory'

그런 다음 다음과 같은 작업을 수행하는 유효성 검사 함수를 만들 수 있습니다.

validate() {

    #validate input and  & return 1 if failed, 0 if succeed
    if [[ ! -d "$1" ]]; then
        return 1
    fi
}

실패시 스크립트를 중단하는 die 함수

die() { echo "$*" 1>&2 ; exit 1; }

추가 인수의 경우 형식을 복제하여 추가 행을 추가하십시오.

: ${1?' You forgot to supply the first argument'}
: ${2?' You forgot to supply the second argument'}

1

이전 게시물이지만 어쨌든 기여할 수 있다고 생각했습니다.

스크립트는 반드시 필요하지 않으며 와일드 카드에 대한 약간의 내성을 가지고 명령 줄에서 수행 할 수 있습니다.

  1. 야생 어디서나 일치합니다. 하위 "폴더"발생을 제거 할 수 있습니다.

    $ rm -rf ~/*/folder/*
  2. 셸 반복. 한 줄로 특정 사전 및 사후 폴더를 제거 할 수 있습니다.

    $ rm -rf ~/foo{1,2,3}/folder/{ab,cd,ef}
  3. 쉘 반복 + var (BASH 테스트 됨).

    $ var=bar rm -rf ~/foo{1,2,3}/${var}/{ab,cd,ef}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.