如下的命令 echo '\1'
在命令行 (M-x terminal 或者 eshell )里的结果为
\1
但是在
(shell-command-to-string "echo '\1'")
的结果为
"^A\n"
是哪里的原因呢?
有一个具体的例子:(OSX环境)
ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'
在命令行里执行结果,可以正确返回本机Ip,
但是
(shell-command-to-string
"ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'")
返回
"^B\n^B\n"
(insert "echo '\\1'")
echo '\1'
(insert "echo '\1'")
echo '^A'
Emacs Lisp 的 string 是有转义的。
2 个赞
如果把 \1
看成两个字符长度的字符串,在 Emacs Lisp 中,你得用 "\\1"
。
从 Emacs Lisp 执行 Shell 时,比较烦人的是,你有可能得要 Quote 参数两次(甚至更多)。正则表达式也有类似的问题。
2 个赞
明白了,有没有办法自动帮我Quote呢,
输入 ‘\1’
输出 ‘\1’
毕竟Bash/Perl 的语法都没有2个\,不想维护2套脚本,如果有办法把bash的脚本转成Elisp可以执行的就好了
用 read-string
read-buffer
之類。
如果是 ParEdit 用户的话,选中之后按 "
就完了。比如选中 \n
之后,按 "
得到 "\\n"
。
1 个赞
这个说法有问题:
(string= "\1" "^A") ;; -> t (^A 需要 C-q C-a 输入)
也就是说实质上 \1 已经被存成 ^A 了。输入和输出应该是下面这个。
;; 输入 "^A"
;; 输出 "\\1"
这样一来不管是 paredit 还是 read-string 都不符合需求的。
(setq str "\1") ;; 已经是 ^A 了
(let ((unread-command-events (listify-key-sequence (kbd "RET"))))
(read-string "" nil nil str))
;; -> "^A"
;; 需求应该是 "\\1"
Eshell的 alias 应该可以满足需求,因为这个是读一个文件。然而这样可定制性就差了,比如需要动态拼接一个命令的时候,就实现不了了
从来没有直接在脚本里面输入 ^A
的道理,因为多数编辑器都直接忽略不会显示这类字符,反而会引起歧义。确保能 \1
变成 \\1
就足够了。
然后我的意思是 read-string
不是这么用的,是让你直接执行 (read-string "")
然后在 minibuffer 里面输入的。如果是整个脚本文件,就用 read-buffer
处理。
我明白你的意思,然而我想找的是一种自动化的方法,不要手动输入。
这个和 Eshell 的alias 应该是一个道理,是可以做的,然而就很难更深入的自定义了,举个例子,
ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'
这样一句命令,后面有2个bash格式的\
,Eshell 可以正确执行,因为读这个Alias文件的时候,就相当于read-buffer一次了。
但是这个命令想修改就很难了,(虽然这个命令确实不怎么需要修改),比如有时候需要Emacs上下文里的变量拼接出一个命令,只能通过字符串替换的方法了。
(defun selected-to-escaped-string (beg end)
(interactive "r")
(let ((standard-output (current-buffer)))
(print (buffer-substring-no-properties beg end))))
ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p'
"ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\\.){3}[0-9]*).*/\\2/p'"
按需自改。
2 个赞