Artikel in Serie "Shells and related Scripting Languages"

~/.bashrc mit Output und scp

Komischerweise führt (auf Debian 5) auch scp die ~/.bashrc aus.

Wenn die ~/.bashrc Ausgaben auf stdout macht, geht davon das SCP-Protokoll kaputt.

1.: alle Ausgaben in der ~/.bashrc nach stderr umleiten (das sollte man so oder so tun, auch vom bildschirmfüllenden Dumps einer /etc/motd o.ä. halte ich übrigens nichts).

2.: alles, was Ausgaben erzeugen kann, in folgendes Konstrukt einbetten:

if /usr/bin/tty &> /dev/null ; then
    echo "Hier könnte Ihre Ausgabe stehen" >&2
fi

Auf diese Weise kann man auch beliebig sinnvolle Sachen mit dem Terminal machen, z.B. es löschen oder zurücksetzen, Optionen setzen usw. Ich z.B. mache in einer Server-bashrc Folgendes:

if /usr/bin/tty &> /dev/null ; then
    shopt -s checkwinsize
    reset
fi

Das löscht zwar bei jedem Login via ssh das aktuelle Terminal, aber dafür stimmt die Anzahl der Spalten auch in eingeschränkten, größenveränderbaren Clients wie z.B. putty.

sed ist auch eine Programmiersprache

Viele machen so etwas:

~$ grep abc | sed 's/x/y/'

Kürzer ist:

~$ sed '/abc/!d;s/x/y/'

Erklärung: Dieses sed-Programm besteht aus zwei durch „;“ voneinander getrennte Kommandos, „d“ und „s///„.

/abc/!“ ist eine Zeilenadressierung durch einen invertierten regulären Ausdruck, d.h. das folgende Kommando wird für alle Zeilen ausgeführt, auf die die ReEx nicht zutrifft. Das damit angewendete Kommando ist „d„, also „Zeile löschen“.

Das zweite Kommando, die Substitution „s///“ wird auf alle verbliebenen Zeilen ausgeführt.

Spass mit „awk“

Heute: wir wollen alle unvollständig deinstallierten Debian-Pakete vollständig löschen (das geht mit „dpkg --purge $Paketname„). Der erste Ansatz lautete:

dpkg -l | grep ^rc | awk '{print $2}' | xargs dpkg --purge

Dies lässt sich vereinfachen zu

dpkg -l | awk '/^rc/{print $2}' | xargs dpkg --purge

Damit ist schon mal das „grep“ gespart. Aber das „xargs“ braucht man auch nicht, sondern verwendet stattdessen die Kommando-Substitution mit „Back-Ticks“ („`„):

dpkg --purge `dpkg -l | awk '/^rc/{print $2}'`

Und das war’s. 🙂

 Minimales „find“ in Bourne Again Shell

#!/bin/sh

_find() {
    echo $1

    test \! -d "$1" && return

    ls -d "$1"/* | while read f ; do
        test "$(readlink -e "$1")" = "$(readlink -e "$f")" && continue

        echo $f
        _find "$f"
    done
}

_find ${1:-.}

 

 GNU find hat keine Option „-older“ …

… aber eine Funktion „-newer“. Die Funktion „-newer“ ist praktisch, um absolute Zeitangaben zum Aufspüren von Dateien zu verwenden. So würde

touch -d "2010/1/1" /tmp/test.newer
find . -type f -a -name 'core.*' -a -newer /tmp/test.newer

alle regulären Dateien mit „core.“ als Anfang des Dateinamens finden, die neuer sind als der 1. Januar 2010, 00:00 Uhr.

Jedoch gibt es (komischerweise) keine Option „-older“, d.h. man kann scheinbar nicht im gleichen Aufruf auch rückwärts eingrenzen, d.h. Dateien aus einem bestimmten absoluten Zeit-Intervall suchen.

Durch kurze Überlegung wird jedoch klar, dass man keine Option „-older“ braucht, weil es ja einen Negations-Operator gibt, der aus „-newer“ („neuer als“) „\! -newer“ (also „nicht neuer als“) macht. Also:

touch -d "2010/1/1" /tmp/test.newer
touch -d "2010/12/31" /tmp/test.older
find . -type f -a -name 'core.*' -a -newer /tmp/test.newer -a \! -newer /tmp/test.older

Und so findet man alle als „core.*“ benannten regulären Dateien aus dem Jahr 2010, wobei genau genommen der Zeitpunkt (timestamp) 1. Januar 2010, 0 Uhr, 0 Minuten und 0 Sekunden fehlt. Will man 100% Abdeckung, muss man die Referenz-Datei für „-newer“ wie folgt anlegen:

touch -d "2009/12/31 23:59:59" /tmp/test.newer

oder

touch -d "2010/01/01 - 1 second" /tmp/test.newer

Done.

Bourne to Bourne Again Shell Forward Compatibility

Introduction

In this article i try to find out, if Bourne Shell scripts are runnable in Bourne Again Shell without modification. If not, i advice on how to modify the code so that it runs on both Shells.

An interpreter for some variant of Bourne Shell is available as an executable /bin/sh on most Linux and UNIX systems. Writing Bourne Shell script has the possible advantage that such a script can work on all these systems with no changes to the code; be it Linux, AIX, FreeBSD, initramfs or a string passed to a C library call system(3), expressions such as

  • echo hello | wc
  • my_pid=$$

or

  • exit 1

would always work.

Recently, i used GNU Bourne Again Shell for scripting, and i wondered if all of my longstandingly Bourne Shell established practices were seamlessly portable. So i did some research, and in this article i highlight some code constructs i found that work in Bourne Shell and do not work as expected in Bourne Again Shell. Code currently interpreted by /bin/sh and containing such constructs must be reviewed if will be interpreted by /bin/bash.

As it stands, i will focus on reserved words and built-ins, brace expansion and pipeline subprocesses.

Protokoll meines Vortrags „Bourne Shell“ bei UUGRN e.V.

Ich hatte am 5.2.2021 die Ehre, einen Vortrag zum Thema „Bourne Shell“ auf der Veranstaltung FIXME der UNIX-User-Group Rhein Neckar, UUGRN e.V. halten zu dürfen. Hier ein Link zu den schriftlichen Aufzeichnungen dazu: https://wiki.uugrn.org/Bourne_Shell

Abseits des Vortrags, den ich auch mit Live-Demos usw. durchgeführt habe, hat die Mitschrift nur begrenzten Nutzen, aber die Linkliste am Ende der Seite ist recht eigenständig. Vor allem den Vortrag von Herrn Bourne aus dem Jahre 2015 empfehle ich für alle, die sich für den geschichtlichen Hintergrund interessieren.

Print XDG Desktop Definition for Application

For an application given by „application name“ or „executable name“, output the corresponding .desktop file, if any:

#!/bin/sh
IFS=":"
xdg_data_dirs=${XDG_DATA_DIRS:-/usr/local/share:/usr/share}
search=$1

for i in $xdg_data_dirs ; do
  a="$i/applications"

  [ -d $a ] && for d in "$a"/*.desktop ; do
    grep -q -e "^Name=.*$search" -e "^Exec=.*$search" "$d" && {
      echo "# $d:"
      grep -Ev '^(Comment|GenericName|Keywords|Name\[)' "$d"
    }
  done
done

To try this code out, save it to /usr/local/bin/xdg-desktop-search, make it executable and test it, for example, as follows:

xdg-desktop-search gnome-terminal