I have a script that downloads and synchronises a project from a web service (where I co-edit this project) to my local machine, where I have the project's Git repo.1
Bash does not read a whole script file in at once, rather reading it in chunks as needed for interpretation. Therefore, it is possible for the file to be modified2 while the script is running. This is seldom useful, unless you want to be really esoteric!
My particular project is downloaded as a zipfile and then unzipped. I do not know whether unzip updates existing files in place (leading to the problem) or deletes and replaces them (sidestepping it). However, I was intrigued by the problem and got to thinking about how I would protect a script against accidentally altering itself and therefore going wrong.
- At the start of the script, copy self to a tempfile under a new name.
execthat file, killing the original (vulnerable) process and transferring control to the copy. The original script file may now safely be updated by the new process.
-
You may have spotted the major flaw in my concept: what's to stop an infinite loop of creating new files and transferring control to them until the disk is full?
-
The current working directory will change between the original invocation and that of the safe copy.
The script needs to know the difference between the first invocation, when it needs to protect itself, and the second, when it needs to get down to work!
- Use
$0, the name the script was executed as, to distinguish between the initial invocation and that of the safe copy, or - Set an environment variable when invoking the safe copy of the script, to flag that it is the safe copy.
Note: My initial script takes no parameters, and operates on the current working directory.
- Pass the directory to be worked on as an optional parameter, with the CWD as default. Definitely pass it when invoking the safe copy of the script, but the user can choose whether to pass a path.
- Set an environment variable for the CWD when invoking the safe copy of the script.
Footnotes
-
The web service does have paid options for Git or GitHub integration, but this project is not generating any income, so although I'd love to follow best practice, I have to fudge it for now. ↩
-
--modified in place, not replaced with a new file of the same name: Were the file to be replaced, the script would continue reading the original file from the original inode, and the new file would be assigned a new inode by the operating system, despite it having the same name. ↩