Home > Blockchain >  create a variable that is in sync with a files content in bash
create a variable that is in sync with a files content in bash

Time:05-24

In bash i'd like to read in a variable that is constantly in sync with a files contents.

how can I get this done?

echo "test" > /tmp/test
value=$(</tmp/test)

echo $value
test

echo "test2" > /tmp/test
echo $value
test    # Id 'like this to return test2

CodePudding user response:

As @Fravadona comments, not possible.

Using a function is a workaround:

value() { cat /tmp/test; }

echo "test" > /tmp/test
echo $(value)   # => test

echo "test2" > /tmp/test
echo $(value)   # => test2

CodePudding user response:

There is a way to do something like what you want. The basic idea is to run a background process to monitor the file for changes and send signals to your program when they occur. A signal handler in your program can update the variable value when it receives a signal.

This Shellcheck-clean code demonstrates the idea:

#! /bin/bash -p

# Turn on monitor mode (job control).
# (This causes background processes to run in separate process groups, making
# it easy to kill a background process and all processes created by it.)
set -o monitor

# Send a SIGUSR1 signal to the given PID ($1) whenever the contents of the
# given file ($2) change
function send_sigusr1_on_file_change
{
    local -r pid=$1
    local -r file=$2

    inotifywait -m -q -e modify -- "$file" \
        |   while read -r _; do
                kill -USR1 "$pid"
            done

    return 0
}

# Initialize the test file and the 'value' variable to empty
: >/tmp/test
value=''

# Update 'value' with the contents of '/tmp/test' whenever a
# SIGUSR1 signal is received.
trap 'value=$(< /tmp/test)' SIGUSR1

# Run a background process to scan for changes in '/tmp/test' and send
# SIGUSR1 signals to this process ($$) when they occur.
send_sigusr1_on_file_change "$$" /tmp/test &
signaller_pid=$!

# Set a trap to kill the background process when this program exits.
# (Negating the PID causes all processes in the process group associated with
# the background process to be killed.  This is to prevent subprocesses
# created by the background process (e.g. 'inotifywait') from continuing to
# run after it is killed.)
trap 'kill -- "-$signaller_pid"' EXIT

# Wait for the signaller loop to start signalling changes
sleep 1

echo 'test' >/tmp/test
sleep 0.01  # Wait for the change to the file to be detected and signalled

printf '%s\n' "$value"

echo 'test2' >/tmp/test
sleep 0.01  # Wait for the change to the file to be detected and signalled

printf '%s\n' "$value"

On my Linux test system the program consistently outputs:

test
test2
  • Because there is a small delay between the file changing and the variable being updated I've had to add calls to sleep to make it work reliably. That may be a problem for you.
  • I've used inotifywait to efficiently scan for changes in the file. You might need to use an alternative depending on what is available, or can be installed, on your systems. You could also use a polling loop, but that would be more resource-hungry, and probably lead to longer delays in updating the variable.
  • See the accepted, and excellent, answer to Why is printf better than echo? for an explanation of why I used printf instead of echo for printing the variable value. (echo is OK for printing (some) fixed strings.)
  •  Tags:  
  • bash
  • Related