See also my aliases and functions and my bashrc.
Exists
exists
#!/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's : word:
#!/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
#!/bin/bash
sudo apt-get "$@"
ai
#!/bin/bash
# ai
ag install "$@"
aii
#!/bin/dash
ai -y "$@"
asr
#!/bin/bash
apt-cache search "$@"
asrc
#!/bin/bash
apt-get source "$@"
Date and time related
While Python is my goto language for most scripting, Perl starts up much more quickly, so I often use it in preference for simple scripts.
ymd
#!/bin/dash
date +"%Y_%m_%d"
ymdt
#!/bin/dash
date +"%Y_%m_%d__%H_%M_%S"
hms — convert e.g. 1h4m32s to 3872 (number of seconds)
#!/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.
#!/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
#!/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
#!/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 enoughand runssh. Thewasp` command also runs a specified signal command to alert me that the target machine is likely booted.
#!/bin/bash
# Wake, sleep and ssh
H="$1"
if [ -z "$H" ]; then
echo "$0 <hostname> [<wait time=120>]"
exit 1
fi
T="${2-120}"
shift
shift
wakey "$H"
sleepy "$T" && ssh "$H" "$@"
#!/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 <hostname> [<wait time=120>]"
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
#!/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
#!/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
#!/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
#!/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.
#!/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
#!/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
#!/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).
#!/usr/bin/env perl
@files = ();
$lines_to_skip = 0;
for($i=0;$i<scalar(@ARGV);$i++) {
$_ = $ARGV[$i];
if(/^-n(\d+)/) {
$lines_to_skip = $1;
} elsif(/^-n/) {
$i++;
if( $i < scalar(@ARGV) ) {
$lines_to_skip = $ARGV[$i];
} else {
die "Run out of arguments for -n";
}
} else {
push @files,$_;
}
}
$j = $lines_to_skip;
if( scalar(@files) > 0 ) {
for $file(@files)
{
if( $file eq "-" ) {
for(<STDIN>) {
if($j > 0) { $j--; }
else { print; }
}
}
else
{
open F, $file || die "Cannot open $file";
for(<F>) {
if($j > 0) { $j--; }
else { print; }
}
close F;
}
}
} else {
for(<STDIN>) {
if($j > 0) { $j--; }
else { print; }
}
}
in AWK wrapped with bash this is skip
#!/bin/bash
N="$1"
shift
awk "NR > $N { print }" "$@"
and to select lines between M and N inclusive: sell
#!/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
#!/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.