Dup Ver Goto 📝

simple-commands-1

PT2/linux/shell does not exist
To
467 lines, 1626 words, 9811 chars Page 'simple-commands-1' does not exist.

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.