Написание кода Common Lisp, который выполняется из командной строки, но не внутри интерпретатора
При написании кода Common Lisp я использую SLIME. В частности, я компилирую буфер, содержащий определения функций, с помощью C C Ck, а затем переключаюсь на REPL для запуска этих функций. Размещение исполняемого кода для запуска этих функций в буфере, кажется, не так хорошо работает. Если в коде есть ошибки, это может привести к путанице.
Было бы удобно иметь способ включать код, который не компилируется в буфер, но запускается из командной строки, например, при выполнении
sbcl --script foo.lisp
Если бы это было так, мне не пришлось бы продолжать добавлять и удалять код каждый раз, когда я хотел запустить код из командной строки. Существует ли такое условие?
Это аналогично условию Питона.
if __name__=='__main__':
значение false, если файл Python импортирован как модуль, но значение true, если он запускается как скрипт.
Этот пост в блоге под названием "Использование SBCL для сценариев оболочки Common Lisp", найденный случайным поиском Google, имеет
;; If run from command line, the following if-test will succeed
(if (> (length sb-ext:*posix-argv*) 1)
;; Code that is executed from the command line only goes here
)
Включенный код действительно не запускается компилятором внутри SLIME, но он не запускается sbcl --script
или.
ОБНОВЛЕНИЕ: Спасибо Всеволоду Демкину за его полезный ответ и продолжение. Далее следуют некоторые заметки об этом ответе, составленные из комментариев к этому ответу. @ Всеволод, если ты добавишь это в свой ответ, я их удалю.
Сначала я попросил способ запуска кода из командной строки, но не из интерпретатора. Поставленное решение делает больше; это также позволяет запускать код из интерпретатора, но не из командной строки.
Первый шаг - определить функцию макроса читателя для символа макроса.
#!
, Как указано в ссылке "При встрече с макро-символом читатель Lisp вызывает свою макро-функцию читателя". Функция читателя определяется вызовомset-dispatch-macro-character
, Итак, когда#!
персонаж виден,set-dispatch-macro-character
вызывает лямбда-функцию, определенную в теле, для вызова. Затем эта функция добавляет ключевое слово:noscript
к*features*
переменная. См. Также обсуждение того, для чего нужны макросы читателей в вопросе SO. Чтение макросов: для чего вы их используете?,Обратите внимание, что ключевое слово
:noscript
добавлен в*features*
именно тогда, когда#!
персонаж присутствует. Кроме того,#!
символ присутствует, когда код выполняется внутри интерпретатора, например, при использованииslime
, но, по-видимому, отсутствует (удален) из программы
текст поsbcl --script
это запустить. Следовательно,:noscript
добавлен в*features*
когда код запускается в интерпретаторе, но не когда запускается как
скрипт.Теперь мы используем макросы встроенного считывателя
#-/#+
которые, как сказал Всеволод, ведут себя так же, как и С#IFDEF/#IFNDEF
, Они проверяют на символ
в*features*
, В этом случае,#-:noscript
проверяет отсутствие:noscript
, а также#+:noscript
проверяет наличие:noscript
,
Если эти условия выполняются, выполняется соответствующий код. Чтобы обернуть блок кода, можно использоватьprogn
как это:#-:noscript (progn <your code here>)
,Наконец, нужно позвонить
set-dispatch-macro-character
перед запуском кода, который использует эту функциональность. В случаеsbcl
можно положить в файл инициализации~/.sbclrc
, Обратите внимание, что этот подход не зависит от реализации Common Lisp, являющейся SBCL.Как упоминалось в списке sbcl-devel, более простой альтернативой является использование факта, что ключевое слово
:SWANK
появляется, когда один из типов*features*
в
REPL внутри Emacs, используя SLIME. SWANK является серверной стороной SLIME. SLIME, вероятно, следует более точно называть SLIME/SWANK, так как эти два компонента являются клиент-серверными компонентами архитектуры клиент-сервер. Я нашел этот пост под названием " Понимание SLIME", который был полезен.Итак, можно использовать
#-:swank
а также#+:swank
как#-:noscript
а также#+:noscript
за исключением того, что не нужно писать какой-либо код. Конечно, это тогда не будет работать, если кто-то использует интерпретатор командной строкиsbcl
Например, с тех пор:SWANK
не появится в*features*
,
3 ответа
Вы можете использовать следующий трюк:
Определите функцию отправки для Шебанга:
(set-dispatch-macro-character #\# #\! (lambda (stream c n) (declare (ignore c n)) (read-line stream) `(eval-when (:compile-toplevel :load-toplevel :execute) (pushnew :noscript *features*))))
В вашем скрипте используйте файл
#-:noscript
:#!/usr/local/bin/sbcl --script (defun test (a) (print a)) (test 1) #-:noscript (test 2) #+:noscript (test 3)
проведение
./test.lisp
выведет 1 и 2, а Cc Ck выведет 1 и 3.
редактирует
Этот трюк должен работать, потому что линия Шебанга полностью удаляется sbcl --script
, но не удаляется, когда файл загружается через SLIME или другие механизмы.
Недостатком этого подхода является то, что мы обусловливаем отсутствие :noscript
в особенностях, а не наличие :script
, Чтобы исправить это, нажатие соответствующей функции должно быть сделано в sbcl --script
обработка сама.
Fare Rideau написал изящную утилиту unix CL-Launch, которая позволяет запускать программное обеспечение lisp из командной строки. Он имеет встроенную поддержку Quicklisp и работает с большинством реализаций Common Lisp.
Пример скрипта может выглядеть так:
#!/usr/bin/cl -sp "hunchentoot" -Q -E main
(defun main (argv)
(format t "~A: ~{~A ~}~%" (truename ".") argv)
(hunchentoot:start
(make-instance 'hunchentoot:acceptor
:document-root (truename ".")
:port 8080))
(do ((x #\s (read-char)))
((char-equal x #\q) nil)))
После добавления +x разрешений к скрипту и его запуска он запустит http-сервер в текущем каталоге. Флаг -sp указывает на пакет, который вы хотите загрузить, так что это довольно чистый способ абстрагирования сценария оболочки от пакета.
Для более подробной информации обращайтесь: http://cliki.net/CL-Launch
У меня был тот же вопрос, и я только что наткнулся на это обсуждение. По крайней мере, с sbcl кажется, я могу использовать (sb-ext:posix-getenv "_")
, При запуске в слизи возвращается /usr/bin/emacs
(или каков бы ни был путь к emacs), а в остальном команда, которую я использую для вызова скрипта. Так что всегда можно различить вызовы слизи и скрипта, пока вы не станете участником emacs:)
Если вы хотите получить полный путь к скрипту, который вы вызываете, вы можете использовать (truename (sb-ext:posix-getenv "_"))
, Тем не менее, при запуске из slime он вернет эффективное имя пути emacs, например /usr/bin/emacs-24.5
, так что это может быть менее удобно.