Por que não posso aplicar esse script a arquivos que não estão no meu diretório atual?

3

Eu recebi este script ~/bin/align-tables do 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)))"

Eu quero aplicar esse script a todos os arquivos .org no diretório ~/foo . Se meu diretório de trabalho for /foo , a emissão de $ align-tables *.org funcionará perfeitamente. No entanto, se eu estiver em um diretório diferente, a emissão de $ align-tables ~/foo/*.org fornecerá o erro

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

Como o diretório de destino está listado duas vezes na mensagem de erro, estou assumindo que o problema é a linha DIR='pwd' no script, mas não sei como modificar o script.

Minha motivação é que estou escrevendo um programa perl que aplica align-tables a muitos diretórios diferentes.

Alguma idéia de como posso fazer com que meu script coopere?

    
por Brian Fitzpatrick 17.09.2015 / 09:45

2 respostas

2

Para completar, você pode contornar esse problema exclusivamente no 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

Isso muda para o diretório contido de cada arquivo e, em seguida, executa o comando emacs existente nesse único arquivo por vez com um nome de caminho relativo e, em seguida, retorna.

Isso é ineficiente, há um monte de lugares onde isso vai dar errado (caminhos estranhos *, em particular, podem ser um problema quando embutidos no elisp), e certamente haverá uma solução melhor dentro do próprio emacs, mas funcionará tão bem quanto o seu roteiro original.

* Por exemplo, suponha que você tenha um arquivo chamado:

"

Isso fará com que seu script elisp seja um erro de sintaxe, porque as aspas serão incluídas textualmente e tratadas como fechando o literal da string. Se alguém pode controlar os nomes de arquivos que existem, eles poderiam construir um que executasse código arbitrário, mas mesmo sem isso as coisas podem se romper misteriosamente.

    
por 17.09.2015 / 10:15
0

Esse script falhará de todas as formas, se o caminho para o diretório atual ou qualquer nome de arquivo transmitido na linha de comando contiver alguns caracteres especiais (espaço em branco, \[?*"~ ). Mas com nomes de arquivo "domesticados", parece bom (supercomplicado, mas bem).

#bar.org# é um arquivo de salvamento automático. O Emacs o cria quando você modifica um buffer associado a bar.org e o remove quando você salva esse buffer.

Não sei o que está acontecendo com os nomes dos arquivos, em particular a ~/foo/foo/ part. Mas é difícil diagnosticar com nome inventado. Se você precisar de ajuda sobre essa parte, poste um exemplo com nomes de arquivo genuínos. Coloque os arquivos em /tmp se você não quiser revelar seu nome de usuário.

O problema pode estar relacionado a ter referências a outros arquivos dentro dos arquivos Org. É difícil dizer sem ver seu conteúdo. Poste algum conteúdo de arquivo real e a saída completa da execução do script, se precisar de ajuda com isso.

Existe uma maneira muito mais simples de operar sobre todos os arquivos passados na linha de comando. O script que você postou primeiro constrói algum código do Emacs contendo os nomes dos arquivos na variável FILES . Mas você não precisa fazer isso: basta passar os nomes dos arquivos na linha de comando emacs e usá-los na variável argv . E você não precisa criar um nome de arquivo absoluto ao abrir os arquivos: basta passar o nome para 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)
" "$@"
    
por 18.09.2015 / 03:53