在Emacs中运行命令行命令,并接收它们的输出

时间:2013-03-19 22:51:05

标签: emacs command-line elisp ascii ascii-art

我想在emacs中的循环中运行一组命令行命令,当用户点击一个键时循环停止。

这是为了看到一个ascii'视频'出现在Emacs中,当你按一个键时会停止。

我认为将图像到ascii位的文本作为注释(图像来自我的相机)会很有趣。

我使用imagesnap来拍摄相机图像,使用jp2a将其转换为ascii。我认为imagesnap只是mac。这是我到目前为止的代码:

(defun ascii-video-comment ()                                                          
    "Makes video comment, requires imagemagick, jp2a, imagesnap"                        
    (interactive)                                                                       
    (shell-command "imagesnap -q ~/Desktop/emacs-snap.jpg")                             
    (insert (shell-command-to-string "jp2a --width=48 ~/Desktop/emacs-snap.jpg"))                                                                                                                                                               
    (shell-command "rm ~/Desktop/emacs-snap.jpg")                                       
) 

这只需从相机拍摄,转换为ascii,将其插入到我的文件中,然后将光标放在后面。

就像我说的那样,我希望它能够保持循环,呈现出一个缓慢的ascii视频,直到我点击一个键来选择当前的“帧”。

这甚至可能吗?


修改

这是我目前的代码,我很满意。它循环20次,您可以通过取消(C-g)来选择当前图像。当你第二次这样做时,事情似乎出了问题。

(defun ascii-video-comment ()
 "Makes video comment, requires imagemagick, jp2a, imagesnap"
 (interactive)
 (cl-loop repeat 20 do
  (shell-command "imagesnap -q ~/Desktop/ascii-video-comment.jpg")
  (cua-set-mark)
  (insert (shell-command-to-string "jp2a --width=120 ~/Desktop/ascii-video-comment.jpg"))
  (shell-command "rm ~/Desktop/ascii-video-comment.jpg")
  (comment-region (mark) (point))
  (cua-set-mark)
  (pop-global-mark)
  (sit-for 0.1)
  (undo)
 )
)

3 个答案:

答案 0 :(得分:2)

EDIT"最终代码":不要在lisp程序中使用交互式命令:代码变得脆弱且效率低下。 例如,shell-command C-h f shell-command RET )的文档字符串明确指出:

  

在Elisp中,通常可以通过致电call-process或更好地为您提供服务   直接start-process,因为它提供更多控制而不是   强制使用shell(需要引用参数)

另外,请使用delete-file代替(shell-command "rm ...")

请勿在程序中使用cua-set-markpop-global-mark,尤其是undo。 改为绑定变量:

(let ((beg (point)))
  (call-process "jp2a" nil t t "--width=120" "~/Desktop/ascii-video-comment.jpg")
  (comment-region beg (point))
  (sit-for 0.1)
  (delete-region beg (point)))

答案 1 :(得分:1)

AFAIR,Emacs不提供API来轮询待处理事件,因此有两个选项。 UPD:忽略这一点,应该事先阅读手册,Emacs确实提供了API轮询待处理事件

(defun start-printing-messages-2 ()
  (interactive)
  (while (not (input-pending-p)) 
    (loop-body-function)
    (redisplay 'force)))

如果您希望在命令执行之间出现延迟,那么您需要sit-for

(defun start-printing-messages-3 ()
  (interactive)
  (while (sit-for 0.05)
    (loop-body-function)))

如果你希望延迟从循环体的开始而不是它的结束开始计数(如果你的循环体可能需要很长时间),你需要设置完整的计时器执行:你基本上在计时器中运行一个函数并添加一个会杀死该计时器的post-command-hook

(defvar loop-run-count 0)
(defvar loop-timer-object nil)

(defun loop-body-function ()
  (setq loop-run-count (1+ loop-run-count))
  (message "The function was run %s times" loop-run-count))

(defun stop-printing-messages ()
  (when loop-timer-object   
    (cancel-timer loop-timer-object)
    (remove-hook 'post-command-hook 'stop-printing-messages)))

(defun start-printing-messages ()
  (interactive)
  (setq loop-run-count 0)
  ;; post-command-hook is added via timer too, because otherwise it
  ;; might get called right after this function completes and this
  ;; would kill the timer that didn't even start yet.
  ;; get killed right after creation.
  (run-with-timer
   0.01 nil (lambda ()
          (add-hook 'post-command-hook 'stop-printing-messages)))
  (setq loop-timer-object 
    (run-with-timer nil 0.01 'loop-body-function)))

答案 2 :(得分:1)

您可以使用while-no-inputsit-for等待输入。

对于您描述的用例,sit-for就是您所需要的。

请注意,您应该将两个shell调用与&&组合在一起,或者创建一个shell 脚本。

这是一个玩具命令,它可以满足您的需要,但只会闪烁随机数。

(defun flash-me ()
  "flash random number until you press a key"
  (interactive)
  (let ((beg (copy-marker (point)))
        (end (copy-marker (point) t)))
    (loop do (progn
               (delete-region beg end)
               (insert (shell-command-to-string "echo $RANDOM")))
          while (sit-for 1))
    (set-marker beg nil)
    (set-marker end nil)))