For discussions about programming and projects not necessarily associated with Porteus.
-
Bogomips
- Full of knowledge
- Posts: 2564
- Joined: 25 Jun 2014, 15:21
- Distribution: 3.2.2 Cinnamon & KDE5
- Location: London
Post#1
by Bogomips » 27 Aug 2015, 14:37
Searching for something else, came across this little gem: goto in bash
goto in bash
October 26, 2012hackingbash, basic-hate
If you are as old and nerdy as I, you may have spent your grade school days hacking in the BASIC computer language. One of the (mostly hated) features of the (mostly hated) language was that any statement required a line number; this provided both the ability to edit individual lines of the program without a screen editor, as well as de facto labels for the (mostly hated) GOTO and GOSUB commands. But you could also use line numbers to run your program starting from any random point: “RUN 250″ might start in the middle of a program, typically after line 250 exited with some syntax error and was subsequently fixed.
Today, in bash, we have no such facility. Why on earth would anyone want it, with the presence of actual flow control constructs? Who knows, but asking Google about “bash goto” shows that I am not the first.
For my part, at $work, I have a particular script which takes several days to run, each part of which may take many hours, and, due to moon phases, may fail haphazardly. If a command fails, the state up to that point is preserved, so I just need to continue where that left off. Each major part of the job is already factored into individual scripts, so I could cut-and-paste commands from the failure point onward, but I’m lazy.
Thus, I present bash goto. It runs sed on itself to strip out any parts of the script that shouldn’t run, and then evals it all. Prepare to cringe.
Code: Select all
#!/bin/bash
# include this boilerplate
function jumpto
{
label=$1
cmd=$(sed -n "/$label:/{:a;n;p;ba};" $0 | grep -v ':$')
eval "$cmd"
exit
}
start=${1:-"start"}
jumpto $start
start:
# your script goes here...
x=100
jumpto foo
mid:
x=101
echo "This is not printed!"
foo:
x=${x:-10}
echo x is $x
results in:
Code: Select all
$ ./test.sh
x is 100
$ ./test.sh foo
x is 10
$ ./test.sh mid
This is not printed!
x is 101
My quest to make bash look like assembly language draws ever nearer to completion.
@Rava
Can you explain what's going on here in words of one syllable?
Linux porteus 4.4.0-porteus #3 SMP PREEMPT Sat Jan 23 07:01:55 UTC 2016 i686 AMD Sempron(tm) 140 Processor AuthenticAMD GNU/Linux
NVIDIA Corporation C61 [GeForce 6150SE nForce 430] (rev a2) MemTotal: 901760 kB MemFree: 66752 kB
Bogomips
-
Ed_P
- Contributor
- Posts: 8369
- Joined: 06 Feb 2013, 22:12
- Distribution: Cinnamon 5.01 ISO
- Location: Western NY, USA
Post#2
by Ed_P » 27 Aug 2015, 15:03
Bogomips wrote:@Rava
Can you explain what's going on here in words of one syllable?
Yes please.
But it seems to be more of a PERFORM or CALL of a subroutine rather than a GOTO. But still useful.
Ed
Ed_P
-
port
- Samurai
- Posts: 137
- Joined: 18 Feb 2016, 09:25
- Distribution: Linux porteus 3.2.2 KDE
- Location: Spain
Post#3
by port » 23 Feb 2016, 19:15
it is a jump in the same way as it is in DOS .bat files.
Code: Select all
echo "done!"
goto next
echo "not done"
:next
echo "also done!"
but this is a backward step to spaghetti code, why to go backwards when we have structured programing in bash? ;-)
port
-
Ed_P
- Contributor
- Posts: 8369
- Joined: 06 Feb 2013, 22:12
- Distribution: Cinnamon 5.01 ISO
- Location: Western NY, USA
Post#4
by Ed_P » 23 Feb 2016, 21:53
Trust me, crappy code can be written using structured programming just like efficient well written code could be written using gotos. The language isn't what makes a program sloppy or inefficient, the programmer is.
This example seems pretty clear and clean to me.
Code: Select all
@echo off
if exist c:\*.tmp goto :tmps
if exist c:\*.log goto :logs
goto :end
:tmps
echo c:\*.tmp files
dir c:\*.tmp
goto :end
:logs
echo c:\*.log files
dir c:\*.log
goto :end
:end
echo Finished
Ed
Ed_P
-
port
- Samurai
- Posts: 137
- Joined: 18 Feb 2016, 09:25
- Distribution: Linux porteus 3.2.2 KDE
- Location: Spain
Post#5
by port » 06 Mar 2016, 19:07
Yes, I know programming style is a matter of BCAK and thus you can produce spaghetti code with structured programing as well as using goto's and in fact pretty old assembly code could be a great example of efficient well written code... but there's a key difference, pretty old assembler has RET statement while DOS scripting (aka BAT files) does not and that's the key point about language contrbuting to crappy code, let's see in in you own example
It's seems pretty clear and clean at first sight but what if you need to call a subroutine inside your rutine? crappiness appeared!
Code: Select all
@echo off
if exist c:\*.tmp goto :tmps
if exist c:\*.log goto :logs
goto :end
:tmps
echo c:\*.tmp files
dir c:\*.tmp
goto stats
:statsret
echo c:\ dirs
tree
goto :end
:logs
echo c:\*.log files
dir c:\*.log
goto :end
:stats
dir c:\*.tmp | find /v /c "%@$fake&*" | more
goto statsret
:end
echo Finished
DOS batch scripting ins inherently crappy and I think is better not to emulate it from more powerfull bash scripting ;-)
port
-
Ed_P
- Contributor
- Posts: 8369
- Joined: 06 Feb 2013, 22:12
- Distribution: Cinnamon 5.01 ISO
- Location: Western NY, USA
Post#6
by Ed_P » 06 Mar 2016, 20:24
Or
Code: Select all
@echo off
if exist c:\*.tmp goto :tmps
if exist c:\*.log goto :logs
goto :end
:tmps
echo c:\*.tmp files
dir c:\*.tmp
call :stats
:statsret
echo c:\ dirs
tree
goto :end
:logs
echo c:\*.log files
dir c:\*.log
goto :end
:stats
dir c:\*.tmp | find /v /c "%@$fake&*" | more
goto :eof
:end
echo Finished
Ed
Ed_P
-
port
- Samurai
- Posts: 137
- Joined: 18 Feb 2016, 09:25
- Distribution: Linux porteus 3.2.2 KDE
- Location: Spain
Post#7
by port » 06 Mar 2016, 21:27
This is a bit tricky because call command only allows calling to a label in windows batch scripting, in pure prior msdos you only can call another batch file as far as I know
Anyway I suppose you agree msdos batch scripting is tricky and head you to crappy code style. Windows has improved things even to a great level when considering powershell
port
-
Ed_P
- Contributor
- Posts: 8369
- Joined: 06 Feb 2013, 22:12
- Distribution: Cinnamon 5.01 ISO
- Location: Western NY, USA
Post#8
by Ed_P » 06 Mar 2016, 23:28
port wrote:prior msdos you only can call another batch file as far as I know
You have a good memory. I believe that was indeed the case.
Anyway I suppose you agree msdos batch scripting is tricky and head you to crappy code style.
I'm pretty sure any programming style can lead to crappy code. It depends on the size of the code, the programmer(s) involved, and when the program has to be implemented.
Ed
Ed_P
-
Dave99
- Black ninja
- Posts: 54
- Joined: 19 Apr 2014, 20:15
- Distribution: Porteus
- Location: R.S.A.
Post#9
by Dave99 » 15 Apr 2016, 00:08
@Bogomips
Rather like your goto in Bash, reminds me of the old Basic days.
Actually your post got me thinking, how much could one replicate the Basic language in Bash.
Bash has no means of creating macros like in MASM or NASM but it does have the "source" command and when used with functions, we could replicate some of the Basic language commands.
So one could first create a file and call it say basic.inc which could amongst other things contain:
Code: Select all
#!/usr/bin/env bash
print()
{
echo $1
}
rem()
{
:
}
cls()
{
clear
}
# and so on.....
Then include the basic.inc file in a Bash script like so:
Code: Select all
#!/usr/bin/env bash
source /path/to/basic.inc
# The two lines above now the only non-Basic lines.
print "hello just testing the new print command"
rem this is just a comment, does nothing
sleep 2 # same as in Basic
cls
Obviously not too useful but fun.
Dave
Dave99
-
Rava
- Contributor
- Posts: 5416
- Joined: 11 Jan 2011, 02:46
- Distribution: XFCE 5.01 x86_64 + 4.0 i586
- Location: Forests of Germany
Post#10
by Rava » 16 Apr 2016, 07:02
I had no idea ":" is also a comment in bash, I always use "#"
What is the difference to "#"?
Seems there are too many instances of ":" to be found in
man bash to get what I ask it for.
Cheers!
Yours Rava
Rava
-
Rava
- Contributor
- Posts: 5416
- Joined: 11 Jan 2011, 02:46
- Distribution: XFCE 5.01 x86_64 + 4.0 i586
- Location: Forests of Germany
Post#12
by Rava » 16 Apr 2016, 07:57
Me thinks this answer nails it quite good:
It's there more for historical reasons. The colon builtin : is exactly equivalent to true. It's traditional to use true when the return value is important, for example in an infinite loop:
while true; do
echo 'Going on forever'
done
It's traditional to use : when the shell syntax requires a command but you have nothing to do.
while keep_waiting; do
: # busy-wait
done
with what someone added:
Because : is a command, the shell still has to process its arguments before it can discover that : ignores them. Mostly, you are just making the shell do extra work in expanding * to a list of files in the current directory; it won't actually affect how the script works. – chepner Nov 5 '14 at 14:48
I suppose it might make your script fail if you happen to run in in a directory with a lot of files, making the glob expansion go over the command length limit? Maybe only if you have set -e on. Anyway, hopefully we can all just agree to use #
– Jack O'Connor Nov 7 '15 at 22:14
So, in short, nope, ":" is not a comment!
It is a "program" that ignores all parameters, but like the comments above, using "* * *" could make a script crash. xD
But.... using "#" instead of ":" would not work, cause for bash "#" is like ""
Cheers!
Yours Rava
Rava
-
Dave99
- Black ninja
- Posts: 54
- Joined: 19 Apr 2014, 20:15
- Distribution: Porteus
- Location: R.S.A.
Post#13
by Dave99 » 16 Apr 2016, 09:23
@Rava
So, in short, nope, ":" is not a comment!
Correct and the easy way to test it as follows:
This works as there is a command in the function even though it does nothing:
While this will
not work as there is only a comment:
Code: Select all
DoNothing()
{
# let's do nothing
}
Dave99
-
Bogomips
- Full of knowledge
- Posts: 2564
- Joined: 25 Jun 2014, 15:21
- Distribution: 3.2.2 Cinnamon & KDE5
- Location: London
Post#14
by Bogomips » 16 Apr 2016, 18:12
Found a use for the ':"
This is in tab expansion. So far been having to use ls for a non-executable, but now:
Code: Select all
guest@porteus:~$ p10/tmp/iso/ # And no further!
# but here:
guest@porteus:~$ :p10/tmp/iso/Porteus-XFCE-x86_64.iso
Also just written something v. long to mount, and forgotten to mkdir mountpoint, so stuck : in front of it all.
Update
Stumbled upon this which has more than a couple of lines about
BASH ':' COMMAND
Last edited by
Bogomips on 17 Apr 2016, 19:28, edited 1 time in total.
Reason: More : info
Linux porteus 4.4.0-porteus #3 SMP PREEMPT Sat Jan 23 07:01:55 UTC 2016 i686 AMD Sempron(tm) 140 Processor AuthenticAMD GNU/Linux
NVIDIA Corporation C61 [GeForce 6150SE nForce 430] (rev a2) MemTotal: 901760 kB MemFree: 66752 kB
Bogomips