Goto in Bash (2012)
bobcopeland.com
bobcopeland.com
See manpage at https://etsh.nl/man/_goto.1.html and source code at https://github.com/eunuchs/tsh/blob/master/goto.c.
See, history can give you a more inert syntax. And maybe a new way of thinking about how to make this thing... I would love to have a more robust version to do C-style goto cleanups.
Break up the script into steps, and place them in order in a dot-d style folder, like 1.foo.sh, 2.bar.sh, 3.baz.sh. Have a separate _context.sh which supplies the upfront variables or whatever are needed for each step. Ensure that each step can run on its own with just the context sourced. Then have a run.sh in the folder above that sources the context and the steps in order.
Now, that said, typically these kinds of things are dataflow or even buildsystem-adjacent, so I've also experimented with doing them in CMake (intermediate products managed with add_custom_target) or as Nix derivations (intermediate products are input-addressed in the Nix store), but I'd love to be aware of other tools that are better for expressing this kind of thing.
1) Use bash PS4 to show the line number currently executing (perhaps append start of line with #).
PS4='# ${LINENO}: ' bash -x script
2) Log to a script transaction file. unset PS4 before a function, reset after function call to restart at function call instead of restarting within a function.
trap signal error & dump variables to state file (via env or set command)
3) check for 'transaction file' on restart of script, if exists, a) restore saved state file settings;
b) pick up where to start via sdiff of original script and transaction file (aka first line not starting with #
sdiff -v ^'# script transaction_file | sh -
[0] : https://stackoverflow.com/questions/17804007/how-to-show-lin...Or, if I were doing it from scratch, I would not have a bash script that takes days to run, that sounds like some Real Programming, and I'd do it in Python, where it's easier to manage complexity, in case there was some more fine grained caching that could be done, maybe even between runs.
Maybe I need to make another coffee or something but I really don't understand at all why they wanted Goto for this.
You can also use flag files to track progress and resume where you left off.
reset.sh:
#! /bin/sh
[ -d DONE ] || mkdir DONE
find DONE -type f -delete
run-all.sh: #! /bin/sh
for s in step-*.sh
do
[ -f "DONE/$s" ] && { echo "*** (skipping $s)"; continue; }
echo "*** running $s"
./"$s" && touch "DONE/$s" || { echo "*** $s failed!"; break; }
doneI can see it being useful in certain situations, but I would think most use cases would benefit most from a better language.
Yesterday I was trying to track down why a bash script that worked on pretty much every platform I'd tried and across a bunch of different bash versions was not working on a new operating system. Turns out the script was relying on "non-standard" behavior in tr(1). Definitely not the first thing you think of when you see bash complaining about undefined variables.
A bit further back I had similar fun with FreeBSD's installer now that they've ripped perl5 out of the base system. The installer is, of course, an unholy mix of C and sh.
Granted, I really wish there was a python-lts that didn't break stdlib stuff every few years, but it seems like everything remotely modern breaks compatibility constantly...
Some people also like not having to do extra installs to run something simple.
And some scripts are simple enough that it's just easier to use bash.
But I agree, these cases are pretty obscure and most of the time the benefits of using a real language is worth the investment.
edited for clarity
Why wouldn't it? It's calling eval, not exec.
Seems janky even by the low standards of the author.
You can also create callbacks (i.e. function pointers to bash code) that you can pass to qsort() or bsearch()... or pthread_create? That last one was a joke, I mean, you probably could but I don't know what would happen - I don't think bash is reentrant :)
Did you have a usecase for this or it was just fun?
sed -n "/$label:/{:a;n;p;ba};"
I think it's more idiomatic to do: sed -n "/$label:/,$ p"
Or even: sed "0,/$label:/,$ d"
Which deletes the label itself, so you don't need the subsequent `grep -v ':$'`, but then you also aren't allowed to put any statements on the same line as the label.Precisely because there's too much gatekeeping in programming and learning etc. Give people sharp knives and let them break things
Neither does bash! That’s why you should always wrap things in a function and have the entry point of the script be the end of the file.
How I have done this is:
1. Put all the steps in functions.
2. Have a main function that calls all the other functions in order.
3. If given an argument, the main function skips all the functions up to that one.
Make is one of the most versatile pipeline tools out there.
TIMTOWTDI.
And nobody stops you from writing in assembly! You'll just spend more time on writing (and debugging) your programs but sure, go ahead, it's not like your time on Earth is finite.
[1] https://stackoverflow.com/questions/6021942/c-c-goto-into-th...
This is offensive nonsense and breathtaking entitlement. They're providing you free tools to try to help you, usually not even being paid for their work.
Edit to add: I just got copypasta trolled, didn't I ...
The way is done in that article doesn't seem best way to do; modifying the shell itself to seek the shell script file for it seem to be better in my opinion, or perhaps using the "enable" command to add your own built-in "goto" command (although I don't know if the API supports that). Another message on here mentions an external program messing with the file descriptors, but does bash use a consistent file descriptor number for the shell script file with which this will work?
As for the script that takes several days and often breaks half way through... sounds like what Makefiles are for to me.
package main
import "os"
func main() {
var x int
switch len(os.Args) {
case 1: goto start
default:
switch os.Args[1] {
case "foo": goto foo
case "mid": goto mid
}
}
start:
x = 100
goto foo
mid:
x = 101
println("This is not printed!")
foo:
if x == 0 { x = 10 }
println("x is", x)
}