Benutzer-Werkzeuge

Webseiten-Werkzeuge


bash

Dies ist eine alte Version des Dokuments!


bash (Bourne SHell / Bourne-Again SHell)

In jedem Unix-Ähnlichen System findet sich die klassische Bourne SHell unter /bin/sh.

Aus diesem Grund scripte ich mit Vorliebe für diese Shell. Dann laufen die Scripte nicht nur auf Linux, sondern auch auf NetBSD, FreeBSD, Solaris, AIX und anderen Unix-Ähnlichen Betriebssystemen.

http://www.linux-praxis.de/linux1/shell2_7.html

http://www.netzmafia.de/skripten/unix/unix2.html

farbige Textausgabe (24bit Farben)

ESC[38;2;⟨r⟩;⟨g⟩;⟨b⟩m   # RGB foreground color
ESC[48;2;⟨r⟩;⟨g⟩;⟨b⟩m   # RGB background color
ESC[0m                # reset
  • ESC: Darstellung mit \033
  • 1. Paramter: 38 (Vordergrundfarbe) und 48 (Hintergrundfarbe)
  • 2. Paramter: 2 (RGB Format verwenden)
  • 3/4/5. Paramter: rot/grün/blau Werte mit jeweils 8-bit in dezimal (0 - 255), weil 24 / 3 = 8-bit

Beispiel um Syntax zu vereinfachen

Variablen:

  • TEXT: Text der printf übergeben wird
  • XY
    • X: Vordergrund (F) oder Hintergrund (B)
    • Y: rot (R), grün (G) oder blau (B)

Umgebungsvariable als Shortcut

PRINT_COLOR='eval printf "\033[39m\033[49m"$(test ${#FG_RGB[@]} -eq 3 && printf "\033[38;2;${FG_RGB[0]};${FG_RGB[1]};${FG_RGB[2]}m")""$(test ${#BG_RGB[@]} -eq 3 && printf "\033[48;2;${BG_RGB[0]};${BG_RGB[1]};${BG_RGB[2]}m")"${TEXT}\033[0m"; unset TEXT FG_RGB BG_RGB;'

Nutzung

TEXT="Hello," FG_RGB=(255 255 255) BG_RGB=(0 0 0); $PRINT_COLOR && TEXT=" "; $PRINT_COLOR && TEXT="World!" FG_RGB=(0 0 0) BG_RGB=(255 255 255); $PRINT_COLOR && echo
TEXT="Hello World!" FG_RGB=(0 0 0) BG_RGB=(255 255 255); $PRINT_COLOR && echo
TEXT="Hello World!" FG_RGB=(127 0 255); $PRINT_COLOR && echo
TEXT="Hello World!" BG_RGB=(127 0 255); $PRINT_COLOR && echo
TEXT="Hello World!"; $PRINT_COLOR && echo

Achtung: Die Semikolon (;) sind wichtig!

Bash - Pattern

Klammererweiterung

Dann gibt es die Klammererweiterung (Brace-Expansion). Klammererweiterung passt technisch nicht in die Kategorie der Muster, ist aber ähnlich. Globs werden nur auf tatsächliche Dateinamen erweitert, aber Klammererweiterungen werden auf jede mögliche Permutation ihres Inhalts erweitert. So funktionieren sie:

$ echo th{e,a}n
then than
$ echo {/home/*,/root}/.*profile
/home/axxo/.bash_profile /home/lhunath/.profile /root/.bash_profile /root/.profile
$ echo {1..9}
1 2 3 4 5 6 7 8 9
$ echo {0,1}{0..9}
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19

Die Klammererweiterung wird durch eine Liste von Wörtern ersetzt, genau wie ein Glob. Diese Wörter sind jedoch nicht unbedingt Dateinamen, und sie sind nicht sortiert (als wären sie vorher gekommen).

Die Klammererweiterung erfolgt vor der Dateinamenerweiterung. Im zweiten echo-Befehl oben haben wir eine Kombination aus Klammererweiterung und Globs verwendet. Die Klammererweiterung geht zuerst und wir erhalten:

$ echo /home/*/.*profile /root/.*profile

Nach der Klammererweiterung werden die Globs erweitert und wir erhalten als Endergebnis die Dateinamen.

Klammererweiterungen können nur verwendet werden, um Wortlisten zu generieren. Sie können nicht für den Mustervergleich verwendet werden.

Es gibt einige interessante und nicht sehr intuitive Unterschiede zwischen den Bereichen in Zeichenklassen wie [a-z] und Klammererweiterung. Zum Beispiel erlaubt die Klammererweiterung das Rückwärtszählen, wie man bei {5..1} oder sogar {b..Y} sehen kann, während [5-1] nicht durch die Shell erweitert wird. [b-Y] kann je nach Gebietsschema erweitert werden oder nicht. Außerdem ignorieren Zeichenbereiche in Klammererweiterungen Gebietsschemavariablen wie LANG und LC_COLLATE und verwenden immer die ASCII-Reihenfolge. Globbing hingegen wird durch die Spracheinstellungen beeinflusst. Das folgende Fragment ist ein Beispiel für das Herunterzählen und die Anzeige von Zeichen in der Reihenfolge ihrer ASCII-Codes:

$ echo {b..Y}
b a ` _ ^ ]  [ Z Y

Feldtrennzeichen - Delimiter (IFS)

Remote Procedure Call - RPC

  • RPC mit BASH - Let's say you have a Bash shell script, and you need to run a series of operations on another system (such as via ssh).
    • rpcsh - So, to solve this, you can use a technique called rpcsh – rpc in shell script…

automatische Ersetzung im Dateinamen

Hier wird die Dateiendung ".text" in ".txt" getauscht:

for Datei in *.mts; do mv -v "${Datei}" ${Datei%.text}.txt; done
ffmpeg -i 00001.text 00001.txt

Optionen

> man bash
Laut Doku werden bei "$*" die Elemente durch $IFS (meisten der Zeilenumbruch) getrennt behandelt.
Laut Doku werden bei "$@" die Elemente durch Leerzeichen getrennt behandelt.
In meinen Test konnte ich bei beiden nur einer Erweiterung mit Leerzeichen erkennen.

Ich konnte keinen Unterschied zwischen "$*" und "$@" feststellen.

/tmp/test.sh
#!/bin/bash
 
echo '# 1: "$*"'
echo "$*"
echo '# 2: $*'
echo $*
 
echo '# 3: "$@"'
echo "$@"
echo '# 4: $@'
echo $@
 
echo '# 5: "$#"'
echo "$#"
echo '# 6: $#'
echo $#
echo '# 7: #'
 
echo "$1"
echo "$2"
echo "$3"
echo "$4"
echo "$5"
echo "#------------------------------------------------------------------------------#"
echo $1
echo $2
echo $3
echo $4
echo $5
> /tmp/test.sh 1 2 3 4 "5 6 7 8"
# 1: "$*"
1 2 3 4 5 6 7 8
# 2: $*
1 2 3 4 5 6 7 8
# 3: "$@"
1 2 3 4 5 6 7 8
# 4: $@
1 2 3 4 5 6 7 8
# 5: "$#"
5
# 6: $#
5
# 7: #
1
2
3
4
5 6 7 8
#------------------------------------------------------------------------------#
1
2
3
4
5 6 7 8

Funktionen

In der Bash wird eine Funktion fast so gebaut wie in "C".

Alle Variablen, die außerhalb der Funktion definiert wurden, sind auch in der Funktion verwendbar:

#!/usr/bin/env bash
VARIABLE="01"
funktionx()
{
        echo "VARIABLE=${VARIABLE}"
}
funktionx

Wird eine Skript-Datei mit z.B. zwei Parametern aufgerufen, dann sind bekanntlich diese vier übergebenen Parameter in der Datei als "$1" und "$2" verwendbar.

Genauso verhält es sich mit den Parametern bei den Funktionen:

#!/usr/bin/env bash
NACHNAME="${1}"
VORNAME="${2}"
funktiony()
{
        echo "
        FARBE='${1}'
        ELEMENT='${2}'
        VORNAME='${3}'
        NACHNAME='${4}'
        "
}
funktiony schwarze erde ${VORNAME} ${NACHNAME}

Hier sind die Parameter, die der Funktion übergeben werden, innerhalb der Funktion auch wieder als “$1“, “$2“, … verwendbar.

Entscheidungen

Bei Entscheideidungen muss man unterscheiden, ob man Zahlen mit oder ohne Kommastellen als Vergleich verwenden möchte!

Die "if"-Anweisung in der BASH kann nicht mit Kommastellen umgehen, in soeinem Fall muss man dann auf AWK zurückgreifen.

BASH

if [ "${1}" < 1 ] ; then
      GR=klein
elif [ "${1}" < 2 ] ; then
      GR=mittel
elif [ "${1}" < 3 ] ; then
      GR=gross
else
      GR="sehr gross"
fi

echo "${GR}"

AWK

# echo "1.5" | awk '{if ($1 < 3) GR="gross" ; if ($1 < 2) GR="mittel" ; if ($1 < 1) GR="klein" ; print $1"="GR}'
1.5=mittel
# echo "1.5" | awk '{if ($1 < 3) GR=3 ; if ($1 < 2) GR=2 ; if ($1 < 1) GR=1 ; print GR}'
2

Zählschleifen

BASH

Unter Linux ist die GNU Bourne-Again SHell unter /bin/bash die Standard-Shell, sie ist in erster Linie für die Konsolenarbeit verbesert worden. So verfühgt sie im Gegensatz zur Bourne SHell über eine Historie.

Die GNU Bourne-Again SHell verfühgt aber auch über zusätzliche Funktionen, die für die Arbeit mit Scripten wichtig sind.

Zum Beispiel funktioniert der kleine Time-Out-Zähler nur in der GNU Bourne-Again SHell, nicht jedoch in der Bourne SHell:

#!/usr/bin/env bash
WARTEZEIT="10"
while [ ! -e /tmp/datei ]
do
        sleep 1
        if [ "${WARTEZEIT}" -gt "0" ] ; then
                WARTEZEIT=$((WARTEZEIT - 1))
        else
                touch /tmp/datei
        fi
done

die folgenden beiden Zählschleifen funktionieren nur mit einer BASH, nicht mit einer "echten sh", wie z.B. die von FreeBSD (in Linux ist die sh ein Link auf die BASH):

> i=0 ; while (("$i"<"10")) ; do i="$((${i}+1))" ; echo "${i}" ; done
1
2
3
4
5
6
7
8
9
10
> i=10;while (("$i">"0")); do echo "${i}"; i="$((${i}-1))"; done
10
9
8
7
6
5
4
3
2
1

sh

Für die Bourne SHell muss das Kommando bc das rechnen im Script übernehmen:

#!/bin/sh
WARTEZEIT="10"
while [ ! -e /tmp/datei ]
do
        sleep 1
        if [ "${WARTEZEIT}" -gt "0" ] ; then
                WARTEZEIT="$(echo "${WARTEZEIT} - 1" | bc)"
        else
                touch /tmp/datei
        fi
done

oder das Kommando awk:

#!/bin/sh
WARTEZEIT="10"
while [ ! -e /tmp/datei ]
do
        sleep 1
        if [ "${WARTEZEIT}" -gt "0" ] ; then
                WARTEZEIT="$(echo "${WARTEZEIT}" | awk '{print $1-1}')"
        else
                touch /tmp/datei
        fi
done

bc

natürlicher Logarithmus aus 5:

> echo "l(5)" | bc -l
1.60943791243410037460

natürlicher Logarithmus aus 20:

> echo "l(20)" | bc -l
2.99573227355399099343

dezimaler Logarithmus 1000:

> echo "l(1000)/l(10)" | bc -l
3.00000000000000000000

binärer Logarithmus aus 8:

> echo "l(8)/l(2)" | bc -l
3.00000000000000000002

Zähler mit while und bc

Dieser Zähler ist zwar etwas umständlich, dafür funktioniert er aber auf jedem aktuellen Uni*-Like Betriebssystem, auf dem es das Programm "bc" gibt. ;-)

Man muss nur darauf achten, dass bc installiert ist. Das kleine Rechenprogramm gibt es aber schon lange für alle Uni*-Like Betriebssysteme.

vorwärts
# i=0;while(("$i"<"10"));do i="$(echo "1+$i"|bc)";echo "$i";done
1
2
3
4
5
6
7
8
9
10
rückwärts
# i=10;while(("$i">"0"));do i="$(echo "$i-1"|bc)";echo "$i";done 
9
8
7
6
5
4
3
2
1
0

Zähler mit awk

Sonst kann man auch awk als Rechenknecht verwenden. AWK gibt es für alle Uni*-Like Betriebssysteme und ist eigentlich immer schon mit drauf.

vorwärts
# echo "10"|awk '{for(i=0;i<$1;i++) {print i}}'
0
1
2
3
4
5
6
7
8
9
# echo "10"|awk '{for(i=1;i<=$1;i++) {print i}}'
1
2
3
4
5
6
7
8
9
10

hier in Zweierschritten:

# echo "10"|awk '{for(i=1;i<=$1;i++) {if( i % 2 == 0) {print i}}}'
2
4
6
8
10

und jetzt mit fester Zahlenlänge:

# echo "10"|awk '{for(i=1;i<=$1;i++){printf("%.8u\n",i)}}'
00000001
00000002
00000003
00000004
00000005
00000006
00000007
00000008
00000009
00000010
rückwärts
# i=10 ; while (( "$i" > "0")) ; do i="$(echo "$i" | awk '{print $1-1}')" ; echo "$i" ; done 
9
8
7
6
5
4
3
2
1
0
# i=10 ; until [ "$i" = "0" ] ; do i="$(echo "$i" | awk '{print $1-1}')" ; echo "$i" ; done 
9
8
7
6
5
4
3
2
1
0

Oder man rechnet gleich komplett nur mit dem awk.

# echo "10"|awk '{for(i=$1;0<i;i--){print i}}'
10
9
8
7
6
5
4
3
2
1

und jetzt mit fester Zahlenlänge:

# echo "10"|awk '{for(i=$1;0<i;i--){printf("%.8u\n",i)}}'
00000010
00000009
00000008
00000007
00000006
00000005
00000004
00000003
00000002
00000001

Zähler mit seq

Dieser Befehl ist sehr bequem aber leider gibt es ihn nicht auf jedem System und ausserdem ist er in Scripten manchmal recht zickig.

vorwärts
# seq 1 10
1
2
3
4
5
6
7
8
9
10
rückwärts
# seq 1 10 | tac
10
9
8
7
6
5
4
3
2
1
Ausgabe mit fester Breite
# seq 1 10 | sed 's/.*/000000&/' | rev | cut -c 1-6 | rev
000001
000002
000003
000004
000005
000006
000007
000008
000009
000010

ewig warten bis ein Ereignis eintrifft

In diesem Fall wartet die Schleife darauf, dass ein Rechner wieder über das Netz erreichbar ist.

#!/bin/sh

START="1"
while [ "$?" != "0" -o "${START}" = "1" ]
do
        START="0"
        sleep 1
        ping -c1 192.168.0.100
done
DATEI="diese_datei_wird_gleich_angelegt.tgz"
while [ -r "${DATEI}" ]
do
        sleep 1
done

nur begrenzte Zeit warten bis ein Ereignis eintrifft

nur "180" Sekunden lang auf die Datei warten:

#!/bin/sh
 
DATEI="diese_datei_wird_gleich_angelegt.tgz"
ZAEHLER="180"
while [ ! -r "${DATEI}" -a "${ZAEHLER}" -gt "0" ]
do
        ZAEHLER="$(echo "${ZAEHLER}"|awk '{print $1-1}')"
        sleep 1
done

oder so:

#!/bin/sh
 
DATEI="diese_datei_wird_gleich_angelegt.tgz"
ZAEHLER="180"
while [ "${ZAEHLER}" -gt "0" ]
do
        ZAEHLER="$(echo "${ZAEHLER}"|awk '{print $1-1}')"
        if [ -r "${DATEI}" ] ; then
                ZAEHLER="0"
        fi
        sleep 1
done

maximal 180 Sekunden lang wartet, bis die Datenbank erreichbar ist:

#!/usr/bin/env bash
 
MYSQL_LOGIN="--defaults-file=/root/.my.cnf"
ZAEHLER="180"
 
while [ "${ZAEHLER}" -gt "0" ]
do
        ZAEHLER="$(echo "${ZAEHLER}"|awk '{print $1-1}')"
        if [ "$(mysqlshow ${MYSQL_LOGIN})" ] ; then
                ZAEHLER="0"
        fi
        sleep 1
done

Parameter mit BASH

#!/usr/bin/env bash

case "${1}" in
        [Ee][Ii][Nn])
                        SCHALTEN="ein"
                        shift
                        ;;
        [Aa][Uu][Ss])
                        SCHALTEN="aus"
                        shift
                        ;;
        *)
                        echo "${0} ein"
                        echo "${0} aus"
                        shift
                        exit 1
                        ;;
esac
#!/usr/bin/env bash

while [ "${#}" -ne "0" ]; do
        case "${1}" in
                -a)
                        OPTION_A=${2}
                        shift
                        ;;
                -b)
                        OPTION_B=${2}
                        shift
                        ;;
                -h)
                        echo "
                        HILFE:
                        ${0} [Option]
                        -a [wert a]
                        -b [wert b]
                        "
                        exit 1
                        ;;
                *)
                        if [ "$(echo "${1}"|egrep '^-')" ] ; then
                                echo "Der Parameter '${1}' wird nicht unterstützt!"
                        fi
                        shift
                        ;;
        esac
done

echo "
OPTION_A=${OPTION_A}
OPTION_B=${OPTION_B}
"

Soll eine Fehlermeldung (unbound variable) erscheinen, wenn nicht alle Parameter übergeben wurden, dann muss "BASH" (in der ersten Zeile) mit der Optionen -u versehen werden.

statt so

#!/usr/bin/env bash

kann es zum Beispiel so aussehen:

#!/bin/bash -eu

Ein Beispiel mit getopts

https://linuxhint.com/getopts-usage-example-linux/

/tmp/beispiel_getopts.sh
#!/bin/sh
 
# wenn dieses Skript ohne Parameter aufgerufen wird
# dann wird eine Hilfe ausgegeben
if [ "x${1}" = x ] ; then
        echo "${0} -l -u [User] -h [Host]"
        exit 1
fi
 
# der ":" besagt, dass dahinter ein Parameter erwartet wird
while getopts "u: h: l" OPTION
do
        case ${OPTION} in
        u)
                USER_NAME="${OPTARG}"
                ;;
        h)
                HOST_NAME="${OPTARG}"
                ;;
        l)
                STATUS="live"
                ;;
        esac
done
 
### Der Parameter, der als Naechstes bearbeitet werden soll, wird bei einer Shell in der automatischen Variable OPTIND verwaltet.
### Der Wert dieser Variablen betraegt beim Aufruf erst einmal 1 – wird aber bei jedem weiteren getopts-Aufruf um 1 erhoeht.
### Wenn eine Kommandozeile mehrfach eingelesen werden soll, muss der Index manuell zurueckgesetzt werden.
#OPTIND=1
 
echo "
OPTIND='${OPTIND}'
USER_NAME='${USER_NAME}'
HOST_NAME='${HOST_NAME}'
STATUS='${STATUS}'
"
> /tmp/beispiel_getopts.sh -l -u abc -h system

OPTIND='6'
USER_NAME='abc'
HOST_NAME='system'
STATUS='live'

while ohne sub-shell

https://de.linux-console.net/?p=19969

Auf die Variablen, die in einer gewöhnlichen while-Schleife gesetzt werden, hat man ausserhalb der Schleife keinen Zugriff, da die while-Schleife gewöhnlich mit einer Pipe gefüttert wird. Wegen der Pipe wird aber eine eigene Shell geöffnet und mit ihrem Ende enden auch die Variablen in ihr.

Deshalb ist die Ausgabe dieses Skripts auch leer:

#!/usr/bin/env bash
 
VAR=""
echo "Test" | while read ZEILE
do
        VAR="${ZEILE}"
done
 
echo "VAR='${VAR}'"

Dieses Skript liefert dagegen eine Ausgabe:

#!/usr/bin/env bash
 
VAR=""
while read ZEILE
do
        VAR="${ZEILE}"
done < <(echo "Test")
 
echo "VAR='${VAR}'"

Keine Pipe, keine Subshell, keine gestobenen Variablen. ;-)

Allserdings funktioniert das auch nicht in vollem Umfang:

#!/usr/bin/env bash
 
VAR1=""
VAR2=""
VAR3=""
while read SPALTE1 SPALTE2 SPALTE3
do
        VAR1="${SPALTE1}"
        VAR2="${SPALTE2}"
        VAR3="${SPALTE3}"
done < <(echo "Test")
 
echo "
VAR1='${VAR1}'
VAR2='${VAR2}'
VAR3='${VAR3}'
"

Wenn die Zeile gleichzeitig noch in Spalten aufgetrennt werden soll, was ja eigentlich geht, dann versagt diese Methode… :-(

Probleme mit der while-Schleife

führt man in einer WHILE-Schleife komplexe Kommandos aus, dann wird oft nur das erste Kommando ausgeführt oder jedes 2. Kommando nicht ausgeführt:

#!/usr/bin/env bash
 
# bei jeder 2. Zeile (Kommando) fehlen an Zeilenanfang, mehrere Zeichen, deshalb bricht es ab
 
find . -type f -iname "Air Strike_*.mkv" | sed 's|^[.]/||' | sort | while read FILE
do
        ffmpeg -i "${FILE}" -c:v mpeg4 -b:v 8192k -profile:v 15 -level 5 -g 300 -c:a ac3 -f avi -y "Test_${FILE}".avi
done

Als Lösung kann man entweder den Umweg über eine Zwischendatei gehen, die dann als statisches Skript ausgeführt wird:

#!/usr/bin/env bash
 
# funktioniert, wie erwartet
 
find . -type f -iname "Air Strike_*.mkv" | sed 's|^[.]/||' | sort | while read FILE
do
        Iecho "ffmpeg -i \"${FILE}\" -c:v mpeg4 -b:v 8192k -profile:v 15 -level 5 -g 300 -c:a ac3 -f avi -y \"Test_${FILE}\".avi"
done > /tmp/statisches_Zwischen-Skript.sh
bash /tmp/statisches_Zwischen-Skript.sh
rm -f /tmp/statisches_Zwischen-Skript.sh

Oder man wählt die FOR-Schleife, muß dann aber den IFS verbiegen:

#!/usr/bin/env bash
 
# funktioniert, wie erwartet, aber nur mit verbogenem IFS
 
IFS=$'\n'
DATEIEN=($(find . -type f -iname "Air Strike_*.mkv" | sed 's|^[.]/||' | sort))
for FILE in "${DATEIEN[@]}"
do
        ffmpeg -i "${FILE}" -c:v mpeg4 -b:v 8192k -profile:v 15 -level 5 -g 300 -c:a ac3 -f avi -y "Test_${FILE}".avi
done

leere Unterverzeichnisse löschen

rmdir löscht nur leere Verzeichnisse, bei vollen wird eine Fehlermeldung ausgegeben:

> rmdir Verzeichnis/

ohne Fehlermeldung kann man das so machen:

> DIR="Verzeichnis/"; [ -d $DIR ] && [ $(ls -l $DIR | wc -l) -eq 1  ] && rmdir $DIR ||  :

leeren Unterverzeichnisse kann man mit find auch rekursiv löschen: leeren_unterverzeichnisse_loeschen

Alphabet generieren

> echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z

cooler Prompt

Wenn man als privilegierter Benutzer angemeldet ist, ist der Benutzername im Prompt pink, ist man als unprivilegierter Benutzer angemeldet ist, ist der Benutzername im Prompt hellblau. Der Hostname ist immer dunkelblau.

Andere Farben kann man nach belieben konfigurieren, die Farbtabelle ist u.a. hier hinterlegt: kommandozeile_cli

Die erste Zahl vor der Uhrzeit zeigt den Exit-Code an:

[root@rechner]---------------------------------------------------[0]-[0]-[14:14]
[~]# false

[root@rechner]---------------------------------------------------[1]-[0]-[14:14]
[~]# 

Die zweite Zahl gibt die Anzahl der im Hintergrund laufenden Job's an.

[root@rechner]---------------------------------------------------[0]-[0]-[14:15]
[~]# sleep 60
^Z
[1]+  Angehalten              sleep 60

[root@rechner]-------------------------------------------------[148]-[1]-[14:15]
[~]# bg 1
[1]+ sleep 60 &

[root@rechner]---------------------------------------------------[0]-[1]-[14:15]
[~]# 

[root@rechner]---------------------------------------------------[0]-[1]-[14:17]
[~]# 
[1]+  Fertig                  sleep 60

[root@rechner]---------------------------------------------------[0]-[1]-[14:17]
[~]# 

[root@rechner]---------------------------------------------------[0]-[0]-[14:17]
[~]# 

mach_bash_prompt.sh

Dieses Skript nimmt den entsprechenden Eintrag in der datei ~/.bashrc vor und erstellt die Datei~/.bash_prompt.

#!/usr/bin/env bash

echo "
[ -f ~/.bash_prompt ] && . ~/.bash_prompt || PS1='\\[\\033[1m\\]\\u@\\h\\[\\033[0m\\]:\\w\\$ '
export EDITOR=vi
" >> ~/.bashrc

(echo '
PROMPT_COLORS=${PROMPT_COLORS:-0}

prompt_command() {
    local EC="$?"
    
    case "$PROMPT_COLORS" in
        *)
            local CUSER="1;36"
            local CHOST="1;34"
            local CEC="1;34"
            local CJOBS="1;34"
            local CTIME="1;36"
            local CDIR="0;35"
            local CAT="0"
            local CWARN="1;35"
            local CDEF="0"
            ;;
    esac
    
    local R="\[\e[${CDEF}m\]"
    
    local USER=$(whoami)
    local HOSTNAME=$(hostname)
    local JOBS=$(jobs|wc -l)
    local PWD=$(pwd)
    local TIME="$(date +"%F %T")"
    
    PWD=${PWD/#$HOME/"~"}
    [ "${TERM:0:5}" == "xterm" ] && echo -en "\033]0;$USER@$HOSTNAME:$PWD\007"
    
    local TMP="[$USER@$HOSTNAME][$EC] [$JOBS] [$TIME]"
    [ -n "$SSH_CONNECTION" ] && TMP="$TMP [ssh]"

    local SP="-"
    [ ${#TMP} -gt 0 ] && local SPACER=$(printf "%$((COLUMNS - ${#TMP}))s") || local SPACER=" "
    SPACER=${SPACER// /$SP}
    
    [ $EC -gt 0 ] && EC="\[\e[${CWARN}m\]$EC" || EC="\[\e[${CEC}m\]$EC"
    [ $JOBS -gt 0 ] && JOBS="\[\e[${CWARN}m\]$JOBS" || JOBS="\[\e[${CEC}m\]$JOBS"
    [ $UID -eq 0 ] && USER="\[\e[${CWARN}m\]$USER" || USER="\[\e[${CUSER}m\]$USER"
    [ -n "$SSH_CONNECTION" ] && HOSTNAME="\[\e[${CHOST}m\]$HOSTNAME$R]$SP[\[\e[${CWARN}m\]ssh" || HOSTNAME="\[\e[${CHOST}m\]$HOSTNAME"
    
    PS1="
$R[$USER\[\e[${CAT}m\]@$HOSTNAME$R]$SPACER\
[$EC$R]$SP[$JOBS$R]$SP[\[\e[${CTIME}m\]$TIME$R]
[\[\e[${CDIR}m\]$PWD$R]\\\$\[\e[0m\] "
}

PROMPT_COMMAND=prompt_command

[ "${TERM:0:6}" == "screen" ] && {
    set_screen_title() {
        while [ 1 ] ; do
            case "$1" in
                sudo|prompt_command|-*) shift ;;
                *) break ;;
            esac
        done
        echo -en "\033k${1:-bash}\033\\"
    }
'
echo "    trap 'set_screen_title \$BASH_COMMAND' DEBUG
}
") > ~/.bash_prompt

~/.bashrc

Dieser Eintrag überprüft, ob unsere spezielle BASH-Prompt-Datei vorhanden ist und bindet sie ein, wenn es sie hier gibt.

[ -f ~/.bash_prompt ] && . ~/.bash_prompt || PS1='\[\033[1m\]\u@\h\[\033[0m\]:\w\$ '
export EDITOR=vi

~/.bash_prompt

Diese Datei enthält die speziellen Einstellungen für unseren coolen Prompt.

PROMPT_COLORS=${PROMPT_COLORS:-0}

prompt_command() {
    local EC="$?"
    
    case "$PROMPT_COLORS" in
        *)
            local CUSER="1;36"
            local CHOST="1;34"
            local CEC="1;34"
            local CJOBS="1;34"
            local CTIME="1;36"
            local CDIR="0;35"
            local CAT="0"
            local CWARN="1;35"
            local CDEF="0"
            ;;
    esac
    
    local R="\[\e[${CDEF}m\]"
    
    local USER=$(whoami)
    local HOSTNAME=$(hostname)
    local JOBS=$(jobs|wc -l)
    local PWD=$(pwd)
    local TIME=$(date +%H:%M)
    
    PWD=${PWD/#$HOME/"~"}
    [ "${TERM:0:5}" == "xterm" ] && echo -en "\033]0;$USER@$HOSTNAME:$PWD\007"
    
    local TMP="[$USER@$HOSTNAME][$EC] [$JOBS] [$TIME]"
    [ -n "$SSH_CONNECTION" ] && TMP="$TMP [ssh]"

    local SP="-"
    [ ${#TMP} -gt 0 ] && local SPACER=$(printf "%$((COLUMNS - ${#TMP}))s") || local SPACER=" "
    SPACER=${SPACER// /$SP}
    
    [ $EC -gt 0 ] && EC="\[\e[${CWARN}m\]$EC" || EC="\[\e[${CEC}m\]$EC"
    [ $JOBS -gt 0 ] && JOBS="\[\e[${CWARN}m\]$JOBS" || JOBS="\[\e[${CEC}m\]$JOBS"
    [ $UID -eq 0 ] && USER="\[\e[${CWARN}m\]$USER" || USER="\[\e[${CUSER}m\]$USER"
    [ -n "$SSH_CONNECTION" ] && HOSTNAME="\[\e[${CHOST}m\]$HOSTNAME$R]$SP[\[\e[${CWARN}m\]ssh" || HOSTNAME="\[\e[${CHOST}m\]$HOSTNAME"
    
    PS1="
$R[$USER\[\e[${CAT}m\]@$HOSTNAME$R]$SPACER\
[$EC$R]$SP[$JOBS$R]$SP[\[\e[${CTIME}m\]$TIME$R]
[\[\e[${CDIR}m\]$PWD$R]\\\$\[\e[0m\] "
}

PROMPT_COMMAND=prompt_command

[ "${TERM:0:6}" == "screen" ] && {
    set_screen_title() {
        while [ 1 ] ; do
            case "$1" in
                sudo|prompt_command|-*) shift ;;
                *) break ;;
            esac
        done
        echo -en "\033k${1:-bash}\033\\"
    }
    trap 'set_screen_title $BASH_COMMAND' DEBUG
}

Array

Array (also mit Index): ${array[0]},     ${array[1]}
Map   (also mit Key)  : ${array["one"]}, ${array["two"]}

Array ist wie eine Liste, in der jedes Element ein Index hat und Map ist einfach eine Liste von Key-Value-Pairs. Theoretisch ist ersteres eine Map, in der der Key ein Integer ist, der bei 0 startet.

mit explizitem Index

ReadArray

> readarray -t testarray < <(find Videos/ -type f)
> echo "${testarray[0]}"
> echo "${testarray[1]}"
> find Videos/ -type f > /tmp/test.txt
> readarray -t testarray < /tmp/test.txt
> echo "${testarray[0]}"
> echo "${testarray[1]}"

mit implizitem Index

> export ARRAY=(127 0 255)
> echo ${ARRAY[@]}
127 0 255
> echo ${#ARRAY[@]}
3
> echo ${#ARRAY[0]}
127
> echo ${#ARRAY[1]}
0
> echo ${#ARRAY[2]}
255
> testarray=($(find Videos/ -type f))
> echo "${testarray[0]}"
> echo "${testarray[1]}"

ACHTUNG!

Es funktioniert mit testarray="$(find Videos/ -type f)" und testarray="($(find Videos/ -type f))" anders und der Index ist, bei diesen beiden, nicht das, was man erwartet!

/home/http/wiki/data/attic/bash.1714237002.txt · Zuletzt geändert: von manfred