2

entr is used to run shell commands when files change. The following script will output File changed. whenever the file /tmp/data.txt is modified.

#!/usr/bin/env sh

echo "/tmp/data.txt" | entr -s 'echo "File changed."'

Once the commands to execute become more complex it would be convenient to move them to a separate function and make entr call the function, like so:

#!/usr/bin/env sh

foo() { echo "File changed!"; }

echo "$0" | entr -s 'foo' # Error: command not found: foo

Unfortunately, this raises a command not found error, which suggests that my locally defined function is not available to entr. Is there a way to make this work and what's the underlying problem here?

Kind regards

korolev
  • 21
  • 4
  • 1
    `bash` allows you to `export` the function. Any reason apart from neatness that stops you executing a script rather than a function? – icarus Mar 13 '20 at 16:18
  • 1
    To clarify the above, variables (and presumably functions) from a script are not normally defined in the environment of the commands they call. To get a variable or function to persist across the call to the command you need to tag them as inheritable with the export command/keyword. – davolfman Mar 13 '20 at 16:23
  • @icarus Executing a script would require passing many parameters to that script which makes everything less intelligible. Neatness all the way! – korolev Mar 14 '20 at 08:30
  • @davolfman I have attempted to do so using bash's `export -f` which did not change anything. – korolev Mar 14 '20 at 08:37
  • @Slavistan make sure that $SHELL inside the script is bash and not sh. However I expect that if your aim is not to pass lots of parameters either via the command line or the environment then you will be disappointed. Can I introduce you to the concept of a script which writes a script which it then executes? – icarus Mar 14 '20 at 08:54
  • You had a syntax error in your examples. Please set `export SHELL=/bin/bash` before the `entr` command and try again your last example. –  Mar 14 '20 at 09:00
  • What is `entr`? It's not a standard Unix utility as far as I know, and it's not a utility I've seen used before. – Kusalananda Mar 14 '20 at 09:43
  • @Kusalananda `entr` is not a standard Unix utility but is available from the package managers of many linux distributions. See http://eradman.com/entrproject/ for a overview. You'll know within three minutes whether that's a useful utility to you or not. – korolev Mar 15 '20 at 07:50
  • @mosvy That did the trick indeed. Would you care to create a proper answer? Thank you. – korolev Mar 15 '20 at 08:01
  • Please put your conclusions in a separate "self-"answer, so it could be appraised separately from the question. In this case, your assertion that exporting variables "gets messy very quickly" is quite dubious, as all bourne shells support `set -a` (long way `set -o allexport`) since like forever. –  Mar 16 '20 at 11:38

1 Answers1

0

You should export SHELL=/bin/bash before invoking env -s cmd if you want to be able to use bash exported functions inside cmd.

As documented in the manpage of entr (emphasis mine):

-s Evaluate the first argument using the interpreter specified by the SHELL environment variable.

Not necessarily /bin/bash, the shell it was called from, or the login shell of the user.

If SHELL is not defined (it doesn't have to, in a script, etc), then entr will set it itself to /bin/sh, which on Debian is dash, a shell which cannot use the functions exported by bash and, moreover, will remove them from the environment.

  • This works for self-contained functions but fails if variables defined in the script need to be accesses. I've updated the question. – korolev Mar 15 '20 at 16:25
  • You should export the variables too: `export x`. How could they work otherwise? The shell doesn't support any kind of closures, especially NOT through exported functions ;-) –  Mar 15 '20 at 16:28
  • 2
    If you wonder how exporting functions work in bash, it's by creating some environment variables which contain the _source code_ of the function, which will be _parsed_ by the inner bash. Thence ShellShock and all the fun. –  Mar 15 '20 at 16:30
  • @Slavistan: I think I already know your next __Q:__ Why does `export x=1; echo "$0" | entr -s 'x=2; echo set x to 2'; echo "$x"` NOT set `x` to `2`? __A:__ because Unix is not plan9, LOL ;-) –  Mar 15 '20 at 17:06
  • That would make a good interview question :p Thanks for the lead! If I got everything right my problem is caused by the fact that [functions are propagated to subshells but they cannot be propagated to independent shell processes](https://unix.stackexchange.com/questions/65751/how-to-get-functions-propagated-to-subshell) which is what `entr -s` is doing. I'll probably generate a script and execute that with entr =) Oh, and .. plan9? I will have to get to know that at some point in time. [cat-v.org](http://www.cat-v.org) had me curious already! – korolev Mar 16 '20 at 07:56