Table of Contents
Shellskripting
Error handling
(this will only work on recent bash
versions)
#!/bin/bash # as this uses bash-isms, better not use #! /bin/sh on_error() { echo "error encountered in $BASH_COMMAND" >&2 } trap on_error ERR # inherit trap on error to functions and subshells set -o errtrace # don't continue after an error: set -o errexit
more sofisticated on_error()
function:
on_error() { case "$1" in "") mailbody="error encountered in '$BASH_COMMAND' on line ${BASH_LINENO[0]}" ;; *) mailbody="$1" ;; esac if tty > /dev/null then echo "ERROR: $mailbody" >&2 else tty || echo "tty failed with $?" mail -s "Probleme mit Notes SIT auf `hostname` in '$0'" $mailto <<EOF $mailbody EOF fi }
safely delete double files
Assuming, you have a complete copy of directory dir
on cdrom, mounted at /cdrom
and want to delete them only after checking if the copy on CD is valid:
find dir -type f -print0 \ | xargs -0 -n 1 -I XXX sh -c "echo 'checking file \"XXX\"' ; diff -- 'XXX' /cdrom/'XXX' && rm -v -- 'XXX' || kill \"$PPID\""
Note: This solution copes even with filenames containg weird characters like $ and linefeeds.
If you want to clean up everything else afterwards without accidently removing left over files (as rm -r
would do) use these commands:
find . -type l -print0 | xargs -0 rm -v find . -type s -print0 | xargs -0 rm -v find . -type p -print0 | xargs -0 rm -v find . -type d -empty -print0 |xargs -0 rmdir -p
Yes, it's paranoid. But I prefere being paranoid over loosing data
And no, this way you won't have a check whether all pipes, sockets and symlinks are the same in both directories. Talking about being paranoid, hmmmm?
Reading config parameters from file
Simple
script:
value=`awk ' $1 == "key" {print $2}' file.conf`
file.conf
:
# key may occur in comments without causing problems key dies_ist_der_wert
will set key
to “dies_ist_der_wert”
Safe
script:
value="`sed -ne 's/^[[:space:]]*key[[:space:]]*\([^#][^[:space:]]*\)[[:space:]]*\(#.*\|\)$/\1/gp' file.conf`"
file.conf
:
# key may occur in comments without causing problems # all text between key and # or end of line without # leading or trailing whitespace is considered # the value (i.e. "dies ist der wert") key dies ist der wert # und dies ist ein Kommentar
Extend PATH and MANPATH in startup scripts
for addpath in ~/bin do if echo "$PATH" | grep -qv "$addpath" then PATH="$PATH:$addpath" fi done if man --path >/dev/null 2>&1 then for addpath in ~/man do MANPATH="`man --path`:$addpath" done fi
Retrieve cn from .ldif file
get_cn() { ( cline=`grep -m 1 '^cn:'` while IFS="" read -r cont do case "$cont" in " "*) cline="$cline""${cont# }" ;; *) break ;; esac done case "$cline" in "cn: "*) echo "${cline#cn: }" ;; "cn::"*) echo "${cline#cn:: }" | base64 -id ;; *) echo "FAILURE" >&2 ; break ;; esac ) < "$1" }
Get process start time
ps --no-headers -o lstart -p "$pid"
Read Passwords from Terminal
read -r -s -p "Password: " pass echo
Use "trap" with inline function
trap '(echo goodbye;echo "and farewell";)' EXIT
Create List of existing and possible aliases
mkaliases() { ( ls /opt/local/bin/ \ | while read item do if alias "$item" > /dev/null then alias $item | sed -e 's:^\(.*\)=\(.*\)$':"alias \1='\2':g" else echo "# alias $item='/opt/local/bin/$item'" fi done alias | sed -E -e "s:^(.*)='?(.*[^'])'?$:alias \1='\2':g" ) \ | sort -u }
Generic Script Template
#! /bin/sh # Let's see which CVS version this is: # $Id: shellscript_template.sh,v 1.4 2008/05/15 14:17:55 pjw Exp $ # set some default: check_arg="foobar" print_usage() { sed -e "s:^ ::g" << EOF # little trick to have HERE document indented This is a lab lubba script to achieve foobar It will skip files starting with [xyz]. Usage: ${0##*/} [-a|-b] [-c <arg1>] <file> [<file> ...] Options: -h : help - print this and exit -v : verbose - print more messages -q : quiet - print less messages -a : add - some explanation what add means -b : block - explain what block means -c <arg1> : check - explain what check means and what arg ist to be given (default "${check_arg}") <file> : file(s) to act on \$Id: shellscript_template.sh,v 1.4 2008/05/15 14:17:55 pjw Exp $ EOF } # # define functions for easy handling of -q (quiet) and -v (verbose) flags echo_noquiet() { [ -z "$be_quiet" ] && echo "$@" ; # "$@" will echo all function args } echo_verbose() { [ -n "$be_verbose" ] && echo "$@" ; # "$@" will echo all function args } # just an example for a function: # (see end of script for invocation) multiply() { # take the two parameters of the fuction call and multiply them # take the two parameters of the fuction call and multiply them let "prod = $1 * $2" echo $prod } # end of function definition # -------------------------------- # start parsing command line arguments # unset vars just in case unset do_add unset do_block unset be_quiet unset be_verbose # init empty array files=() # check whether there are command line args left while [ "$#" -gt 0 ] do case "$1" in "-h") print_usage # print to stdout as this is no error exit # normal exit as the user asked for help ;; "-a") do_add=yes ;; "-b") do_block=yes ;; "-q") be_quiet=yes ;; "-v") be_verbose=yes ;; "-c") check_arg="$2" # use quotes to cope with whitespace shift # do additional shift for parameter ;; "-"*) echo "Unknown Option $1" >&2 # print error to stderr print_usage >&2 # print to stderr as it was not expected exit 42 # return error as it is one ;; *) files=( "${files[@]}" "$1") # build shell arrays of filename to cope with # multiple files even when containing whitespace ;; esac shift # drop first command line argument and shift other ones done # avoid any writes / changes if called without arguments # so if there is no read-only default action, abord: if [ -z "$do_add" -a -z "$do_block" ] then echo "Missing run option" >&2 # print errors to stderr print_usage >&2 # print errors to stderr exit 42 # indicate error my exit code != 0 fi # abord if no files are given (never do any write operation by default) if [ "${#files[@]}" -eq 0 ] then echo "Missing filenames" >&2 # print errors to stderr print_usage >&2 # print errors to stderr exit 42 # indicate error my exit code != 0 fi echo_noquiet "check_arg is $check_arg" # loop over array elements while respecting spaces in one element for file in "${files[@]}" do echo " ========= " echo "handling $file" case "$file" in [xyz]*) echo_noquiet "skipping file '$file' starting with x,y or z" continue # jump to next ;; *) echo_noquiet -n "do something with file" [ -n "$do_block" ] && echo_noquiet -n " while blocking something" [ -n "$do_add" ] && echo_noquiet -n " and add something" echo_noquiet # finish line echo_verbose "tell more about file '$file'" ;; esac done echo echo "end of parsing files" echo echo "this output is generated by a function:" multiply 23 42
Move by year in name with zsh
mkdir 2015 2016 2017 2018 setopt cshnullglob for year in 19?? 20?? do mv -iv _"$year"*.JPG IMG_"$year"*.jpg IMG_OC_"$year"*.jpg PANO_"$year"*.jpg PANO_"$year"*.raw VID_"$year"*.mp4 "$year"/ \ || echo "ERROR on $year" >&2 done
Append to history in script (bash)
- addhist.sh
#! /bin/bash bash -i -c "history -s \"echo 'Super langer Befehlt aus Skript' `date`\"" exit 0
Variant 1
~ $ date Sat Aug 24 14:13:57 CEST 2019 ~ $ addhist.sh ~ $ history -r ~ $ date Sat Aug 24 14:14:11 CEST 2019 ~ $ [Key ↑] ~ $ echo 'Super langer Befehlt aus Skript' Sat Aug 24 14:14:01 CEST 2019
Variant 2
~ $ alias addhist='export=HISTFILE=`mktemp` ; /full/path/to/addhist.sh ; history -r ; rm "$HISTFILE" ; export HISTFIL=~/.bash_history' ~ $ date Sat Aug 24 14:13:57 CEST 2019 ~ $ addhist ~ $ date Sat Aug 24 14:14:11 CEST 2019 ~ $ [Key ↑] ~ $ echo 'Super langer Befehlt aus Skript' Sat Aug 24 14:14:01 CEST 2019