1

I am writing several .gv files (graphviz) in the same directory and want to create a png file with neato immediately when I have saved one of them. I am on macOS 10.12.6, with zsh as my default shell, and I have installed entr from https://github.com/eradman/entr to monitor file changes. I have tried following command without any luck

$ 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

Following works

  1. Simple printing of last modified file

     $ ls *.gv | entr echo /_
     # Press <Space> to manually trigger event to see last changed file
     /Users/hotschke/complete.gv
    
  2. Using the same output name for all files:

     $ ls *.gv | entr neato -Tpng /_ -oConstantname.png
    
  3. Replace .gv with .png

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

Note the special argument /_ of entr

The special /_ argument (somewhat analogous to $_ in Perl) provides a quick way to refer to the first file that changed. When a single file is listed this is a handy way to avoid typing a pathname twice:

It would be great to have several answers using different tools (e.g. watchman, watchdog, fswatch, entr, launchd (mac-only); see also the discussion https://stackoverflow.com/q/1515730/)

Hotschke
  • 275
  • 2
  • 11

4 Answers4

2

Call a script with entr

tl;dr: pass /_ as an argument to a script to Do The Thing™

Rather than trying to futz with special arguments and variables, pass /_ to a simple script (which itself can be a one liner) to do your png generation:

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

with update.sh along the lines of:

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

I tested the above approach with with a simple script to use imagemagick to convert an image, which worked without quotes, but it's better to leave them in in case of spaces etc in filenames.

As a side note, I tend to use an update script with entr anyway as it keeps things clearer to my mind. For example, I use entr to watch LaTeX source files and generate an updated PDF, the update script in that case runs xelatex, biber and also refreshes the PDF viewer (pkill -HUP mupdf).

bertieb
  • 7,543
1

Using fswatch instead of entr

Thanks to @Attie for pointing fswatch out. Following works for me:

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

fswatch considers by default all files: you need to use filters to limit to a certain file type (see https://stackoverflow.com/q/34713278/).

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

Hotschke
  • 275
  • 2
  • 11
1

Using a 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)

Now you can use

$ ls *.gv | entr make

There is room for improvement: there a two commands to list files with the extension .gv which contradicts a single source of truth (SSOT).

Vim Notes

You can also use the Makefile inside the text editor vim

:mak[e]

You can automatically call make on save by setting up an autocommand with

:au BufWritePost <buffer> make

You might consider to use a plugin for runnning make async: see the plugins vim-dispatch (normal mode mapping m<CR>), neomake, or asyncrun.vim.

However, there are already compiler definitions for the commands dot and neato:

This means you do not need to write a Makefile. You can set the makeprg by :comp[iler] neato or :comp dot. Note that you see all compiler definitions by :comp <C-d> and you can tab-complete :comp n<Tab> to :comp neato and :comp d<tab> to :comp dot.

Now you have to call make with an argument to specifiy your output format:

:make png
:au BufWritePost <buffer> make png

If you use vim-dispatch, this looks like

:Make png
m<Space>png<CR>
:au BufWritePost <buffer> Make png
Hotschke
  • 275
  • 2
  • 11
0

Entr – Pass /_ as an argument to a shell

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

This answer is from https://bitbucket.org/eradman/entr/issues/75/run-command-on-single-file-when-changed#comment-44183153.

This command silently executes the command neato. If you want to have feedback on the command line when entr detects a file change, add -x to bash to see the executed command:

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