Skip to content

Instantly share code, notes, and snippets.

@Masterxilo
Last active August 30, 2023 14:46
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Masterxilo/29ac0df083827bbd45a7c8ddcf3936d7 to your computer and use it in GitHub Desktop.
Save Masterxilo/29ac0df083827bbd45a7c8ddcf3936d7 to your computer and use it in GitHub Desktop.
permfunction
#!/bin/bash
# This script adds a program/script/command called 'permfunction' to you PATH
# permfunction allows you to write system-wide, on-PATH instantly available, persisted scripts, i.e. improved shell functions/aliases/abbreviations/short programs.
#
# TODO also persist these functions as gists for the user
# inspired by
# https://askubuntu.com/questions/1414/how-to-create-a-permanent-alias
# https://askubuntu.com/a/80290/521770
#
# installation/update to latest version for current user (this takes precedence over global version, if your PATH is set up as usually):
#
# curl -s https://gist.githubusercontent.com/Masterxilo/29ac0df083827bbd45a7c8ddcf3936d7/raw/permfunction | bash - ; hash -d permfunction &> /dev/null || true
#
# installation/update of global version for all users (depending on your PATH only this might work out of the box):
#
# curl -s https://gist.githubusercontent.com/Masterxilo/29ac0df083827bbd45a7c8ddcf3936d7/raw/permfunction | sudo -E bash - ; hash -d permfunction &> /dev/null || true
#
# This last way of installing is required if you want to use sudo permfunction.
#
# uninstall:
# rm ~/bin/permfunction
# sudo rm /usr/bin/permfunction
# Note: Installation of the very latest version should be done with a hardlink to the current version /raw/.../functionname instead, because http caching
# in the gist server makes the /raw/functionname route not always reflect the latest update
set -e
# note that -e only exits immediately if top level error occus - except pipelines (see below) and sequences (unconditional continuation), expressions are evaluated to completion
set -o pipefail
installdir="/usr/bin"
! [ -w "$installdir" ] && { installdir="$HOME/bin" ; }
mkdir "$installdir" &> /dev/null || true
file="$installdir/permfunction"
[ -f "$file" ] && { echo >&2 "warning: $file exists, overwriting" ; }
echo "installing permfunction at $file..." ;
which permfunction &> /dev/null && ! [ "`which permfunction`" == "$file" ] && { echo >&2 "error: permfunction is already installed elsewhere at `which permfunction`. Prefer to have one up-to-date installation to avoid version confusion." ; exit 1 ; }
# -------- permfunction source code -----------
cat > "$file" <<'EOF'
#!/bin/bash
set -e
set -o pipefail
[ "$1" == "--redefine" ] && { shift ; redefine=true ; }
[ "$1" == "--file" ] && { shift ; file=true ; }
name="$1"
if [ -z "$redefine" ] ; then
which "$name" &>/dev/null && { echo "error: command with name $name exists at `which "$name"`. use --redefine to overwrite" ; exit 1 ; } || true # refuse to overwrite/take precedence over existing commands to avoid confusion!
fi
shift || true # shift fails if there are no variables!
code=$(echo "$@") # code is not just $2, but all remaining args, space separated
[ ! -z "$file" ] && { code=$(cat "$code") ; }
FD_NUMBER_STDIN=0 # this is a system constant in linux
if [ -z "$code" ] && [[ -t $FD_NUMBER_STDIN ]] ; then
echo >&2 "code is not defined and STDIN is a tty which is not allowed. use 'cat - | permfunction $name' if you want a simple editor. or use 'echo code | permfunction $name'";
echo ""
# will display usage below:
else
[ -z "$code" ] && {
code="$(cat /dev/stdin)";
}
fi
( [ "$name" == "-help" ] || [ "$name" == "-?" ] || [ "$name" == "--?" ] || [ "$name" == "--help" ] || [ -z "$name" ] || [ -z "$code" ] ) && { cat >&2 <<'USAGEEOF'
permfunction allows you to write system-wide, on-PATH instantly available, persisted scripts, i.e. improved shell functions/aliases/abbreviations/short programs.
usage: [sudo] permfunction [--redefine] name code
[sudo] permfunction [--redefine] name code more-code even-more-code
[sudo] permfunction [--redefine] name <<'EOF'
code
EOF
[sudo] permfunction [--redefine] name < file_containing_code
-command generating code on stdout- | [sudo] permfunction [--redefine] name
[sudo] permfunction [--redefine] --file name file_containing_code
where stdin cannot be a tty (we don't want to implement any line/terminal editing in this tool).
usage examples:
permfunction cls "clear"
after this, cls does the same as clear or ctrl+l in any open terminal (with the same PATH)
sudo permfunction cls "clear"
same, but install for all users
permfunction sayhi 'echo Hello $1'
after this, "sayhi World" prints "Hello World". Note that we cannot let the shell expand $1!!!
permfunction dosomething '
echo "this script does"
echo "something with $1"
echo "which is so complex that it needs this many lines!!!"
'
after this, try 'dosomething "your shell"', and check out 'cat `which dosomething`'
USAGEEOF
exit 1 ; }
re="[[:space:]]+"
if [[ "$name" =~ $re ]]; then
echo >&2 "usage: permfunction [--redefine] name code" ;
echo >&2 "error: name may not contain spaces, was '$name'" ;
exit 1 ;
fi
installdir="/usr/bin" # system install. requires sudo to write
! [ -w "$installdir" ] && { installdir="$HOME/bin" ; }
mkdir "$installdir" &> /dev/null || true
file="$installdir/$name"
[ -f "$file" ] && [ -z $redefine ] && {
echo >&2 "error: $file exists. Use --redefine to force overwriting" ;
[ -w ] && echo >&2 "or use rm $file to delete the existing command manually first";
! [ -w ] && echo >&2 "or use sudo rm $file to delete the existing command manually first";
exit 1 ;
}
[ -f "$file" ] && ! [ -z $redefine ] && { echo >&2 "warning: $file exists. Redefining." ; }
echo "$code" > "$file"
chmod +x "$file"
echo "installed $name to $file"
echo "-------------- code listing 'cat $file' ---------------"
cat "$file"
echo "-------------- code listing end ---------------"
! [ "`which $name`" == "$file" ] && { echo >&2 """
error: Installation in PATH failed
which $name
returns
`which $name`
instead of expected
$file
Probably '$installdir' is not in your PATH or another program of the same name is taking precedence. If you used [sudo] permfunction, try to use with/without sudo instead.
""" ; exit 1 ; }
[ "`which $name`" == "$file" ] && { echo "successfully installed $name in PATH, you can now use it" ; }
hash -d $name &> /dev/null || true # no idea if this helps something/escapes the scope of this script
EOF
chmod +x $file
name="permfunction"
echo "installed $name to $file"
! [ "`which $name`" == "$file" ] && { echo >&2 """
error: Installation in PATH failed
which $name
returns
`which $name`
instead of expected
$file
Probably '$installdir' is not in your PATH or another program of the same name is taking precedence. If you used [sudo] permfunction, try to use with/without sudo instead.
"""; exit 1 ; }
[ "`which $name`" == "$file" ] && { echo "successfully installed $name in PATH, you can now use it" ; }
hash -d permfunction &> /dev/null || true # no idea if this helps something/escapes the scope of this script
@Masterxilo
Copy link
Author

it would be cool if I could know what the current revision's url is when installing... maybe using the etag ...

@Masterxilo
Copy link
Author

hash -d permfunction to stop permfunction from being searched in the wrong place after reinstallling.

@Masterxilo
Copy link
Author

TODO make this persist the functions to a repository
TODO make all functions/scripts this creates self-updating

@Masterxilo
Copy link
Author

tested working on ubuntu & WSL (windows subsystem for linux) ubuntu

@Masterxilo
Copy link
Author

however, on WSL the PATH is polluted with windows stuff, so only the sudo variant worked - the personal ~/bin dir appears not to be in the PATH.

@Masterxilo
Copy link
Author

Masterxilo commented Nov 27, 2018

Here's a command I recently defined (adapted from https://stackoverflow.com/questions/12498304/using-bash-to-display-a-progress-working-indicator https://stackoverflow.com/a/50699733/524504):

permfunction --redefine spinner <<'EOF'
#!/usr/bin/env bash

show_spinner()
{
  local -r pid="${1}"
  local -r delay='0.05'
  local spinstr='\|/-'
  local temp
  while ps a | awk '{print $1}' | grep -q "${pid}"; do
    temp="${spinstr#?}"
    printf " [%c]  " "${spinstr}"
    spinstr=${temp}${spinstr%"${temp}"}
    sleep "${delay}"
    printf "\b\b\b\b\b\b"
  done
  printf "    \b\b\b\b"
}

("$@") &
show_spinner "$!"
EOF

usage:

spinner sleep 5

(known problem: the cursor jumps around and blinks to draw this spinner)

@Masterxilo
Copy link
Author

@Masterxilo
Copy link
Author

Recommended aliases:

  • define
  • pfunction
  • persistent_function
  • persist

definition using:

permfunction --redefine palias $'name=$1; shift; permfunction $name "$@"\' "$@"\''

# then
palias define permfunction
palias pfunction permfunction
palias persistent_function permfunction
palias persist permfunction

palias persistent_alias palias

@Masterxilo
Copy link
Author

nice command: recursive touch/mkdir -p

palias mkdirp 'mkdir -p'

permfunction touchp 'mkdir -p "$(dirname "$1")" && touch "$1" ;'
palias touch2 touchp

https://askubuntu.com/questions/800845/create-file-and-its-parent-directory

touchp test/about/me

find test
tree test

@Masterxilo
Copy link
Author

touchp src/main/java/hello/Application.java
for
https://spring.io/guides/gs/testing-web/

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