14
#beginning of bashscript1.sh
source bashscript2.sh
echo $variable

and here is the source file:

#beginning of bashscript2.sh
rm -f bashscript2.sh
variable = "integer of some kind"

How will the bash script be handled? Does it first load the source file and then deletes it, or will the variable have a value in bash script 1?

Mogsdad
  • 42,835
  • 20
  • 145
  • 262
Paolo
  • 1,949
  • 4
  • 22
  • 27

2 Answers2

27

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.

  • 1
    Are you sure? :) Try putting these two lines into `echo.sh`, adding whatever third line you want: `echo line 1` `echo echo overwrite | dd of=echo.sh obs=1 seek=$(head -n2 echo.sh | wc -c)` – rici Dec 21 '13 at 20:15
  • @rici Huh, interesting. It seems to be a little bit of both: bash does buffer as I described, but *tries* to re-read the file (presumably precisely to support this) when it gets to the next line. In your example, that works. In mine, that doesn't (because the position it would seek to is no longer valid). Will edit. Perhaps noteworthy: dash doesn't print "overwrite" with your example. –  Dec 21 '13 at 20:33
  • hvd: On my system (bash 4.2.25 on Ubuntu 12.04) @rici's example works, but the example from your update does not - the toggle's new value is NOT reported. Where do you see your example work? – mklement0 Jan 13 '14 at 17:52
  • @mklement0 Previous comment removed, I misread. I'm using bash 4.2.25 on a fairly standard x86-64 Linux system where I now get the same behaviour as you. It's the same system that previously did behave as I described in my answer. Will check what's going on. –  Jan 13 '14 at 18:08
  • @mklement0 Edited my answer, does this work on your system too? –  Jan 13 '14 at 18:35
  • @hvd: Yes, it does - and also on bash 3.2.51 (OS X 10.9.1); it does appear to be tied to whether a builtin is used or not (the `printf` builtin/executable pair exhibits the same difference in behavior). http://stackoverflow.com/a/21100710/45375 came up with an interesting technique for categorically preventing the re-reading: enclose the entire script in `{ ...; exit; }` – mklement0 Jan 13 '14 at 23:54
  • @hvd Sorry to comment on an old answer but I just wanted to clarify something. Firstly, if I understand you correctly then deleting the current shell script is safe but modifying it is not. So if I wanted to replace the existing file, should it be safe to delete the existing file then re-create it with the same name? Secondly, is this in any way shell or file system dependant? I'm working on an embedded Linux system using Sash on a JFFS2 flash file system. Thanks. – Sonic Atom Jan 06 '16 at 13:44
  • @SonicAtom I would expect it (removing and recreating) to work on any sane shell on any sane file system, but that's no guarantee. It's something that should only take a few moments to check though, and then you can tell for sure. –  Jan 06 '16 at 13:48
4

Is this a curiosity you have or what you actually want to obtain?

Anyway, I've tried myself this simple script (test_script.sh):

rm -f test_script.sh
echo "Still here!"

and it prints Still here!, so the file was loaded before execution. Execution is not possible the second time.

To answer your question, yes and no. The commands in the script are loaded and executed, and can delete the source file of the script. This will make impossible to execute the same file again, of course, but will not affect the first execution.

elnigno
  • 1,661
  • 14
  • 35
  • It's both. I'm curious on how it behaves and why. Also, I'm thinking of doing an ugly hack for which I need a source file to disappear while my initial script still has all the variables while still running. – Paolo Dec 21 '13 at 18:25
  • It seems to me the code you posted does pretty much that, am I wrong? – elnigno Dec 21 '13 at 18:31