See also [[my aliases and functions]] and [[my bashrc]]. ## Exists `exists` ```perl #!/usr/bin/env perl # returns true if at least one argument exists (false if no args) for(@ARGV) { -e && exit 0; } exit 1; ``` `allexist` — contrapositive of `exists` ``` #!/usr/bin/env perl # returns false if at least one argument does not exist (true if no args) for(@ARGV) { ! -e && exit 1; } exit 0; ``` `filterstat` — basically `filter(stat,args)` ``` #!/usr/bin/env perl # prints out args iff they exist for(@ARGV) { -e && print "$_\n"; } ``` ## Script creator I call this `d`, based on the idea of [Forth](/lang/forth)'s `:` word: ```bash #!/bin/bash # This is Forth's famous colon # Each arg is a command with parameters as a literal string # e.g. # $ d hello 'echo hello world' 'echo fluffy bunny' 'echo "$0"' A="$1" ; shift if [ -e "$A" ]; then echo "$A already exists."; exit 1; fi echo '#!/bin/bash' > "$A" || exit 2 I=11 while [ -n "$1" ]; do echo "$1" >> "$A" || exit $((I)) shift ((I++)) done cx "$A" # cx is short for chmod a+x ``` ## Apt-get shorthands I'm pretty easy with `sudo` on my own Linux machines. And my fingers are lazy, so `ag` ```bash #!/bin/bash sudo apt-get "$@" ``` `ai` ```bash #!/bin/bash # ai ag install "$@" ``` `aii` ```bash #!/bin/dash ai -y "$@" ``` `asr` ```bash #!/bin/bash apt-cache search "$@" ``` `asrc` ```bash #!/bin/bash apt-get source "$@" ``` ## Date and time related While [Python](/lang/python) is my goto language for most scripting, [Perl](/lang/perl) starts up much more quickly, so I often use it in preference for simple scripts. `ymd` ```bash #!/bin/dash date +"%Y_%m_%d" ``` `ymdt` ```bash #!/bin/dash date +"%Y_%m_%d__%H_%M_%S" ``` `hms` — convert e.g. `1h4m32s` to `3872` (number of seconds) ```perl #!/usr/bin/perl @s = (); %f = (); $total = 0; sub tohms { my ($t) = @_; my ($d,$h,$m,$s); $s = $t % 60; $m = int($t / 60) % 60; $h = int($t / 3600) % 24; $d = int($t / (3600*24)); my ($x); $x = ""; if( $d ) { $x .= "${d}d";} if( $h ) { $x .= "${h}h";} if( $m ) { $x .= "${m}m";} if( $s ) { $x .= "${s}";} if( $x =~ /^$/ ) { $x = "0s"; } return $x; } sub fromhms { my ($hms) = @_; my ($hh,$mm,$ss,$h,$m,$s,$t); if( $hms =~ /^(\d+h)?(\d+m)?(\d+s?)?$/ ) { $hh = $1; $mm = $2; $ss = $3; $hh =~ s/h$//; $mm =~ s/m$//; $ss =~ s/s$//; $h = int($hh); $m = int($mm); $s = int($ss); $t = 3600*$h + 60*$m + $s; return $t; } else { return -1; } } for (@ARGV) { print(fromhms($_)."\n"); } ``` `tohms` — inverse of `hms` — convert number of seconds to `hms` format, e.g. `3872 => 1h4m32s`. ```perl #!/bin/bash A="$1" ((s=A%60)) ((A /= 60)) ((m=A%60)) ((A /= 60)) T="" if (( A > 0 )); then T="$T${A}h"; fi if (( m > 0 )); then T="$T${m}m"; fi if (( s > 0 )); then T="$T${s}s"; fi if [ -n "$T" ]; then echo "$T"; fi ``` ## Clipboard related These detect the running system and use the appropriate command for accessing the clipboard. `pc` ```bash #!/bin/dash # copy to clipboard if [ -n "$DISPLAY" ]; then # X11 cat "$@" | xsel -i -b elif [ -d "/Applications" ]; then # macos cat "$@" | pbcopy elif [ -d "/cygdrive/c/cygwin64" ]; then # cygwin cat "$@" > /dev/clipboard else echo "Cannot copy as not gui" > /dev/stderr fi ``` `pp` ```bash #!/bin/dash if [ -n "$DISPLAY" ]; then # X11 paste() { xsel -o -b; } elif [ -d "/Applications" ]; then # macos paste() { pbpaste; } elif [ -d "/cygdrive/c/cygwin64" ]; then # cygwin paste() { cat /dev/clipboard; } else echo "Cannot paste as not gui" > /dev/stderr fi if [ -n "$1" ]; then paste | tee "$1" else paste fi ``` ## Wakeonlan and ssh These send a `wakeonlan` to a given machine, wait for it to boot up (based on the empirical result that 2 minutes is enough` and run `ssh`. The `wasp` command also runs a specified signal command to alert me that the target machine is likely booted. ```bash #!/bin/bash # Wake, sleep and ssh H="$1" if [ -z "$H" ]; then echo "$0 []" exit 1 fi T="${2-120}" shift shift wakey "$H" sleepy "$T" && ssh "$H" "$@" ``` ```bash #!/bin/bash # Wake, sleep, and ssh -- signal by launching an app specified in the SIG environment variable H="$1" SIG="${SIG-sig}" if [ -z "$H" ]; then echo "$0 []" echo "Use env variable SIG to set command to fire when wait is over. By default 'sig'." exit 1 fi T="${2-120}" shift shift wakey "$H" sleepy "$T" && { "$SIG" $SIGARGS >& /dev/null & ssh "$H" "$@" ; } ``` ## sleepy ­— sleep with formatted countdown ```python #!/usr/bin/python3 import os, sys, time, math, colors, re def newline(): print() def get_width(): return os.get_terminal_size().columns def showline(x,color='white',style=""): x = str(x) width = get_width() cx = "[ "+colors.color(x,fg=color,style=style)+" ]" print(f"\r{cx:=^{width}}",end="") def select_colour(t): if t < 10: return 'red' if t < 30: return 'yellow' else: return 'green' def dosleep(t): t0 = int(math.floor(t)) t1 = t - t0 if t1 < 0.01: t1 = 0 showline(f"sleeping for {t} seconds",color=select_colour(t)) if t1 > 0: time.sleep(t1) while t0 > 0: showline(f"sleeping for {t0} seconds",color=select_colour(t0)) t0 -= 1 time.sleep(1) showline(f"finished sleeping",color="green") newline() def main(): args = sys.argv[1:] txs = [] try: for arg in args: if m := re.match(r"(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)m)?(?:(\d+(?:\.\d+)?)s?)?$",arg): d, h, m, s = map(lambda t: float(t) if t is not None else 0.0, m.groups()) txs.append(s+60*(m+60*(h+24*d))) else: raise ValueError() except ValueError: print(f"sleepy t0 [t1 ...]\nwhere t0 etc are numbers or take the form 3h45m34s with the latter s optional") exit(1) t = sum(txs) #print(f"total time: {t}") #exit() dosleep(t) if __name__ == "__main__": main() ``` ## Tmux related tm — launch `tmux` ```bash #!/bin/bash if [ -z "$1" ]; then tmux else S="$1" shift if [[ $S =~ : ]]; then echo "Session names cannot contain :" exit 2 fi if tmux list-sessions | cut -f1 -d: | grep -q "^$S\$"; then echo "Session $S already exists" exec tmx "$S" fi tmux new -s "$S" "$@" fi ``` tmx — connect to a running `tmux` or list them ```bash #!/bin/bash #screen -x -r "$@" if [ -z "$1" ]; then tmux ls elif [ "$1" = "-1" ]; then tmux ls | cut -f1 -d: else S="$1" shift tmux attach -t "$S" "$@" fi ``` `tmx_completion.stuff` — bash completion for `tmx` ```bash #!/bin/bash _tmx_completions() { COMPREPLY=() local CURWORD="${COMP_WORDS[COMP_CWORD]}" local WORDS=() if tmux list-sessions >& /dev/null; then while read -r line do WORDS+=("$line") done < <(tmux list-sessions | cut -f1 -d:) COMPREPLY=($(compgen -W "${WORDS[*]}" -- "$CURWORD")) else echo "No tmux sessions" fi } complete -F _tmx_completions tmx ``` ## Udisks mounting and unmounting, and related `udm` — for mounting, e.g. `udm c2` to mount `/dev/sdc2`. ```bash #!/bin/bash do_one() { local BD if BD="$(guessbd "$1")"; then fstype="$(lsblk -n -o FSTYPE "$BD")" echo Mounting "$BD" O=() if [ "$fstype" = "ntfs" ]; then O+=(noatime) fi if [ "${#O[@]}" -ne 0 ]; then opts=(-o "$(IFS=, ; echo "${O[*]}")") else opts=() fi udisksctl mount -b "$BD" "${opts[@]}" else echo Cannot mount "$1" fi } for s; do do_one "$s"; done ``` `udu` — for unmounting e.g. `udm c1` to unmount `/dev/sdc1`, or `udm /media/john/mymount` to find the device for it and pass this onto `udisksctl unmount -b` ```bash #!/bin/dash do_one() { local BD if BD="$(guessbd "$1")"; then echo Unmounting "$BD" udisksctl unmount -b "$BD" else echo Cannot unmount "$1" fi } for s; do do_one "$s"; done ``` This latter one uses `guessbd`: e.g `guessbd /dev/sda1` or `guessbd sda1` or `guessbd a1` or `guessbd /media/john/mymount/path/to/file` ```bash #!/bin/bash f() { if [ -b "$1" ]; then echo "$1" exit 0 fi } f "$1" f "/dev/$1" f "/dev/sd$1" if [ -e "$1" ]; then A="$(df "$1" | tail -n1 | cut -f1 -d\ )" f "$A" fi echo "Failed to guess block device for '$1'" exit 1 ``` ## Text file related `skip` — skip n lines in a file (this is before I discovered [AWK](/lang/awk)). ```perl #!/usr/bin/env perl @files = (); $lines_to_skip = 0; for($i=0;$i 0 ) { for $file(@files) { if( $file eq "-" ) { for() { if($j > 0) { $j--; } else { print; } } } else { open F, $file || die "Cannot open $file"; for() { if($j > 0) { $j--; } else { print; } } close F; } } } else { for() { if($j > 0) { $j--; } else { print; } } } ``` in AWK wrapped with bash this is `skip` ```bash #!/bin/bash N="$1" shift awk "NR > $N { print }" "$@" ``` and to select lines between M and N inclusive: `sell` ```bash #!/bin/bash N="$1" shift awk "NR >= $N && NR <= $M { print }" "$@" ``` note the double quotes so that `$M` and `$N` are inserted into the program send to `awk`, that is `sell 3 6` runs `awk 'NR >= 3 && NR <= 6`. Note that the `{ print }` is implicit if left off, so we can even simply the above to ```bash #!/bin/bash N="$1" shift awk "NR >= $N && NR <= $M" "$@" ``` note that `bash` doesn't have an array slice, so we use `shift` to remove the first argument from `argv`.