I have a board that is running a "patch" script. The patch script always runs in the background and it is a shell script that runs the following pseudo code:

while true; do
    # checks if a patch tar file exists and if yes then do patching
    sleep 10

This script is at /opt/patch.sh and it started by SystemV init script.

The problems is that when the script finds the tar, it extracts it, and inside there is a shell script called patch.sh which is specific for the contents of the tar.

When the script at /opt/patch.sh finds the tar it does the following:

tar -xf /opt/update.tar -C /mnt/update
mv /mnt/update/patch.sh /opt/patch.sh
exec /opt/patch.sh

It replaces itself with the another script and executes it from the same location. Can any problems occur doing that?

3 Answers 3


If the file is replaced by being written over in-place (inode stays the same), any processes having it open would see the new data if/when they read from the file. If it's replaced by unlinking the old file and creating a new one with the same name, the inode number changes, and any processes holding the file open would still have the old file.

mv might do either, depending on if the move happens between filesystems or not... To make sure you get a completely new file, unlink or rename the original first. Something like this:

mv /opt/patch.sh /opt/patch.sh.old     # or rm
mv /mnt/update/patch.sh /opt/patch.sh

That way, the running shell would still have a file handle to the old data, even after the move.

That said, as far as I've tested, Bash reads the whole loop before executing any of it, so any changes to the underlying file would not take change the running script as long as the execution stays within the loop. (It has to read the whole loop before executing it, since there may be redirections affecting the whole loop at the end.)

After exiting the loop, Bash moves the read pointer back to and then resumes reading the input file from the position right after the loop ended.

Any functions defined in the script are also loaded to memory, so putting the main logic of the script to a function, and only calling it at the end would make the script quite safe against modifications to the file:

main() {

Anyway, it's not too hard to test what happens when a script is overwritten:

$ cat > old.sh <<'EOF'
for i in 1 2 3 4 ; do
        # rm old.sh
        cat new.sh > old.sh 
        sleep 1
        echo $i
echo will this be reached?
$ cat > new.sh <<'EOF'
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
$ bash old.sh

With the rm old.sh commented out, the script will be changed in-place. Without the comment, a new file will be created. (This example partly relies on new.sh being larger than old.sh, as if it were shorter, the shell's read position would be past the end of the new script after the loop.)


I have had this issue before and can confirm this can be a problem. In my case, a regression script first executes a git pull and might get updated after it starts to run, causing issues.

The problem is usually when the shell will go back and checks if there are any more lines to interpret. This can cause error even when the desired code is within a loop. To avoid this, use the structure in this post.


A self-executing, self-modifying script? That's not a good idea.

A better solution would be to create a stub daemon with minimal functionality (i.e. responsible for installing new versions of the slave script and invoking it at intervals). Something like ... (not tested)

while true; do
  # check if a patch tar file exists and if yes then do patching
  if [ -f "$PATCH" ]; then
      ( cd /usr/local/mydaemon \
      && tar -xzf "$PATCH" \
      && rm -f "$PATCH" ) \
      || exit -1
  sleep 10

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .