loveknight

joined 1 month ago
 

This is a little tutorial that I found in my search to learn how to use getopt (mind: not getopts, which is a completely different thing). I want to share it here because I find it refreshingly to the point. Just the main code block already tells almost the whole story:

#!/bin/bash
# Set some default values:
ALPHA=unset
BETA=unset
CHARLIE=unset
DELTA=unset

usage()
{
  echo "Usage: alphabet [ -a | --alpha ] [ -b | --beta ]
                        [ -c | --charlie CHARLIE ] 
                        [ -d | --delta   DELTA   ] filename(s)"
  exit 2
}

PARSED_ARGUMENTS=$(getopt -a -n alphabet -o abc:d: --long alpha,bravo,charlie:,delta: -- "$@")
VALID_ARGUMENTS=$?
if [ "$VALID_ARGUMENTS" != "0" ]; then
  usage
fi

echo "PARSED_ARGUMENTS is $PARSED_ARGUMENTS"
eval set -- "$PARSED_ARGUMENTS"
while :
do
  case "$1" in
    -a | --alpha)   ALPHA=1      ; shift   ;;
    -b | --beta)    BETA=1       ; shift   ;;
    -c | --charlie) CHARLIE="$2" ; shift 2 ;;
    -d | --delta)   DELTA="$2"   ; shift 2 ;;
    # -- means the end of the arguments; drop this, and break out of the while loop
    --) shift; break ;;
    # If invalid options were passed, then getopt should have reported an error,
    # which we checked as VALID_ARGUMENTS when getopt was called...
    *) echo "Unexpected option: $1 - this should not happen."
       usage ;;
  esac
done

echo "ALPHA   : $ALPHA"
echo "BETA    : $BETA "
echo "CHARLIE : $CHARLIE"
echo "DELTA   : $DELTA"
echo "Parameters remaining are: $@"

Just be sure to correct the inadvertent mixing of beta and bravo.

[–] loveknight@programming.dev 1 points 2 weeks ago* (last edited 2 weeks ago) (1 children)

Perfect, thanks for the explanation. Indeed, I found the same solution via StackOverflow about simultaneously.

 

Edit: I figured it out: The solution is over a process substitution operator:

join <(echo "$var1") <(echo "$var2")

See also the comment by @notabot@piefed.social below, this StackOverflow comment, and the GNU documentation..


It's comparatively straightforward to use the content of one variable in join by using - to tell it to use standard input for that file:

echo $variable | join - anotherfile

However, is there a way to serve both input 'files' from variables, avoiding temporary files on the disk?

It seems like the easiest way would be via creating and mounting a temporary partition via tmpfs,

mount -t tmpfs -o size=50m tmpfs /mountpoint,

and just create temporary files in there. And afterwards clean things up.

So far I've also attempted here-documents, but apparently this too can only provide standard input, so that the other input still has to be served from a file.

Maybe one can also try doing it via named pipes (mkfifo), but I fear this could introduce lots of potentialities for errors.

[–] loveknight@programming.dev 2 points 3 weeks ago

Ah that's good to know about zsh.

Sorry regarding the second code block; it does indeed work as intended, and quite elegantly.

[–] loveknight@programming.dev 1 points 3 weeks ago* (last edited 3 weeks ago) (2 children)

For the first code snippet to run correctly, $list would need to be put in double quotes: echo "$list" | ... , because otherwise echo will conflate the various lines into a single line.

The for loop approach is indeed quite readable. ~~To make it solve the original task (which here means that it should also assign a number just smaller than $threshold to $tail, if $threshold is not itself contained in $list), one will have to do something in the spirit of what @Ephera@lemmy.ml and I describe in these comments.~~

[–] loveknight@programming.dev 3 points 3 weeks ago* (last edited 3 weeks ago)

Thank you, in fact I ended up doing something that's mathematically pretty much just that: I have the previous line stored in an auxiliary variable lastline, and it is the evaluation of the current line $0 that determines whether the previous line gets printed.

awk -v threshold=150 'BEGIN {lastline=""}
  (lastline!="" && threshold<$0){print lastline} #the additional check lastline!="" prevents an empty line at the very beginning
  {lastline=$0}
  END{print} #hardcode printing of the very last line, because otherwise it would never be printed
' 

Of note, in the case where some list entries are repeated, the behavior of this script will be:

  • The threshold value, if it's in the list, will always be printed just once, even if it occurs multiple times in the list, and also if it happens to be the first, last, or only entry in the list.
  • All larger entries will be printed exactly as often as they occur in the list. This even holds for the largest value: its last repetition will be printed via the final END{print} statement, whereas all preceding instances get printed through the statement that depends on threshold<$0.

(IIRC, it was a StackOverflow post that led me to this.)

 

Let me show you what I mean by giving an example:

# Assume we have this list of increasing numbers
140
141
145
180
190
...

# If we pick 150 as threshold, the output should consist of 145 and all larger values:
145
180
190
...

# In the edge case where 150 itself is in the list, the output should start with 150:
150
180
190
...

I guess one can always hack something together in awk with one or two track-keeper variables and a bit of control logic. However, is there a nicer way to do this, by using some nifty combination of simpler filters or awk functionalities?

One way would be to search for the line number n of the first entry larger than the threshold, and then print all lines starting with n-1. What command would be best suited for that?

Still, I'm also wondering: Can awk or any other standard tool do something like "for deciding whether to print this line, look at the next line"?

(In my use case, the list is short, so performance is not an issue, but maybe let's pretend that it were. Also, in my use case, all entries are unique, but feel free to work without this assumption.)

[–] loveknight@programming.dev 1 points 3 weeks ago

Very specifically for learning about GNU/Linux and Unix, I highly recommend the book Classic Shell Scripting by Arnold Robbins and Nelson Beebe (O'Reilly Media, 2005).

ISBN: 9780596005955

I recently wrote the following about it in a post:

This book is extremely readable and gives a very good introduction to the various standard Unix shell commands (grep, sed, awk, tr, sort, to name but a few) and how to tie them together to do useful things. It’s very suitable if you have some experience with the command line at the level of individual commands but now want to see how to do construct more interesting pipelines and scripts. It includes an introduction to regular expressions. The fact that the book is already 20 years certainly means that some explanations and approaches are outdated, but since shell programming is at the core about text processing, almost all contents of the book are still highly relevant today.

 

Things I would like every young web engineer to learn:

  • anything you can do in CSS + HTML, you should do in CSS + HTML
  • framework du jour is not a platform, it's a high-interest loan against your future capacity. The platform is the platform
  • understanding the memory hierarchy always matters
  • client-side isn't easier than the server, and "generalists" usually suck at client-side. Mind the (packet) gap
  • managers who are not technical are not useful
  • put users first, always

Second-order things to learn:

  • the way browsers work isn't static, but it also isn't changing that fast. Learn as much as you can and update every few years; particularly about networking and the rendering loop.
  • JS is the slowest way to do anything on the web. Never let it become the way you do everything.
  • a11y isn't nice-to-have, it's the job
  • shipping fast almost never matters as much as quality, & there are simple heuristics you can use to understand the difference
[–] loveknight@programming.dev 2 points 1 month ago

New to this instance, but for me too it is comparatively sluggish since I started using it yesterday.

[–] loveknight@programming.dev 15 points 1 month ago* (last edited 1 month ago) (3 children)

Or get a used thin client (e. g. HP T620, T630, T640 or Dell Wyse 5070). Cost: ~40-100$. Biggest advantage: Passive cooling, i. e. they're absolutely quiet.