Skip to content

Instantly share code, notes, and snippets.

@kovetskiy
Created July 7, 2020 19:01
Show Gist options
  • Save kovetskiy/a4bb510595b3a6b17bfd1bd9ac8bb4a5 to your computer and use it in GitHub Desktop.
Save kovetskiy/a4bb510595b3a6b17bfd1bd9ac8bb4a5 to your computer and use it in GitHub Desktop.
#!/bin/bash
###
### my-script — does one thing well
###
### Usage:
### my-script <input> <output>
###
### Options:
### <input> Input file to read.
### <output> Output file to write. Use '-' for stdout.
### -h Show this message.
help() {
sed -rn 's/^### ?//;T;p' "$0"
}
if [[ $# == 0 ]] || [[ "$1" == "-h" ]]; then
help
exit 1
fi
echo Hello World
@cvega
Copy link

cvega commented Jul 7, 2020

This doesn't actually work cross-platform, the -r option does not exist on macOS. You can do the same with awk though, just use:

Install GNU coreutils and gnu-sed on MacOS using brew install coreutils gnu-sed if you want to get closer to Linux.

@zouhair
Copy link

zouhair commented Jul 7, 2020

awk '/^###/' "$0"

awk -F'### ' '/^###/' "$0"

@brucewoodward
Copy link

That's cool but I think I'll stick with here documents.

#!/bin/bash

Help=$(cat <<-"HELP"

 my-script — does one thing well

 Usage:
   my-script <input> <output>

 Options:
   <input>   Input file to read.
   <output>  Output file to write. Use '-' for stdout.
   -h        Show this message.

HELP
)

help() {
    echo "$Help"
}

if [[ $# == 0 ]] || [[ "$1" == "-h" ]]; then
    help
    exit 1
fi

echo Hello World

@jdugan1024
Copy link

This is a really nifty approach, thanks for sharing. It's fairly obvious in retrospect but it hadn't occurred to me before!

Slight tweak, this awk solution will only print the help text, not the leading ### :

    awk -F'### ' '/^###/ { print $2 }' "$0"

@SuperSandro2000
Copy link

SuperSandro2000 commented Jul 8, 2020

Compacted:
[[ $# == 0 || "$1" == "-h" ]]

@evandrix
Copy link

evandrix commented Jul 8, 2020

This doesn't actually work cross-platform, the -r option does not exist on macOS. You can do the same with awk though, just use:

awk '/^###/' "$0"

Complete script:

#!/bin/bash
###
### my-script — does one thing well
###
### Usage:
###   my-script <input> <output>
###
### Options:
###   <input>   Input file to read.
###   <output>  Output file to write. Use '-' for stdout.
###   -h        Show this message.

help() {
    awk '/^###/' "$0"
}

if [[ $# == 0 ]] || [[ "$1" == "-h" ]]; then
    help
    exit 1
fi

echo Hello World

or you can use gsed, if you don't want to pollute the macos sed namespace

@bobrippling
Copy link

A sh compatible version, with portable sed usage

#!/bin/sh
###
### my-script — does one thing well
###
### Usage:
###   my-script <input> <output>
###
### Options:
###   <input>   Input file to read.
###   <output>  Output file to write. Use '-' for stdout.
###   -h        Show this message.

help() {
    sed -n 's/^### \?//p' "$0"
}

if [ $# -eq 0 ] || [ "$1" = "-h" ]; then
    help
    exit 1
fi

echo Hello World

@NightMachinery
Copy link

NightMachinery commented Jul 8, 2020

Complete awk version:

#!/usr/bin/env bash
###
### my-script — does one thing well
###
### Usage:
###   my-script <input> <output>
###
### Options:
###   <input>   Input file to read.
###   <output>  Output file to write. Use '-' for stdout.
###   -h        Show this message.

help() {
    awk -F'### ' '/^###/ { print $2 }' "$0"
}

if [[ $# == 0 || "$1" == "-h" ]]; then
    help
    exit 1
fi

echo Hello World

I personally use macros to add help messages to my zsh functions. It's as easy as:

mdoc-test() {
	# uses the global alias MAGIC
    mdoc Usage: sth MAGIC
    echo no
}
mdoc-test2() {
	# without global alias
    magic mdoc Usage: sth ; mret
    echo no
}

@rmNyro
Copy link

rmNyro commented Jul 8, 2020

Awesome idea, thanks very much!

@james-see
Copy link

Very cool.

Copy link

ghost commented Jul 12, 2020

That's a good idea. Awesome!

@jc00ke
Copy link

jc00ke commented Jul 12, 2020

@typebrook
Copy link

typebrook commented Jul 17, 2020

Glad someone proved this is a good strategy to write helper message, I also did it in my commonly-used script:
https://gist.github.com/typebrook/b0d2e7e67aa50298fdf8111ae7466b56#file-gist

what I did is simply print the comment at the top(except the first line to # --):
sed -Ene "/^#/ !q; 1,/^# --/ d; s/^# //p

@kovetskiy
@benhutchins

This doesn't actually work cross-platform, the -r option does not exist on macOS. You can do the same with awk though, just use:

-E works on BSD sed, so you can make it portable with sed -Ene 's/^### ?//;T;p' "$0" both on mac(BSD sed) or Linux(GNU sed).

@Qbin
Copy link

Qbin commented Jul 24, 2020

666

@snejus
Copy link

snejus commented Oct 15, 2020

Why even use the extended version?

sed -n 's/^###//p' achieves the same. Just keep the leading ### format to the help only.

sed -n '/^###/ s///p' this one's faster but a tiny bit more verbose.

and sed '/^###/!d' if you don't mind hashes. Or grep "^###"

@ivant
Copy link

ivant commented Nov 29, 2023

My version, that doesn't require multiple # (but allows more than one if you want it for stylistic reasons):

#!/bin/bash
# Usage:
#   test_usage.sh ...

print_usage_and_exit() {
  # The sed script below:
  # - Deletes the first line (shebang).
  # - Quits on the first line that doesn't start with #.
  # - Removes the '#+ ?' prefix from the string.
  sed -En '1d; /^[^#]/ q; s/^#+ ?//p;' < "$0" >&2
  exit 1
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment