~/bin/align-tables
我从 emacs stackexchange得到了这个脚本
#!/bin/sh
# -*- mode: shell-script -*-
#
# tangle files with org-mode
#
DIR=`pwd`
FILES=""
# wrap each argument in the code required to call tangle on it
for i in $@; do
FILES="$FILES \"$i\""
done
emacs -Q --batch \
--eval "(progn
(require 'org)(require 'org-table)
(mapc (lambda (file)
(find-file (expand-file-name file \"$DIR\"))
(org-table-map-tables 'org-table-align)
(write-file file nil)
(kill-buffer)) '($FILES)))"
我想将此脚本应用于.org
目录中的每个文件~/foo
。如果我的工作目录是/foo
,那么发行$ align-tables *.org
工作就完美了。但是,如果我在不同的目录中,则$ align-tables ~/foo/*.org
发出错误
Opening output file: no such file or directory, ~/foo/foo/#bar.org#
由于目标目录在错误消息中列出了两次,我假设问题出在脚本中的行DIR='pwd'
,但我不确定如何修改脚本。
我的动机是我正在编写一个适用align-tables
于许多不同目录的 Perl 程序。
有什么想法可以让我的脚本配合吗?
答案1
为了完整起见,您可以纯粹在 shell 中解决此问题:
for i in "$@"; do
pushd "$(dirname "$i")"
DIR=`pwd`
FILES="\"$(basename "$i")\""
emacs -Q --batch \
--eval "(progn
(require 'org)(require 'org-table)
(mapc (lambda (file)
(find-file (expand-file-name file \"$DIR\"))
(org-table-map-tables 'org-table-align)
(write-file file nil)
(kill-buffer)) '($FILES)))"
popd
done
这将更改为每个文件的包含目录,然后emacs
使用相对路径名一次在该单个文件上运行现有命令,然后切换回来。
这是低效的,有很多地方会出错(特别是奇怪的路径名*,在嵌入 elisp 时可能会出现问题),并且 emacs 本身内部肯定会有更好的解决方案,但它会起作用以及您的原始脚本。
* 例如,假设您有一个名为:
"
这将使您的 elisp 脚本出现语法错误,因为引号将被逐字包含并被视为结束字符串文字。如果其他人可以控制存在的文件名,他们就可以构造一个执行任意代码的文件名,但即使没有这些文件名,事情也可能会神秘地破坏。
答案2
\[?*"~
如果当前目录的路径或命令行上传递的任何文件名包含一些特殊字符(空格、 ),则此脚本将以各种方式失败。但对于“驯服”的文件名,它看起来不错(过于复杂但也不错)。
#bar.org#
是一个自动保存文件。当您修改与 关联的缓冲区时,Emacs 会创建它bar.org
,并在您保存该缓冲区时删除它。
我不确定文件名发生了什么,特别是~/foo/foo/
部分。但用虚构的名字很难诊断。如果您需要有关该部分的帮助,请发布包含真实文件名的示例。/tmp
如果您不想泄露您的用户名,请将文件放在下面。
该问题可能与 Org 文件中其他文件的引用有关。如果没有看到您的内容,很难判断。如果您需要帮助,请发布一些实际的文件内容和运行脚本的完整输出。
有一种更简单的方法来操作命令行上传递的所有文件。您首先发布的脚本构建了一些包含FILES
变量中的文件名的 Emacs 代码。但您不需要这样做:只需在emacs
命令行上传递文件名,然后从argv
变量中使用它们即可。打开文件时不需要创建绝对文件名:只需将名称传递给find-file
.
#!/bin/sh
emacs --batch -l org -l org-table --eval "
(mapc (lambda (file)
(find-file (file))
(org-table-map-tables 'org-table-align)
(save-buffer)
(kill-buffer)
argv)
" "$@"