A habit I'm thinking of adopting for my shell scripting:

Write scripts in the form of a sequence of function definitions, with no commands at the top level that actually do anything. Then, at the very end, write a compound statement that calls one of the functions and ends with an exit. Like this:

#!/bin/bash
subroutine() { ... }
main() { ... }
{ main "$@"; exit $?; }

The idea is that the shell doesn't actually _do_ anything while reading this script until it reads the braced compound statement at the end. And then it's committed to exiting during the execution of that statement. So it won't ever read from the script file again.

With a normal shell script, it's dangerous to edit the script file while an instance of the shell is still running it, because the shell will read from the modified version of the file starting at the file position it had got to in the old one, perhaps reading a partial command or the wrong command and doing something you didn't want. But with a script in this style, the shell finishes reading the script before it does anything, so it's safe to edit.

(Of course, if your editor saves the new script file to a different file name and renames it over the top, you're safe anyway. But not all editors do: emacs, in particular, reopens the existing file and overwrites its contents.)

@simontatham I would never use this... It requires that you avoid ctrl-c. Why? And if you are testing a script why is it in the background anyway?

If you are so scared of your program crashing and running in a type of zombie state, why use nested functions at all, that almost guarantees bad behaviour. Furthermore, don't normal people use 'set -e'?

Idk maybe I'm just overthinking it or just don't understand..

@ponies I'm completely confused by everything you just said!

What's the problem you've spotted with Ctrl-C?

Who said the script was running in the background? More usually I'm running the script in one terminal window, and editing it in another, or in a GUI editor.

What nested functions are you talking about? I didn't show any.

How does 'set -e' help? That's solving a completely different problem.

@simontatham well my point is that stopping and starting the program again makes your issue irrelevant, right?

@ponies yes, certainly. If you always stop the script running, then edit it, then start it again, you don't need this protection.

But it's _useful_ to be able to edit a program so that the next run will work better, while the previous run is still going. It means you can fix each problem as you see it, instead of having to remember what they all were once the program finishes running, maybe hours later.

In almost any other language – interpreted _or_ compiled – you can do this safely. It's only in shell where editing a running script causes a disaster. Why _wouldn't_ someone want to eliminate that exception, and make shell scripts behave the same as everything else?

@simontatham This is dumb, even with no burden of implementing it.

So yes bash may have this problem, find me an editor that actually behaves this way. Basically, you have to contrive some crackpot scenario to have this eventuate.

Not even with sed -i "s/code/replacement/" can I trigger this.

Yes I note your emphasis on "useful". Indeed, it's not useful at all and furthermore I have to add functions which simply do not need to be there in the first place

@ponies I already mentioned an editor that behaves this way – emacs.

There's no weird edge case involved. You have a script wrapping some subcommand that takes a long time and you don't want to ^C it, like a backup. You run the script. You notice it could do something better. You edit the script, in emacs, now, while you can still remember what it was you wanted to change.

Then the backup (or whatever) finishes, bash reads more data from the script file, and gets confused by the fact that you edited it.

@simontatham no, I cannot do this in Emacs either.

Can you actually reproduce this at all? Please tell me how you encounter this

And if you can't.. why change your code style for a non issue 🧐

@ponies certainly I can reproduce it – I checked it this morning before making the original post. And I've reproduced it again just now. Here's a demo video.

If you can't reproduce this in emacs, that's very interesting. Perhaps if you stopped trying to insult me we might investigate *why* your emacs is behaving differently from mine? Does your emacs write a new file and rename it into place?

(You could check by examining the inode number of the file before and after saving over it, or by watching in strace what it's actually doing during the save action. In mine, strace shows that it opens the existing file name with O_CREAT|O_TRUNC|O_WRONLY, and before and after saving, 'ls -li' reports the same inode number.)

@simontatham so to make the above example work you have to specifically tell your editor to keep the same inode. I can see the Emacs setting which by default is set to nil (gnu manual). On my distro it is also nil.

If you are changing settings and then crying wolf.. it's kind of a lie isn't it? I can break stuff too you know

@ponies I didn't deliberately configure emacs to keep the same inode. I don't know why you think I did.

I did configure 'make-backup-files' to nil, to avoid the clutter of all those extra foo~ files. It looks as if that's what causes it: emacs in its default configuration renames the old file to the backup name and then makes a fresh
inode for the new file, but if you turn off make-backup-files, it just saves over the original file with no renaming.

But it's not unreasonable to turn off backup files. And emacs _could_ still make a new inode and atomically replace the old one, even if it's not making a backup file. It just doesn't.

What setting are you talking about, which defaults to nil? I haven't been able to find it. You apparently know its name but preferred to insult me again rather than actually saying what the name of the setting is?

@simontatham look I'm following and bookmarking you so 👍

I have also figured out how to make bash do this without Emacs. Ok it's real.

I'm just saying the idea of somebody running code and editing it at the same time (despite your use case) is for the most part a fiction, it requires

1. Editing configuration away from defaults, or
2. Overwriting the file from within the script whilst it's running

Edit: some possible setting, maybe
http://www.gnu.org/s/emacs/manual/html_node/emacs/Backup-Copying.html

Backup Copying (GNU Emacs Manual)

Backup Copying (GNU Emacs Manual)

@ponies >I'm just saying the idea of somebody running code and editing it at the same time (despite your use case) is for the most part a fiction, it requires [...]

It is not fiction, and a very common (and known case) case. You have a semi-long running shell script, you run it, and then see something to improve -- edit, and boom. This can lead to real crap too, depending on how far bash has executed the file (not parsed!).

I'm not sure that @simontatham way is solves the problem.

@amszmidt @simontatham The whole discussion was about the real possibility that this will NEVER HAPPEN if somebody did what you just said, @amszmidt.

Editors don't behave this way unless you make them behave that way.

@ponies Sorry, bullshit. This has happened to me during this single year multiple times. And this is with default settings on editors, and in this case #GNU Emacs.

@simontatham

@amszmidt @simontatham gnu documentation says otherwise.

All I ask is - what distro and which Emacs

@ponies Nonsense. It is unrelated to GNU/Linux system, or even Unix. It is also unrelated to #GNU #Emacs. It is all about how the standard input/output/error streams buffer data.

@simontatham

@ponies The solution @simontatham is/was presenting, could possibly (untested) be simplified by putting { and } around all the code. It forces #GNU #bash to read the whole block (storing it in memory, so buffered reading of the file won't be an issue -- in theory) before executing it.
@amszmidt @simontatham the only nonsense here is you ignoring what I'm actually saying -- 👌😆