Executar o comando em um único arquivo quando alterado com alguma mágica de shell (por exemplo, substituir extensão)

1

Eu estou escrevendo vários .gv arquivos ( graphviz ) no mesmo diretório e deseja criar um arquivo png com neato imediatamente quando salvei um deles. Eu estou no macOS 10.12.6, com zsh como meu shell padrão e eu instalei entr de http://entrproject.org/ para monitorar alterações de arquivos. Eu tentei seguir o comando sem qualquer sorte

$ echo $0
-zsh
$ ls *.gv | entr 'F="/_";neato -Tpng "$F" -o"${F/%\.gv/.png}"'
entr: exec F="/_";neato -Tpng "$F" -o"${F/%\.gv/.png}": No such file or directory

Trabalhos seguintes

  1. Impressão simples do último arquivo modificado

    $ ls *.gv | entr echo /_
    # Press <Space> to manually trigger event to see last changed file
    /Users/hotschke/complete.gv
    
  2. Usando o mesmo nome de saída para todos os arquivos:

    $ ls *.gv | entr neato -Tpng /_ -oConstantname.png
    
  3. Substitua .gv por .png

    $ F="/Users/hotschke/complete.gv";neato -Tpng "$F" -o"${F/%\.gv/.png}"
    

Observe o argumento especial /_ do entr

O especial /_ argumento (algo análogo a $_ em Perl) fornece uma maneira rápida de se referir ao primeiro arquivo que foi alterado. Quando um único arquivo é listado, essa é uma maneira prática de evitar digitar um nome de caminho duas vezes:

Seria ótimo ter várias respostas usando diferentes ferramentas (por exemplo, vigia, watchdog, fswatch, entr, launchd (somente para mac); veja também a discussão https://stackoverflow.com/q/1515730/ )

Hotschke
fonte
Por que não usar inotifywait -m, que produz nós tocados, em vez de entr que parece consumir uma lista de nós para monitorar. Você pode então usar sed (ou similar) para produzir um comando para executar.
Attie
Não me importo de usar inotifywait. No entanto, tanto quanto eu posso ver, não existe no macOS.
Hotschke
Eu preferiria uma solução multi-plataforma, mas usar apenas utilitários internos é tão bom quanto isso.
Hotschke
Você usou o inotify tag ...
Attie
Attie: Desculpe pela tag enganosa! Eu não encontrei um bom.
Hotschke

Respostas:

2

Chame um script com entr

tl; dr : passe /_ como um argumento para um script para fazer a coisa

Ao invés de tentar futz com argumentos e variáveis ​​especiais, passe /_ para um script simples (que pode ser um liner) para fazer sua geração de PNG:

$  ls *.gv | entr ./update.sh /_

com update.sh ao longo das linhas de:

neato -Tpng "$1" -o"${1/%\.gv/.png}"

Eu testei a abordagem acima com um script simples para usar imagemagick para convert uma imagem, que funcionou sem aspas, mas é melhor deixá-las no caso de espaços, etc. em nomes de arquivos.

Como uma nota lateral, eu costumo usar um script de atualização com entr de qualquer maneira, pois mantém as coisas mais claras para minha mente. Por exemplo, eu uso entr Para assistir aos arquivos fonte LaTeX e gerar um PDF atualizado, o script de atualização nesse caso é executado xelatex, biber e também atualiza o visualizador de PDF ( pkill -HUP mupdf ).

bertieb
fonte
Obrigado! Neste caso, prefiro evitar criar um script. Se eu criasse um arquivo de suporte para criação, eu escreveria um Makefile e usaria o fluxo de trabalho sugerido $ ls *.gv | entr make. O Makefile funcionaria sem entr ou similar e os editores de texto geralmente suportam chamar um makefile (por exemplo, vim). Eu uso vim e poderia ativar um autocommand para o evento de salvar :au BufWritePost <buffer> make. Se eu quiser que o make seja executado em segundo plano, posso usar vim-dispatch, asyncrun.vim, neomake, vim-accio ou um dos muitos outros plugins disponíveis.
Hotschke
BTW, você sabe latexmk ? Faz parte do TexLive. Oferece a opção $ latexmk -pvc que faz entr redundante e é muito inteligente em relação a chamar os programas xelatex, biber e outros tão frequentemente quanto necessário e também atualiza o visualizador.
Hotschke
@Hotschke ponto justo sobre makefiles (e boa resposta); Eu não venho de um background de programação, então seria mais fácil para mim aprender a sintaxe deles - na lista TODO - e então escrever um liner de um para três, dependendo do contexto; embora eu admita que parece um pouco unixy não usar a ferramenta certa para o trabalho.
bertieb
A sintaxe IMHO Makefile não é realmente intuitiva. Levei um tempo para aprender e ainda tenho que procurar o significado de $<, $+ e similar. Eu não o uso com freqüência suficiente e também prefiro o latexmk para latex e cmake para c / c ++.
Hotschke
1

Usando um Makefile

SOURCES:=$(shell find . -maxdepth 1 -name '*.gv')

ifndef OTYPE
    OTYPE:=png
endif
# use `make OTYPE=pdf` to generate pdfs

TARGETS:=$(SOURCES:.gv=.$(OTYPE))

.PHONY: all clean

all: $(TARGETS)

clean:
    rm $(TARGETS)

%.$(OTYPE) : %.gv
    neato -T$(OTYPE) $< -o$(basename $<).$(OTYPE)

Agora você pode usar

$ ls *.gv | entr make

Há espaço para melhorias: há dois comandos para listar arquivos com a extensão .gv que contradiz um única fonte de verdade (SSOT).

Notas Vim

Você também pode usar o Makefile dentro do editor de texto vim

:mak[e]

Você pode ligar automaticamente para salvar, configurando um autocommand com

:au BufWritePost <buffer> make

Você pode considerar usar um plugin para executar make async: veja os plugins despacho de vim (mapeamento de modo normal m<CR> ) neomake ou asyncrun.vim .

No entanto, já existem definições de compilador para os comandos dot e neato:

Isso significa que você não precisa escrever um Makefile. Você pode definir o makeprg :comp[iler] neato ou :comp dot. Note que você vê todas as definições do compilador :comp <C-d> e você pode tab-completar :comp n<Tab> para :comp neato e :comp d<tab> para :comp dot.

Agora você precisa chamar make com um argumento para especificar seu formato de saída:

:make png
:au BufWritePost <buffer> make png

Se você usa o vim-dispatch, isso parece

:Make png
m<Space>png<CR>
:au BufWritePost <buffer> Make png
Hotschke
fonte
1

Usando fswatch ao invés de entr

Graças a @Attie por apontar fswatch Fora. Seguintes obras para mim:

 $ fswatch -e ".*" -i "$PWD/[^.]*\\.gv$" -0 $PWD |
     xargs -0 -n 1 -I {} sh -c 'F={}; neato -Tpng "$F" -o"${F/%\.gv/.png}"'

O fswatch considera por padrão todos os arquivos: você precisa usar filtros para limitar a um determinado tipo de arquivo (Vejo https://stackoverflow.com/q/34713278/ ).

https://emcrisostomo.github.io/fswatch/ por Enrico M. Crisostomo (2013-2017)

Hotschke
fonte
0

Entr - Passe /_ como um argumento para um shell

ls *.gv | entr bash -c 'neato -Tpng "$0" -o"${0/%\.gv/.png}"' /_

Esta resposta é de https://bitbucket.org/eradman/entr/issues/75/run-command-on-single-file-when-changed#comment-44183153 .

Este comando executa silenciosamente o comando neato. Se você quiser ter feedback sobre a linha de comando quando entr detecta uma alteração de arquivo, adicione -x para bater para ver o comando executado:

ls *.gv | entr bash -x -c 'neato -Tpng "$0" -o"${0/%\.gv/.png}"' /_
Hotschke
fonte