3

I got this script ~/bin/align-tables from 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)))"

I want to apply this script to every .org file in the directory ~/foo. If my working directory is /foo, then issuing $ align-tables *.org works perfectly. However, if I'm in a different directory, issuing $ align-tables ~/foo/*.org gives the error

Opening output file: no such file or directory, ~/foo/foo/#bar.org#

Since the target directory is listed twice in the error message, I'm assuming the problem is the line DIR='pwd' in the script but I'm not sure how to go about modifying the script.

My motivation is that I'm writing a perl program that applies align-tables to many different directories.

Any ideas how I can get my script to cooperate?

  • I'm not sure whether this isn't really an elisp question, rather than a Bash question (though it certainly isn't perl, like the tag says) – Michael Homer Sep 17 '15 at 7:53
  • @MichaelHomer I tagged perl because I ultimately want to use this from a perl script... but maybe that doesn't merit a tag so I'll remove it. – Brian Fitzpatrick Sep 17 '15 at 7:54
  • @MichaelHomer Would this be more on-topic at emacs stackexchange? – Brian Fitzpatrick Sep 17 '15 at 7:58
  • That's what I was wondering. I suspect that answering the question well depends on elisp knowledge primarily. "I want to run command X on absolute paths from a shell script, but it requires relative paths" is on-topic here, though, so you don't have to move it. – Michael Homer Sep 17 '15 at 8:06
  • @MichaelHomer Is cross-posting considered bad form? If so, I think I'll leave it here for a day or so and move it if no answers crop up. – Brian Fitzpatrick Sep 17 '15 at 8:15
2

For completeness, you can work around this issue purely in the 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

This changes to the containing directory of each file, then runs your existing emacs command on that single file at a time with a relative path name, then switches back.

This is inefficient, there's a whole bunch of places where it will go wrong (weird pathnames*, in particular, could be a problem when embedded in elisp), and there'll certainly be a better solution inside emacs itself, but it will work as well as your original script.


* For example, suppose you have a file called:

"

That will make your elisp script into a syntax error, because the quotation mark will be included verbatim and treated as closing the string literal. If someone else can control the filenames that exist, they could construct one that executed arbitrary code, but even without that things may break mysteriously.

  • Out of curiosity, what would be a "weird" pathname? – Brian Fitzpatrick Sep 17 '15 at 8:18
  • Suppose that a filename contains ", or something that's illegal in an elisp string literal. That just gets plugged in to the source string, and then it'll be a syntax error for emacs. – Michael Homer Sep 17 '15 at 8:22
0

This script will fail in all kinds of ways if the path to the current directory or any file name passed on the command line contains some special characters (whitespace, \[?*"~). But with “tame” file names, it looks fine (overcomplicated but fine).

#bar.org# is an autosave file. Emacs creates it when you modify a buffer that's associated with bar.org, and removes it when you save that buffer.

I'm not sure what is going on with the file names, in particular the ~/foo/foo/ part. But it's hard to diagnose with made-up name. If you need help about that part, post an example with genuine file names. Put the files under /tmp if you don't want to reveal your username.

The problem may be related to having references to other files inside the Org files. It's hard to tell without seeing your content. Post some actual file content and the full output of running the script if you need help with that.

There's a much simpler way to operate over all the files passed on the command line. The script you posted first builds some Emacs code containing the file names in the FILES variable. But you don't need to do that: simply pass the file names on the emacs command line, and use them from the argv variable. And you don't need to make an absolute file name when you open the files: just pass the name to 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)
" "$@"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.