Перевірка параметрів до сценарію 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 (як я це робив тоді) , то це може потенційно видалити багато речей, які ви дійсно не хочете видаляти.

Чи можна додати будь-яку форму перевірки до параметрів командного рядка? У моєму випадку було б непогано перевірити, що а) є один параметр, б) він числовий і в) що папка існує; перед продовженням сценарію.

Відповіді:


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; він не використовує ніяких башизмів, що насправді важливо, тому що /bin/shUbuntu насправді є dashці дні, а не bash.


не забудьте встановити + e і використовувати '-eq' замість '==' для цілих порівнянь
гармати

Змінено на -eq; що вас тут купує + e?
Брайан Кемпбелл

У моїй відповіді я виявив дві речі, які ви також можете поправити у вашому: спочатку hilighter SO божеволіє за $ # (трактуючи це як коментар). я зробив "$ #", щоб виправити це. по-друге, регулярний вираз також відповідає "foo123bar". я виправив це, зробивши ^ [0-9] + $. Ви також можете виправити це, скориставшись опцією grep's -x
Йоханнес Шауб - litb

1
@ojblass Я пропустив один із тестів, про які він запитував. Додавання цього означало також додавання до його каталогів для тестування, що значно розширило розмір відповіді, оскільки вони не можуть поміститися в одному рядку. Чи можете ви запропонувати більш компактний спосіб перевірки наявності кожного каталогу?
Брайан Кемпбелл,

1
відповідно до коментаря @Morten Nielsen нижче, grep '$ [0-9] + ^' справді виглядає дивним. Це не повинно бути '^ [0-9] + $'?
martin jakubik

21

shРішення по Brian Campbell, в той час як благородний і добре виконані, є кілька проблем, тому я думав , що забезпечити своє власне bashрішення.

Проблеми з shодним:

  • Тильда ~/fooне розширюється до вашого домашнього довідника всередині гередоків. І ні тоді, коли це читається у readзаяві або цитується у rmзаяві. Це означає, що ви отримаєте No such file or directoryпомилки.
  • Вилучення grepта таке для основних операцій є непростим. Особливо, коли ви використовуєте дерьмову оболонку, щоб уникнути "важкої" ваги баша.
  • Я також помітив кілька питань цитування, наприклад навколо розширення параметра в його echo.
  • Хоча це рідко, рішення не може впоратися з іменами файлів, що містять нові рядки. (Майже жодне рішення не shможе з ними впоратись - ось чому я майже завжди віддаю перевагу bash, це набагато стійкіше до кулі та важче використати при хорошому використанні).

Так, так, використання /bin/shдля вашого hashbang означає, що ви повинні уникати bashізмів будь-якою ціною, ви можете використовувати всі bashбажані вам ізми, навіть на Ubuntu або щось інше, коли ви чесні і ставите #!/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, вам слід помістити регулярний вираз у змінну. У будь-якому випадку, такі глобуси, як мій, завжди кращі і підтримуються в набагато більших версіях.

1
Тож чи характерний для цього ударний шматок $1 != *[^0-9]*?
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

11

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

Будь ласка, див. Статтю: Написання надійних скриптів Bash Shell - David Pashley.com .


Це дуже просто виправити в багатьох випадках і працює і для функцій. Дякую!
Манфред Мозер

9

Сторінка користувача для test ( 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

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>

Цей метод повинен добре працювати в ш.


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. Оболонка повторена. Давайте вилучимо певні папки попереднього та попереднього розміщення одним рядком

    $ 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}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.