Проверка параметров в скрипте Bash - PullRequest
87 голосов
/ 31 марта 2009

Я придумал базовую, чтобы помочь автоматизировать процесс удаления нескольких папок, когда они становятся ненужными.

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

Это вызывается так:

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

Проблема в том, что если вы забудете набрать id-number (как я только что сделал) , то это может потенциально удалить многие вещи, которые вы действительно не хотите удалять.

Есть ли способ добавить любую форму проверки к параметрам командной строки? В моем случае было бы хорошо проверить, что а) есть один параметр, б) он числовой, и c) эта папка существует; прежде чем продолжить со сценарием.

Ответы [ 9 ]

148 голосов
/ 31 марта 2009
#!/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/sh в Ubuntu в действительности dash в наши дни, а не bash.

20 голосов
/ 31 марта 2009

Решение sh от Brian Campbell, хотя и благородное и хорошо выполненное, имеет несколько проблем, поэтому я подумал, что смогу предложить собственное решение bash.

Проблемы с sh one:

  • Тильда в ~/foo не распространяется на ваш домашний каталог внутри heredocs. И ни тогда, когда он читается оператором read или цитируется в операторе rm. Это означает, что вы получите No such file or directory ошибок.
  • Отключение grep и такое для основных операций - глупость. Особенно, когда вы используете дерьмовую оболочку, чтобы избежать «тяжелого» веса удара.
  • Я также заметил несколько проблем с цитированием, например, вокруг расширения параметра в его echo.
  • Хотя это редко, решение не может справиться с именами файлов, которые содержат переводы строк (Практически ни одно решение в sh не может справиться с ними - вот почему я почти всегда предпочитаю bash, это гораздо более пуленепробиваемый и труднее использовать при правильном использовании).

Хотя да, использование /bin/sh в качестве хэш-банга означает, что вы должны избегать bash измов любой ценой, вы можете использовать все 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, вы должны поместить регулярное выражение в переменную. В любом случае глобусы, подобные моему, всегда предпочтительнее и поддерживаются в гораздо большем количестве версий bash.
14 голосов
/ 31 марта 2009

Я бы использовал bash's [[:

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

Я нашел этот FAQ , чтобы быть хорошим источником информации.

12 голосов
/ 31 марта 2009

Не такой пуленепробиваемый, как приведенный выше ответ, но все же действует:

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

# rm commands go here
10 голосов
/ 07 декабря 2010

Используйте set -u, что приведет к тому, что любая неустановленная ссылка на аргумент немедленно завершит работу сценария.

http://www.davidpashley.com/articles/writing-robust-shell-scripts.html

8 голосов
/ 31 марта 2009

Используйте '-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
7 голосов
/ 31 марта 2009

Страница man для теста (man test) содержит все доступные операторы, которые вы можете использовать в качестве логических операторов в bash. Используйте эти флаги в начале вашего скрипта (или функций) для проверки ввода так же, как и на любом другом языке программирования. Например:

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

if [ -z $2 ] ; then
  echo "Second parameter needed!" && exit 2;
fi
5 голосов
/ 26 марта 2010

Вы можете компактно проверить точку 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.

1 голос
/ 22 декабря 2010

Старый пост, но я решила, что могу внести свой вклад в любом случае.

Сценарий, возможно, не нужен, и с некоторой терпимостью к подстановочным знакам можно было бы выполнить из командной строки.

  1. дикий где угодно. Позволяет удалить любое вхождение подпапки "папка"

    $ rm -rf ~/*/folder/*
    
  2. Оболочка повторяется. Позволяет удалить определенные папки pre и post одной строкой

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