Kleine Tools für die tägliche Arbeit

Kleine Tools für die tägliche Arbeit

R.Wobst, www.ifw-dresden.de/~wobst/shell.html


*** Navigieren

Problem:

Einfache Lösung (ksh, zsh):

CDPATH=Dietmar:Sven:Axel:Andreas

--> aus dem Directory Telco kann man dann einfach mit "cd Witze" zu Axel wechseln.

Achtung!

"Heiko-Trick": CPATH=.:..:Dietmar...

Damit können auch Nichten und Neffen per "cd" gefunden werden:

$ pwd
/home/Dietmar/Telco
$ cd Windows
/home/Dietmar/Windows
$

Nachteil: Man kann dem Rechner nicht beibringen, dass es sowohl ../Windows als auch ../../Andreas/Windows gibt und man das zweite in der Liste meint.

Ausweg: cd als Funktion programmieren ... umständlich.

In der Praxis kommen "cd ../dir" und "cd ../../dir" am häufigsten vor. Daher nutze ich folgende Shellfunktionen:

CD() { cd ../$1; }
CD2() { cd ../../$1; }

Anwendung:

$ CD Windows
$ CD2 Naideb

Mein CDPATH enthält weder ".." noch "../..".


Problem: mkdir Heiko; cd Heiko

... ewig diese Tipperei! Shellfunktion:

Mkdir() { mkdir $1 && cd $1; }


Problem: Wo bin ich?

"pwd" - klar. Schöner (bis heute!):

PS1='${PWD#$HOME/}> '

Wirkung:

Anwendung:

Dietmar/Telco> pwd
/home/Dietmar/Telco
Dietmar/Telco> cd
/home> cd /etc/init.d
/etc/init.d>

Optional: Rechnernamen integrieren (Vorsicht, lange Prompts!)


*** Transportieren

Problem:

"cd" quer durch den Baum geht schön; ich möchte gerne copyen/moven/linken

Lösung:

Shellskripte Mv, Cp, Ln:

Mv: _mvmvmv_ mv $*
Cp: _mvmvmv_ cp $*
Ln: _mvmvmv_ ln $*

Shellskript _mvmvmv_ (alt):

#!/bin/ksh

old=$PWD # PWD changes during cd cmd=$1; shift # arg1 = command

eval cd \$$# 2>&1 >/dev/null && # here CDPATH is used! { new=$PWD; cd $old list= while [ $# -gt 1 ] ; do list="$list $1"; shift; done $cmd $list $new; }

Anwendung:

Ln humbug preise agb Witze

Beachte: Wenn "Witze" nicht gefunden wird oder ein File ist (also "cd Witze" nicht klappt), richtet das Kommando keinen Schaden an!

Nutze ich mittlerweile weniger.


Problem: Ich möchte gern quer durch den gesamten Baum moven/copyen; CDPATH ist mir zu schwach.

Lösung:

Shellskripte CP, MV, GET:

CP:  ls $* | cpio -o >~p
MV:  CP $* && rm -i $*
GET: cpio -ivmd <~p

Hierbei ist $HOME/p eine Fifo:

$ cd
$ mkfifo p
$ chmod 660 p
$ ls -l p
prw-rw-rw-    1 milchbrat     cdu          0 Apr 18 22:52 p

Anwendung:

vt1:
Dietmar/Telco> CP humbug agb
vt2:
Alex/Witze> GET

Diese Tools nutze ich am laufenden Band zwischen virtuellen Screens (oder xterms, lassen sich aber nicht so schnell per Tastatur selektieren).

Die Fifo nutze ich auch zum Datentausch zwischen Exemplaren des vi/vim (vim kann es direkt per "visual", erfordert aber mehr Tastendrücke).


Problem: Ich möchte mir Files von einem anderen Rechner (sein Name ist in der Shellvariablen p2 enthalten) holen, die an gleicher Stelle liegen.

Lösung: Shellskript get2:

rsh $p2 cd $PWD \; find "$@" -print \| cpio -oc | cpio -ivmd


*** Finden

Problem: Wo finde ich den letzten Brief ans Finanzamt? Hieß vermutlich finanz... oder ...finanz...

Lösung unter Linux:

$ cd
$ locate finanz

Nachteil: Nur unter Linux; wenn doch unter Linux ... ich habe den Brief erst heute früh geschrieben ... wo ist er bloß ...

Lösung:

Shellskript where:

find ${2:-.} -name "*$1*" -print | less

Anwendung:

$ cd
$ where finanz		# oder auch
$ where finanz briefe	# Directory /home/briefe existiert


Problem: Wo ist der Brief (Name? Keine Ahnung!), der Text oder WordPerfect-Format war und eine Passage wie "bitte ich um Verlängerung" (oder hieß es "Aufschub"?) enthielt? Habe den Brief vielleicht schon komprimiert (mit gzip? bzip2? weiß nicht mehr).

--> wie man sieht, ein Problem der Praxis.

Lösung: Mit gut Glück hilft

$ cd
$ zfind "bitte ich um (Verl.*ngerung|Aufschub)"

zfind ist eine schwieriges Shellskript (nur 51 Zeilen), biete Download zusammen mit dem UNIX/open-Artikel aus Heft 5/2000 (Alltägliches Chaos) an: Daunlot hier!

Files werden ggf. mit konfigurierbaren Programmen dekomprimiert, gefundene egrep-Matches invers dargestellt (sehr nützlich), auch in Textverarbeitungsfiles kann man noch suchen; einzelne Filetypen und Directories lassen sich mit der Shellvariablen ZFIND_EXCL ausschließen.

--> Powertool. Arbeitet unter ksh wie unter bash (schwierig!)


Problem: Ich möchte die drei neuesten Files dekomprimieren. Ich weiß zwar, wie sie heißen (ls -t), aber die Namen sind so furchtbar lang, und ich bin so furchtbar faul.

Lösung: Shellskript fl ("file last"). Nicht trivial! Listet die neuesten 20 Files. Gibt man eine Zahl ein, z.B. 5, werden die 5 neuesten File geechoed. Gibt man mehrere Zahlen ein, werden genau diese Einträge selektiert. Gedacht für folgende Verwendung:

$ more $(fl)		# alt: more `fl`
1 -rwxr-xr-x    1 wobst    other        691 Apr 18 23:48 fl
2 drwxr-xr-x    2 wobst    other       1024 Apr 18 21:44 Save
3 -rwxr-xr-x    1 wobst    other        140 Mär 26 18:22 menc
4 -rwxr--r--    1 wobst    other        291 Mär 26 18:00 ab
5 -rwxr-xr-x    1 wobst    other         19 Mär 25 22:18 sig
5 -rwxr-xr-x    1 wobst    other        111 Mär  7 19:00 vic
7 -rwxr-xr-x    1 wobst    other         45 Mär  3 12:04 csc
Enter number(s): 1
...
Daunlot hier!


*** Ordnung machen

Es gibt drei Grundtypen von Nutzern:


Empfehlung für Schlampis: Filenamen, die mit Steuerzeichen beginnen oder aus '\n' bestehen. Durchlaufend nummerierte Corefiles (SVR4). Auspacken von zip-Archiven im Homedirectory.


Empfehlung für Radikale: delold

Löscht alle Files, die älter als ein gewisses Datum sind - das Datum ist nicht bekannt. Skript sieht so aus:

ls -t -l $* | grep -v '^total' | pr -n -t | more

while : do echo -n "delete files from no.: " read nr || exit 1 case "$nr" in ([1-9]|[1-9][0-9]|[1-9][0-9][0-9]) break;; (*) echo "sorry?"; continue;; esac done

ls -t $* | tail +$nr | xargs rm -f

Anwendung:

delold *k
    1	-rwxr-xr-x    1 wobst    other          2 Dez 20 12:46 drink
    2	-rwxr-xr-x    1 wobst    other      16000 Nov 11 21:39 lock
    3	-rwxr--r--    1 wobst    other        409 Jun 16  2000 bck
    4	-rwxr-xr-x    1 wobst    other        438 Jul  6  1998 look
    5	-rwxr-xr-x    1 wobst    other       3384 Jan  5  1998 newwpcrack
delete files from no.: 


Empfehlung für Buchhalter: dirsplit

Sortiert Files, nach Alter geordnet, in Schubfächer (Directories). Gut z.B. zum Archivieren von Mail, nach Jahreszahlen gegliedert (brauche ich oft). Skript:

#!/bin/ksh

typeset -i i=0 n

. ~/util/ask

ls -l -t | grep '^-' | nl -w3 | less

dask "Enter number" n || exit 1 dask "Enter dirname" name || exit 1

[ -d "$name" ] || mkdir $name || exit 1

ls -l -t | grep '^-' | while ((i < n)) do read line && set -- $line && eval mv \$$# $name ((i+=1)) done

In ~/util/ask steht u.a.:

dask()
  {
   typeset word

while : do if [ "$3" ] then print "$1 [$3]: \c" >/dev/tty else print "$1: \c" >/dev/tty fi

read word

if [ -z "$word" -a $# = 2 ] then error "Bitte eine nichtleere Eingabe" >/dev/tty continue else [ "$word" ] || word="$3" fi

break done

eval $2="\$word" return 0 }

Anwendung:

dirsplit
    1	-rwxr-xr-x    1 wobst    other          2 Dez 20 12:46 drink
    2	-rwxr-xr-x    1 wobst    other      16000 Nov 11 21:39 lock
    3	-rwxr--r--    1 wobst    other        409 Jun 16  2000 bck
    4	-rwxr-xr-x    1 wobst    other        438 Jul  6  1998 look
    5	-rwxr-xr-x    1 wobst    other       3384 Jan  5  1998 newwpcrack
Enter number: 2
Enter dirname: 02

... nacheinander Directories 02, 01, ... erzeugen und zuletzt "mv 02/* ." geben. Gewiss eleganter möglicht, reichte aber für die Praxis völlig aus.

Benutze ich viel!


Bequemes Archivieren: makecpio, uncpio

Anwendung:

makecpio dirname[s] [filename[s]]

Beispiel:

$ ls -F
wichtig/ muell/
$ ls -F muell
agb witze/ config prot
$ makecpio muell
...
Press ENTER to remove, ^D if not:
$ ls -F
wichtig/ muell.cpio.gz

makecpio2 komprimiert mit bzip2!

Auspacken: uncpio muell make

Listen: uncpio muell list

(bzw. uncpio2)

Skripte:

makecpio =

case "$0" in
  (*2) CP=bzip2; suff=bz2;;
  (*io) CP=gzip; suff=gz;;
esac

( find $1 -print | cpio -oc | $CP -v >$1.cpio.$suff ) && { print "press ENTER to remove, ^D if not"; read word && rm -r -f $1; } ls -l $1.cpio.$suff

uncpio =

#!/bin/ksh

case "$0" in (*2) CP=bunzip2; suff=bz2;; (*io) CP=gunzip; suff=gz;; esac

arch="$1" [ -f "$arch" ] || arch=$arch.cpio.$suff

[ -f "$arch" ] || { echo "file '$arch' does no exist!\07"; exit 1; } case "$2" in (list) key="t $3 | more";; (make) key="dm" [ "$3" ] && key="dm \"$3\"";; (*) echo "usage: $0 archive_name {list|make} [pattern_or_name]\07" exit 1 ;; esac $CP <$arch | eval cpio -iv$key


Problem: Man hat mir Windoof-Files mit Leerzeichen, Umlauten und Steuerzeichen in mein gatesfreies UNIX-Filesystem geschmuggelt.

Ausweg: deNT

Transformiert die Filenamen in anstaendige (Steuerzeichen --> '_', Umlaute wie ä --> ae) und passt auf, dass kein Name doppelt entsteht (ggf. Anhängen einer Folgenummer).

Skript:

#!/bin/bash
# rename files containing control characters and umlauts in their name
# (C) Reinhard Wobst, @(#) 7.Apr 17:38

typeset -i n

find . -print | while read fn do replace="$(echo -n "$fn" | tr '[\001-\040]' '[_*]')"

replace="$(echo "$replace" | sed -e 's-ä-ae-g' -e 's-ö-oe-g' -e 's-ü-ue-g' \ -e 's-Ä-Ae-g' -e 's-Ö-Oe-g' -e 's-Ü-Ue-g' \ -e 's-ß-ss-g' )"

[ "$replace" = "$fn" ] && continue

[ -a "$replace" ] && { n=0 while [ -a "$replace$n" ] ; do let n+=1; done replace="$replace$n" }

echo renaming \""$fn\"" to \""$replace\"" mv "$fn" "$replace" done | cat -vt | tee ${1:-.deNTprot}

Anwendung:

$ ls
anstaendig anständig Müll mit Leerzeichen
$ rm Müll*
rm: Müll: no such file or directory
$ deNT
renaming "anständig" to "anstaendig1"
renaming "Müll mit Leerzeichen" to "Muell_mit_Leerzeichen"

*** Diverses

Problem: Ich möchte gern "rm" interaktiv haben, außer wenn ich schon selbst einen Schalter gesetzt habe (dann weiß ich, was ich mache).

Shellfunktion rm:

rm() {
  case "$1" in
    -f|-r|-i)	/bin/rm $*;;
    *)		/bin/rm -i $*;;
  esac
}


Problem: Mein Chef taucht ab und zu unerwartet auf.

Lösung: Cheftastenfunktion ccc:

ccc() { clear; cd; }


Problem: Ich möchte tabs durch entsprechende Leerzeichenfolgen ersetzen.

Lösung: Skript detab

#!/bin/ksh

TMP=tmp$$.$(basename $0)

trap 'rm -f $TMP' 0 1 2 3 15

for i in $* do pr -t -e $i >$TMP [[ -n "$TMP" ]] && mv $TMP $i done

Anwendung:

detab agb witz15


Problem: Ich bin zu faul, jedesmal das gleiche Kommando aus der History zu holen; außerdem wird so die History sinnlos vollgekritzelt.

Lösung: Skipt cyc

#!/bin/ksh

print "$*" | IFS='; ' read prompt rest #!! Linux: zsh nehmen

while : do eval $* print "($prompt) >>>>>> Prrresss ENTE or die: \c" >/dev/tty read word

Anwendung:

$ cyc 'make && debug copt'
...
(make) >>>>>> Press ENTE or die:
Abbruch mit Interrupt oder ^D.


Problem: Ich komprimiere Files in einem bestimmten Directory üblicherweise und will nur die noch nicht komprimierten haben.

Lösung: sehr simpel - lz

ls $* *[!z]

Das "$*" reserviert Platz für Schalter:

$ ls -F
humbug oldhumbug.gz witze/
$ lz
humbug oldhumbug.gz

witz: witz1 witz2 ... $ lz -d -F humbug witze/

Anwendung:

$ gzip -v $(lz)

Problem: Ich möchte auf jedem virtuellen Screen eine eigene History haben.

Lösung: Im Profile folgenden Eintrag unterbringen (Linux):

HISTFILE=$(tty | sed -e 's-/-.-g' -e "s-^-$HOME/.hist-")

Anwendung:

$ ls -l .h*
-rw-r--r--   1 wobst    asw          1649 Apr 19 15:47 .hist.dev.pts.0
-rw-r--r--   1 wobst    asw          2326 Apr 19 14:59 .hist.dev.pts.1
-rw-r--r--   1 wobst    asw            15 Mar  3 17:18 .hist.dev.pts.10
-rw-r--r--   1 wobst    asw          2509 Apr 19 14:59 .hist.dev.pts.2
-rw-r--r--   1 wobst    asw          1527 Apr 19 14:59 .hist.dev.pts.3
-rw-r--r--   1 wobst    asw          1830 Apr 18 22:22 .hist.dev.pts.4
-rw-r--r--   1 wobst    asw          1904 Apr 19 00:36 .hist.dev.pts.5
-rw-r--r--   1 wobst    asw          1779 Apr 15 22:58 .hist.dev.pts.6
-rw-r--r--   1 wobst    asw          1870 Apr 15 22:58 .hist.dev.pts.7
-rw-r--r--   1 wobst    asw          1216 Apr 15 22:58 .hist.dev.pts.8
-rw-r--r--   1 wobst    asw           211 Mar  3 22:56 .hist.dev.pts.9


Hier nochmals der Link zur Erinnerung:

www.ifw-dresden.de/~wobst/shell.html