emacs 中是否有应用命令到区域内的每一行?

Is there an apply-command-to-each-line-in-region in emacs?

我有一堆 links 保存在一个 orgmode 文件中,比如说...

http://www.whosebug.com
http://www.google.com
http://www.github.com

我可以通过将光标放在 link 上并执行 C-c C-o 打开每一个,它会方便地弹出我的默认浏览器并在选项卡中打开 link。

现在假设我有 20 个 link。有没有一种方便的方法可以将这样的函数应用于选定区域内的每一行,而无需录制显式宏?

我想象它看起来像...

Select region
M-x foreach-in-region
Keystrokes to apply to each line: C-c C-o

这只是针对已经定义的函数。我想没有的方式会像...

with cursor on first line of link
F3 # to start record macro
C-c C-o 
down arrow
F4
Select region (omitting the first line, since that's now already opened in my browser)
C-x C-k r

这个存在吗?如果没有,我将如何口齿不清?

(defun do-lines (fun &optional start end)
  "Invoke function FUN on the text of each line from START to END."
  (interactive
   (let ((fn   (intern (completing-read "Function: " obarray 'functionp t))))
     (if (use-region-p)
         (list fn (region-beginning) (region-end))
       (list fn (point-min) (point-max)))))
  (save-excursion
    (goto-char start)
    (while (< (point) end)
      (funcall fun (buffer-substring (line-beginning-position) (line-end-position)))
      (forward-line 1))))

评论后更新--

现在听起来您不想输入函数名称而是按下一个键,然后将绑定到该键的命令应用于区域(或缓冲区)中的每一行。

像下面这样的东西会做到这一点。但是,请注意该命令通常具有特定的行行行为。例如,如果您要按 C-k (kill-lines) 键,那么它已经在它杀死的每一行之后向前移动。因为 do-lines 不知道您将调用哪种函数(命令),所以每次调用后它都会前进到下一行。对于像 kill-lines 这样的命令,这会做错事:它最终会前进两行,而不是一行,从而跳过行。 IOW,请注意 do-lines 的代码无法补偿它调用的特定函数可能执行的操作可能与您的预期不符。相反,它会按照它所说的去做。

(defun do-lines (command &optional start end)
  "Invoke COMMAND on the text of each line from START to END."
  (interactive
   (let* ((key  (read-key-sequence-vector "Hit key sequence: "))
          (cmd  (lookup-key global-map key t)))
     (when (numberp cmd) (error "Not a valid key sequence"))
     (unless (commandp cmd) (error "Key `%s' is not defined" (key-description key)))
     (if (use-region-p)
         (list cmd (region-beginning) (region-end))
       (list cmd (point-min) (point-max)))))
  (setq start  (copy-marker start)
        end    (copy-marker end))
  (save-excursion
    (goto-char start)
    (while (< (point) end)
      (funcall command (buffer-substring (line-beginning-position) (line-end-position)))
      (forward-line 1))))

您应该录制一行宏,然后使用apply-macro-to-region-lines为该区域的所有行执行它。 C-x C-k r

或者,您可以使用多光标在每一行上创建一个光标,然后 C-c C-o 打开所有光标。如果您有机会,multiple-cursors 会随着时间的推移更好地改变您的使用模式。

在某些情况下,您可以使用 Emacs Repeating 使用 C-x z 后跟更多的 `z'。我试图评论区域中的所有行,它非常适合我的用例。

The command C-x z (repeat) provides another way to repeat an Emacs command many times

To repeat the command more than once, type additional z’s: each z repeats the command one more time

本着 TIMTOWTDI[1] 的精神,我将指出一种适用于 某些 情况的技术,包括OP 中的一个。

如果您要 运行 一行 space 分隔的字符串(如 URL)上的外部命令:

  1. Select地区
  2. 调用M-| (Alt+Shift+\, shell-command-on-region)
  3. 使用 xargs 作为所需命令的前缀命令(例如,xdg-open,或 x-www-browser

例如,为第 3 步输入的完整命令可能是:

xargs -n1 xdg-open

-n1 开关导致 xargs 一次用一个参数打开调用给定程序;它将为每个输入 运行 程序一次。如果命令可以一次处理多个参数,则可以省略 -n1。例如,我有一个 web 命令可以打开多个 URL 作为参数,所以只需要 xargs web 就可以了。

这种方法的主要好处是,它适用于任何 POSIX 兼容的东西,而无需提前做任何事情。缺点包括,它仅适用于外部命令,并且需要 xargs(默认情况下不包含在每个 OS 中)。


[1] 实现它的方法不止一种,最初来自 Perl,但在其他地方也很有用。