Написание сценариев оболочки может быть довольно сложным, прежде всего потому, что оболочка не самый дружественный из используемых языков. Тем не менее, я надеюсь показать вам в этом уроке, что написание сценариев оболочки на самом деле не так сложно и не так страшно, как вы могли бы ожидать.
Для этого урока мы напишем скрипт, который немного упростит процесс использования тестовой среды Jasmine . На самом деле, я бы не использовал этот скрипт сегодня; Я бы использовал Grunt.js или что-то подобное. Тем не менее, я написал этот сценарий еще до того, как появился Grunt, и обнаружил, что его написание оказалось отличным способом освоения сценариев оболочки, поэтому мы его используем.
Одно замечание: этот урок слабо связан с моим предстоящим курсом Tuts + Premium «Расширенные методы командной строки». Чтобы узнать больше о чем угодно из этого урока, следите за обновлениями этого курса. В дальнейшем в этом уроке он будет называться «курс».
Итак, наш сценарий, который я называю jazz
, будет иметь четыре основные функции:
- Он загрузит Jasmine из Интернета, разархивирует его и удалит пример кода.
- Он создаст файлы JavaScript и связанные с ними файлы спецификаций и предварительно заполнит их небольшим количеством кода шаблона.
- Это откроет тесты в браузере.
- Он отобразит текст справки, в котором изложено выше.
Давайте начнем с файла сценария.
Шаг 1 — Создание файла
Написание сценария оболочки полезно, только если вы можете использовать его из терминала; чтобы иметь возможность использовать ваши собственные скрипты на терминале, вам нужно поместить их в папку, которая находится в переменной PATH
вашего терминала (вы можете увидеть вашу PATH
, запустив echo $PATH
). Я создал папку ~/bin
(где ~
— домашний каталог) на моем компьютере, и там я хотел бы сохранить пользовательские сценарии (если вы сделаете то же самое, вам придется добавить его в свой путь). Итак, просто создайте файл с именем jazz
и поместите его в свою папку.
Конечно, мы также должны сделать этот файл исполняемым; иначе мы не сможем его запустить. Мы можем сделать это, выполнив следующую команду:
1
|
chmod +x jazz
|
Теперь, когда мы действительно можем выполнить скрипт, давайте добавим очень важную часть. Все сценарии оболочки должны начинаться с шебанга ). Как говорится в Википедии, это должна быть первая строка сценария; в нем указывается, с каким интерпретатором или оболочкой следует запускать этот скрипт. Мы просто собираемся использовать базовую стандартную оболочку:
1
|
#!/bin/sh
|
Хорошо, со всей этой настройкой мы готовы начать писать настоящий код.
Шаг 2 — Изложение потока сценариев
Ранее я указывал, какими должны быть различные возможности нашего сценария оболочки. Но как скрипт узнает, какую функцию запустить? Мы будем использовать комбинацию параметра оболочки и оператора case. При запуске сценария из командной строки мы будем использовать подкоманду, например:
1
2
3
4
|
jazz init
jazz create SomeFile
jazz run
jazz help
|
Это должно выглядеть знакомо, особенно если вы использовали Git:
1
2
3
|
git init
git status
git commit
|
Основываясь на этом первом параметре ( init
, create
, run
, help
), наш оператор case решит, что запускать. Однако нам нужен случай по умолчанию: что произойдет, если не указан первый параметр или мы получим нераспознанный первый параметр? В этих случаях мы покажем текст справки. Итак, начнем!
Шаг 3 — Написание текста справки
Мы начнем с оператора if
который проверяет наш первый параметр:
1
2
3
4
5
6
|
if [ $1 ]
then
# do stuff
else
# show help
fi
|
Сначала вы можете быть немного смущены, потому что оператор if
в оболочке довольно сильно отличается от оператора if
в «обычном» языке программирования. Чтобы лучше понять это, посмотрите видеоролик с условными заявлениями в курсе. Этот код проверяет наличие первого параметра ( $1
); если он есть, мы выполним код then
; else
мы покажем текст справки.
Это хорошая идея, чтобы обернуть печать текста справки в функцию, потому что мы должны вызывать ее несколько раз. Нам нужно определить функцию перед ее вызовом, поэтому мы поместим ее сверху. Мне это нравится, потому что теперь, как только я открываю файл, я вижу документацию для скрипта, которая может быть полезным напоминанием при возврате к коду, который вы давно не видели. Без дальнейших церемоний, вот функция help
:
1
2
3
4
5
6
7
|
function help () {
echo «jazz — A simple script that makes using the Jasmine testing framework in a standalone project a little simpler.»
echo «
echo » jazz init — include jasmine in the project»;
echo » jazz create FunctionName — creates ./src/FunctionName.js ./spec/FunctionNameSpec.js»;
echo » jazz run — runs tests in browser»;
}
|
Теперь просто замените эту функцию # show help
вызовом функции help
.
1
2
3
|
else
help
fi
|
Шаг 4 — Написание описания дела
Если есть первый параметр, нам нужно выяснить, какой это. Для этого мы используем оператор case
:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
case «$1» in
init)
;;
create)
;;
run)
;;
*)
help
;;
esac
|
Мы передаем первый параметр в оператор case
; затем он должен соответствовать одной из четырех вещей: «init», «create», «run» или наш подстановочный знак, регистр по умолчанию. Обратите внимание, что у нас нет явного случая «помощи»: это только наш случай по умолчанию. Это работает, потому что все, кроме «init», «create» и «run», не являются командами, которые мы распознаем, поэтому оно должно получить текст справки.
Теперь мы готовы написать функциональный код, и мы начнем с jazz init
.
Шаг 5 — Подготовка Жасмин с jazz init
Весь код, который мы здесь напишем, пойдет в нашем случае init)
из приведенного выше оператора case. Первым шагом является загрузка автономной версии Jasmine, которая поставляется в виде zip-файла:
1
2
|
echo «Downloading Jasmine …»
curl -sO $JASMINE_LINK
|
Сначала мы выводим небольшое сообщение, а затем используем curl
для загрузки zip. Флаг s
делает его без вывода сообщений (без вывода), а флаг O
сохраняет содержимое zip-файла в файл (в противном случае он выводит его в стандартный поток). Но что с этой переменной $JASMINE_LINK
? Ну, вы могли бы поместить туда фактическую ссылку на zip-файл, но я предпочитаю помещать ее в переменную по двум причинам: во-первых, это удерживает нас от повторения части пути, как вы увидите через минуту. Во-вторых, с этой переменной в верхней части файла, это позволяет легко изменить версию Jasmine, которую мы используем: просто измените одну переменную. Вот это объявление переменной (я поместил его вне оператора if
, в верхней части):
1
|
JASMIME_LINK=»http://cloud.github.com/downloads/pivotal/jasmine/jasmine-standalone-1.3.1.zip»
|
Помните, что в этой строке не должно быть пробелов вокруг знака равенства.
Теперь, когда у нас есть наш zip-файл, мы можем распаковать его и подготовить содержимое:
1
2
|
unzip -q <code>basename $JASMINE_LINK</code>
rm -rf <code>basename $JASMINE_LINK</code> src/*.js spec/*.js
|
В двух из этих строк мы используем basename $JASMINE_LINK
; команда basename
просто уменьшает путь к базовому имени: поэтому path/to/file.zip
становится просто file.zip
. Это позволяет нам использовать эту переменную $JASMINE_LINK
для ссылки на наш локальный zip-файл.
После распаковки мы удалим этот zip-файл, а также все файлы JavaScript в каталогах src
и spec
. Это примеры файлов, которые поставляет Jasmine, и они нам не нужны.
Далее у нас есть проблема только для Mac. По умолчанию, когда вы загружаете что-то из Интернета на Mac, когда вы пытаетесь запустить это в первый раз, вам будет предложено подтвердить, что вы хотите запустить это. Это из-за расширенного атрибута com.apple.quarantine
который Apple помещает в файл. Нам нужно удалить этот атрибут.
1
2
3
4
|
if which xattr > /dev/null && [ «<code>xattr SpecRunner.html</code>» = «com.apple.quarantine» ]
then
xattr -d com.apple.quarantine SpecRunner.html
fi
|
Мы начнем с проверки наличия команды xattr
, поскольку она не существует в некоторых системах Unix (я не уверен, но это может быть программа только для Mac). Если вы смотрели экранную трансляцию курса по условным выражениям, вы будете знать, что мы можем передать любую команду if
; если он имеет статус выхода, отличный от 0
, он ложен. Если команда найдет команду xattr
, она завершится с 0
; в противном случае он выйдет с 1
. В любом случае, which
будет отображать некоторый вывод; мы можем предотвратить это, перенаправив его в /dev/null
(это специальный файл, который отбрасывает все записанные в него данные).
Этот двойной амперсанд является логическим И; это для второго условия, которое мы хотим проверить. То есть есть ли у SpecRunner.html
этот атрибут? Мы можем просто запустить команду xattr
для файла и сравнить ее вывод со строкой, которую мы ожидаем. (Мы не можем просто ожидать, что файл будет иметь атрибут, потому что вы действительно можете отключить эту функцию в Mac OS X, и мы получим ошибку при попытке удалить его, если файл не имеет атрибута).
Итак, если xattr
найден и файл имеет атрибут, мы удалим его с флагом d
(для удаления). Довольно просто, правда?
Последний шаг — редактирование SpecRunner.html
. В настоящее время он содержит теги сценариев для примеров файлов JavaScript, которые мы удалили; мы также должны удалить эти теги сценариев. Я случайно знаю, что эти теги сценариев занимают строки от 12 до 18 в файлах. Итак, мы можем использовать редактор потоков sed
для удаления этих строк:
1
2
|
sed -i «» ‘12,18d’ SpecRunner.html
echo «Jasmine initialized!»
|
Флаг i
указывает sed
редактировать файл на месте или сохранять выходные данные команды в тот же файл, который мы передали; пустая строка после флага означает, что мы не хотим, чтобы sed
создавал для нас резервную копию файла; если вы этого хотите, вы можете просто вставить расширение файла в эту строку (например, SpecRunner.html.bak
, чтобы получить SpecRunner.html.bak
).
Наконец, мы дадим пользователю знать, что Jasmine был инициализирован. И это все для нашей jazz init
команды jazz init
.
Шаг 6 — Создание файлов с помощью jazz create
Далее, мы собираемся позволить нашим пользователям создавать файлы JavaScript и связанные с ними файлы спецификаций. Эта часть кода будет помещена в раздел «create» оператора case
мы написали ранее.
1
2
3
4
5
6
|
if [ $2 ]
then
# create files
else
echo «please include a name for the file»
fi
|
При использовании jazz create
нам необходимо включить имя файла в качестве второго параметра: например, jazz create View
. Мы будем использовать это для создания src/View.js
и spec/ViewSpec.js
. Итак, если второго параметра нет, мы напомним пользователю добавить его.
Если есть имя файла, мы начнем с создания этих двух файлов (внутри части then
):
1
2
|
echo «function $2 () {\n\n}» > src/$2.js
echo «describe(‘$2’, function () {\n\n});»
|
Конечно, вы можете поместить в файл src
все, что захотите. Я делаю что-то простое здесь; поэтому jazz create View
создаст src/View.js
с этим:
1
2
3
|
function View () {
}
|
Вы можете заменить эту первую строку echo
на это:
1
|
echo «var $2 = (function () {\n\tvar $2Prototype = {\n\n\t};\n\n\treturn {\n\t\tcreate : function (attrs) {\n\t\t\tvar o = Object.create($2Prototype);\n\t\t\textend(o, attrs);\n\t\t\treturn o;\n\t\t}\n \t};\n}());»
|
И тогда jazz create View
приведет к этому:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
var View = (function () {
var ViewPrototype = {
};
return {
create : function (attrs) {
var o = Object.create(ViewPrototype);
extend(o, attrs);
return o;
}
};
}());
|
Так что, действительно, ваше воображение это предел. Конечно, вы захотите, чтобы файл спецификаций был стандартным кодом спецификации Jasmine, что я и описал выше; но вы можете настроить это так, как вам нравится.
Следующим шагом является добавление тегов сценария для этих файлов в SpecRunner.html
. Поначалу это может показаться сложным: как мы можем программно добавить строки в середину файла? Еще раз, это sed
который делает работу.
1
2
3
4
|
sed -i «» «11a\\
<script src=’src/$2.js’></script>\\
<script src=’spec/$2Spec.js’></script>
» SpecRunner.html
|
Мы начинаем так же, как и раньше: редактирование на месте без резервного копирования. Затем наша команда: в строке 11 мы хотим добавить две следующие строки. Важно избежать двух новых строк, чтобы они появлялись в тексте. Как видите, здесь просто вставляются эти два тега сценария, именно то, что нам нужно для этого шага.
Мы можем закончить с некоторыми выводами:
1
2
3
4
5
|
echo «Created:»
echo «\t- src/$2.js»
echo «\t- spec/$2Spec.js»
echo «Edited:»
echo «\t- SpecRunner.html»
|
И это jazz create
!
Шаг 7 — Запуск спецификаций с jazz run
Последний шаг — запустить тесты. Это означает открытие файла SpecRunner.html
в браузере. Здесь есть небольшая оговорка. В Mac OS X мы можем использовать команду open
чтобы открыть файл в программе по умолчанию; это не будет работать на любой другой ОС, но я так и делаю здесь. К сожалению, я не знаю настоящего кроссплатформенного способа сделать это. Если вы используете этот скрипт в Cygwin под Windows, вы можете использовать cygstart
вместо open
; в противном случае попробуйте поискать «скрипт браузера вашей операционной системы», откройте браузер »и посмотрите, что вы придумали. К сожалению, некоторые версии Linux (по крайней мере, Ubuntu, по моему опыту) имеют команду open
для чего-то совершенно другого. Все это говорит о том, что ваш пробег со следующим может отличаться.
1
2
3
4
5
6
|
if [ «`which open`» = ‘/usr/bin/open’ ]
then
open SpecRunner.html
else
echo «Please open SpecRunner.html in your browser»
fi
|
К настоящему времени вы точно знаете, что это делает: если мы open
, мы откроем SpecRunner.html
, в противном случае мы просто напечатаем сообщение, сообщающее пользователю открыть файл в браузере.
Первоначально, if
условие выглядело так:
1
|
if which open > /dev/null
|
Как и в случае с xattr
, он просто проверял существование open
; однако, поскольку я узнал, что в Linux есть другая команда open
(даже на моем сервере Ubuntu, который не может даже открыть браузер!), я подумал, что было бы лучше сравнить путь open
программы, поскольку Linux один находится в /bin/open
(опять же, по крайней мере, на сервере Ubuntu).
Все эти лишние слова об open
могут звучать как оправдание моего отсутствия хорошего решения, на самом деле это указывает на что-то важное в командной строке. Не путайте понимание терминала с пониманием конфигурации компьютера. Это руководство и связанный с ним курс научили вас немного больше об оболочке Bash (и оболочке Z), но это не значит, что каждый используемый вами компьютер будет настроен одинаково; Есть много способов установить новые команды (или разные версии команд), а также удалить команды. Будьте разработчиком .
Ну вот и весь сценарий! Вот и снова все вместе:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
#!
function help () {
echo «jazz — A simple script that makes using the Jasmine testing framework in a standalone project a little simpler.»
echo «»
echo » jazz init — include jasmine in the project»;
echo » jazz create FunctionName — creates ./src/FunctionName.js ./spec/FunctionNameSpec.js»;
echo » jazz run — runs tests in browser»;
}
JASMIME_LINK=»http://cloud.github.com/downloads/pivotal/jasmine/jasmine-standalone-1.3.1.zip»
if [ $1 ]
then
case «$1» in
init)
echo «Downloading Jasmine . . .»
curl -sO $JASMIME_LINK
unzip -q `basename $JASMIME_LINK`
rm `basename $JASMIME_LINK` src/*.js spec/*.js
if which xattr > /dev/null && [ «`xattr SpecRunner.html`» = «com.apple.quarantine» ]
then
xattr -d com.apple.quarantine SpecRunner.html
fi
sed -i «» «12,18d» SpecRunner.html
echo «Jasmine initialized!»
;;
create)
if [ $2 ]
then
echo «function $2 () {\n\n}» > ./src/$2.js
echo «describe(‘$2’, function () {\nit(‘runs’);\n});»
sed -i «» «11a\\
<script src=’src/$2.js’></script>\\
<script src=’spec/$2Spec.js’></script>
» SpecRunner.html
echo «Created:»
echo «\t- src/$2.js»
echo «\t- spec/$2Spec.js»
echo «Edited:»
echo «\t- SpecRunner.html»
else
echo ‘please add a name for the file’
fi
;;
«run»)
if [ «`which open`» = ‘/usr/bin/open’ ]
then
open ./SpecRunner.html
else
echo «Please open SpecRunner.html in your browser»
fi
;;
*)
help;
;;
esac
else
help;
fi
|
Ну, давай, попробуй!
1
2
3
4
5
6
|
mkdir project
cd project
jazz init
jazz create Dog
# edit src/Dog.js and spec/DogSpec.js
jazz run
|
Кстати, если вы хотите повеселиться с этим проектом, вы можете найти его на Github .
Вывод
Так что у вас есть это! Мы только что написали скрипт оболочки промежуточного уровня; это было не так уж плохо, правда? Не забывайте следить за моими предстоящими курсами Tuts + Premium ; Вы узнаете намного больше о многих методах, используемых в этой статье, а также о бесчисленном количестве других. Веселитесь на терминале!