How to make shell scripts robust to source being changed as they run
Have people noticed that if you modify the source of a shell script, any instances that are currently running are liable to fail?
This in my opinion is very bad; it means that I have to make sure all instances of a script are stopped before I make changes. My preferred behavior would be that existing scripts continue running with old source code and that new instances use the new code (e.g. what happens for perl and python programs).
Do folks have any good workarounds for this behavior, other than pre-copying the shell script to a tempfil开发者_StackOverflow社区e and running from that?
Thanks, /YGA
Very slight addition to the other answers:
#!/bin/sh
{
# Your stuff goes here
exit
}
The exit
at the end is important. Otherwise, the script file might still be accessed at the end to see if there are any more lines to interpret.
This question was later reposted here: Can a shell script indicate that its lines be loaded into memory initially?
Make sure the shell has to parse the whole file before executing any of it:
#!/bin/ksh
{
all the original script here
}
That does the trick.
Incidentally, with Perl (and I assume Python), the program parses the entire file before executing any of it, exactly as recommended here. Which is why you don't usually run into the problem with Perl or Python.
The desired behavior may not be possible, depending on complexity of the shell scripts that are involved.
If the full shell script is contained in a single source file, and that file is fully parsed before execution, then the shell script is generally safe from modifications to the copy on the disc during execution. Wrapping all the executable statements into a function (or series of functions) will generally achieve the goal you are after.
#!/bin/sh
doit()
{
# Stuff goes here
}
# Main
doit
The difficulty comes when the shell script "includes" other shell scripts (e.g. ".", or "source"). If these includes are wrapped in a function, they are not parsed until that statement is reached in the flow of execution. This makes the shell script vulnerable to changes to that external code.
In addition, if the shell script runs any external program (e.g. shell script, compiled program, etc), that result is not captured until that point in the execution is reached (if ever).
#!/bin/sh
doit()
{
if [[some_condition]] ; then
resultone=$(external_program)
fi
}
# Main
doit
this answer contains a robust and self contained way to make a script resistant to this problem: have the script copy and re-execute itself like this:
#!/bin/bash
if [[ $0 != /tmp/copy-* ]] ; then
rm -f /tmp/copy-$$
cp $0 /tmp/copy-$$
exec /tmp/copy-$$ "$@"
echo "error copying and execing script"
exit 1
fi
rm $0
# rest of script...
(This will not work if the original script begins with the characters /tmp/copy-
)
精彩评论