3

For learning purposes, I wish to change a non-stop script like this:

#!/bin/env bash

while true; do
        sleep 3

        echo 1 >> /tmp/log
done

I know that Linux will load the recent file into memory so I'm guessing that is the reason why I can't simply change the echo 1 >> /tmp/log to echo 2 >> /tmp/log and save the script to see the result.

So I change the script :

#!/bin/env bash

CMD="$0 $@"
handleSigHup() {
        echo "Receive single SIGHUP, reloading..."
        exec $CMD
}

trap 'handleSigHup' SIGHUP

while true; do
        sleep 3

        echo 2 >> /tmp/log
done

Now I can use kill -HUP to see the /tmp/log output:

1
1
1
2
2
2
2...

Here are my questions:

  1. Is my guessing right? If so, can I change some sort of system vars to turn off the buffering and make It work?

  2. My code above will only work once, when I change the script to echo 3 >> /tmp/log and kill -HUP again, It won't output the 3 as expect, WHY ?

Thanks all!

agc
  • 7,002
  • 2
  • 25
  • 44
Micr
  • 131
  • 8
  • `CMD="$0 $@"` loses a *lot* of data. If your arguments have quotes, escaped globs, etc., you're in for a bad time; see also [BashFAQ #50](http://mywiki.wooledge.org/BashFAQ/050). – Charles Duffy Mar 09 '18 at 13:57
  • 1
    `cmd=( "$0" "$@" )`, followed by `exec "${cmd[@]}"`, would be somewhat less awful, though see also [BashFAQ #28](https://mywiki.wooledge.org/BashFAQ/028) re: the (un)reliability of `$0`. – Charles Duffy Mar 09 '18 at 14:05
  • Anyhow, on the topic -- see [Chet Ramey's chapter about bash in The Architecture of Open Source Applications](http://aosabook.org/en/bash.html). You'll see that there's a lexing-and-parsing layer between reading the input and the runtime expansion/execution loop. The output of that parsing layer? Well, there's nowhere else it *could* be stored except for memory. – Charles Duffy Mar 09 '18 at 14:10

1 Answers1

1

You can't change nor reload a running script (unless you exec $0). What I believe you should do is use two scripts (or binaries):

  • One script which actually does the work, which may get rewritten
  • Another script which runs every fixed period of time, and executes the second script

This way, the outer, launcher script will exeucte the newest version of the script, not something it has cached.

H-O-W-E-V-E-R...

You can do something else entirely. On most systems there's a daemon/service named cron, which executes commands by a fixed schedule set in configuration files. You could probably drop the second script, and get cron to run the first (potentially changing) script once every so many seconds.

This is not entirely trivial since cron's resolution is typically minutes, but have a look at this question:

How to get a unix script to run every 15 seconds?

einpoklum
  • 86,754
  • 39
  • 223
  • 453
  • Thanks for answer, I do know there are other ways around, but as I said, this is a learning sample, I really want to know why my way is not working. – Micr Mar 09 '18 at 10:29
  • To say that "the launcher script" may otherwise execute "something it has cached" is potentially misleading -- no OS-level caching is relevant here; the *only* "caching" at hand is (1) the interpreter having a file descriptor on a particular inode (thus, *not* seeing a new inode written-and-replaced over it with a new version of the script without an explicit reopen of the file), and (2) the interpreter parsing code it read into structures that live in memory (which it would be hard/impossible to implement a language feature as simple as a loop without). – Charles Duffy Mar 09 '18 at 14:02
  • Even then, parsing code into in-memory structures isn't something I would consider it fair to describe as "caching" -- a cache is something kept around for performance; operating off an AST isn't a performance enhancement, but part of *the conventional way interpreted languages are built*. – Charles Duffy Mar 09 '18 at 14:04
  • (granted, bash's AST is much, *much* simpler than most -- being at its core a list of words with flags associated -- but the concept still is pertinent; you can't execute code without parsing it first, and the parsed result can only be executed if it's stored in memory). – Charles Duffy Mar 09 '18 at 14:11