Skip to content

Instantly share code, notes, and snippets.

@hmason
Created August 21, 2012 22:01
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hmason/3419831 to your computer and use it in GitHub Desktop.
Save hmason/3419831 to your computer and use it in GitHub Desktop.
Did you know that bash will reload a script *while it is executing*?!
#!/bin/bash
function addnext {
NUM=$1
sleep 1
echo HI $NUM
NUM=$(expr $NUM + 1)
echo addnext $NUM >> $0
}
addnext 1
@RichardBronosky
Copy link

Not to split hairs here, but because the file lacks an EOL, this does not work until you edit the file and add a \n to it.

curl -LO https://gist.githubusercontent.com/hmason/3419831/raw/test.sh; chmod +x ./test.sh; ./test.sh

In other words...

$ curl -LO https://gist.githubusercontent.com/hmason/3419831/raw/test.sh; chmod +x test.sh; ./test.sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   122  100   122    0     0    346      0 --:--:-- --:--:-- --:--:--   346
HI 1

$ cat test.sh
#!/bin/bash

function addnext {
	NUM=$1
	sleep 1
	echo HI $NUM
	NUM=$(expr $NUM + 1)
	echo addnext $NUM >> $0
}

addnext 1addnext 2

$ curl -LO https://gist.githubusercontent.com/hmason/3419831/raw/test.sh; chmod +x test.sh; echo >> test.sh; ./test.sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   122  100   122    0     0    321      0 --:--:-- --:--:-- --:--:--   321
HI 1
HI 2
HI 3
HI 4
HI 5
HI 6
HI 7
HI 8
HI 9
HI 10
...

@ngrigoriev
Copy link

ngrigoriev commented May 16, 2018

A simple strace shows that it is not reloading anything.

script:

#!/bin/bash

num=0

while [[ $num -lt 20 ]] ; do
        echo "waiting...$num"
        sleep 1s
        num=$(( $num + 1 ))
done


echo "I am done!"
open("./run.sh", O_RDONLY)              = 3
ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7ffda02d2010) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR)                   = 0
read(3, "#!/bin/bash\n\nnum=0\n\nwhile [[ $nu"..., 80) = 80
lseek(3, 0, SEEK_SET)                   = 0
getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=4*1024}) = 0
fcntl(255, F_GETFD)                     = -1 EBADF (Bad file descriptor)
dup2(3, 255)                            = 255
close(3)                                = 0
fcntl(255, F_SETFD, FD_CLOEXEC)         = 0
fcntl(255, F_GETFL)                     = 0x8000 (flags O_RDONLY|O_LARGEFILE)
fstat(255, {st_mode=S_IFREG|0775, st_size=128, ...}) = 0
lseek(255, 0, SEEK_CUR)                 = 0
...
read(255, "#!/bin/bash\n\nnum=0\n\nwhile [[ $nu"..., 128) = 128
...
lseek(255, -20, SEEK_CUR)               = 108
...
read(255, "\n\necho \"I am done!\"\n", 128) = 20
read(255, "", 128)                      = 0
exit_group(0)                           = ?

I did not check bash source code but I would imagine it would never read the entire script in memory. It should read it by chunks as it executes it. I am not sure why it does that lseek() but probably just to go to the beginning of the last line and continue reading from there. Just a guess. So, the behaviour of the script "changing" one the fly would depend on how the filesystem react to the concurrent reading and writing.

I may be wrong, this is just my guess based on what I would expect and what I see in strace on Linux....

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment