On Unix-like operating systems, removing a file is possible if it is still open. The file name will no longer exist, but the data itself does remain, and any programs that have an open handle, including bash, can continue to read (and even write) using that handle. Only when the last program closes its handle does the file really get deleted.
So, in a sense, yes, a shell script can delete itself, but it won't really be deleted until after the execution of that script finishes.
Note that you get different results if you overwrite the file instead of deleting it. Shells may (and do) use buffering to avoid reading one byte at a time, but the buffer has a finite size. If the file contents change beyond what the shell has already read into memory, it will see the new content. You can create a simple example of this if you examine what the buffer size for your system is. On my system, it happens to be 8 KiB. So a shell script that contains
echo line 1
>test.sh
# (8167 random characters)
echo line 4
so the second block of data is "cho line 4
", on my system, outputs
$ bash test.sh
line 1
test.sh: line 4: e: command not found
because the e
has already been read, and the shell encountered EOF when trying to read the rest of the line.
Update: rici shows that with a different example of overwriting the file, the newly written contents do get used. Reworking that:
script="`tr 12 21 <test.sh`"
env echo "$script" >test.sh
echo toggle: 1
The second line intentionally doesn't use bash's built-in echo
command, because that sometimes doesn't cause bash to re-read the script, per the comments, but precisely when it does and doesn't is unclear to me. bash outputs the newly written toggle value when it gets to line three. The relevant difference between this and my earlier example seems to be that here, bash can seek to the current position when it gets to line three, whereas in my earlier example, that is impossible, because that position no longer exists, as the file is empty.