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)パラメータが1つあること、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

edit:最初にディレクトリが存在するかどうかを確認する部分を逃したので、スクリプトを完成させて追加しました。また、コメントで提起された問題に対処しました。正規表現を修正し、から==に切り替えましたeq

これは、私の知る限り、移植可能なPOSIX準拠のスクリプトでなければなりません。/bin/shUbuntuでは最近ではなく、バシズムを使用していませdashbash


整数比較のために+ Eと使用の代わりに「==」の「-eq」を設定することを忘れない

それを-eqに変更しました。セット+ eはここで何を買うのですか?
ブライアンキャンベル

私はあなたの答えでも修正したいと思うかもしれない2つのことを私の答えで見つけました:最初にSOハイライターが$#のために狂ってしまいます(コメントとして扱います)。私はそれを修正するために「$#」をしました。次に、正規表現も「foo123bar」に一致します。^ [0-9] + $を実行して修正しました。grepの-xオプションを使用して修正することもできます
Johannes Schaub-litb 2009年

1
@ojblass彼が尋ねていたテストの1つが欠けていました。これを追加することは、テスト対象のディレクトリにも追加することを意味し、1行に収まらないため、回答のサイズが大幅に拡大しました。各ディレクトリの存在をテストするよりコンパクトな方法を提案できますか?
ブライアンキャンベル

1
下記の@Morten Nielsenの回答コメントに従って、grep '$ [0-9] + ^'は実際に奇妙に見えます。'^ [0-9] + $'にすべきではありませんか?
マーティン・ジャクビク

21

sh溶液はBrian Campbell、高貴でうまく実行、いくつかの問題を有している、ので、私は、私は自分の提供したいと思ったbashソリューションを。

sh1つの問題:

  • ~/fooチルダインは、ヒアドキュメント内のホームディレクトリに展開されません。そして、それがread声明によって読まれたり、声明で引用されたりするときも同様rmです。つまり、No such file or directoryエラーが発生します。
  • grep基本的な操作のためのフォークオフなどはダフです。特に、bashの「重い」重みを回避するためにくだらないシェルを使用している場合。
  • また、たとえば彼のパラメータ拡張に関するいくつかの引用の問題にも気付きましたecho
  • まれではありますが、ソリューションは改行を含むファイル名に対処できません。(ほとんどの解決策はshそれらに対処できません-これが私がほとんど常に好む理由ですbash、それははるかに防弾であり、うまく使用すると悪用することが難しくなります)。

はい、/bin/shハッシュバングに使用することはbashすべてのコストでismsを回避する必要があることを意味しますがbash、Ubuntuや正直なところにある場合でも、好きなismsをすべて使用できます#!/bin/bash

だから、これはbashより小さく、よりクリーンで、より透明で、おそらく「より速く」、そしてより防弾的なソリューションです。

[[ -d $1 && $1 != *[^0-9]* ]] || { echo "Invalid input." >&2; exit 1; }
rm -rf ~/foo/"$1"/bar ...
  1. 周りの引用に注意してください $1rmステートメントください!
  2. -d場合もチェックは失敗します$1空であるので、それは1に2つのチェックです。
  3. 理由のために正規表現を避けました。=~bashで使用する必要がある場合は、変数に正規表現を入れる必要があります。いずれにせよ、私のようなグロブは常に望ましいものであり、はるかに多くのbashバージョンでサポートされています。

1
では、グロビングピースの$1 != *[^0-9]*bashは具体的ですか?
2014年

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

テスト(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 2012

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() { 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. シェルは繰り返しました。特定のpreおよびpostフォルダーを1行で削除できます

    $ 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.