====== 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) ===== * [[https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters|SGR Parameter]] * [[https://en.wikipedia.org/wiki/ANSI_escape_code#24-bit|24-bit Farben]] * [[https://google.com/search?q=color+picker|Color Picker]] 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 ===== [[https://mywiki.wooledge.org/BashGuide/Patterns|BashGuide/Patterns]] ==== 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) ===== [[https://www.cyberciti.biz/faq/unix-linux-bash-while-read-function-define-ifs-delimiter/|Bash / KSH: Define Delimiter (IFS) While Using read Command]] anzeigen cat -etv ===== Remote Procedure Call - RPC ===== * [[https://gist.github.com/derekp7|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). * [[https://gist.github.com/derekp7/9978986|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.__ #!/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 > echo "a=1; b=2; c=3; print \"a=\", a, \" b=\", b, \" c=\", c; print \"\nSumme:\", a + b + c;print \"\n\"" a=1; b=2; c=3; print "a=", a, " b=", b, " c=", c; print "\nSumme:", a + b + c;print "\n" > echo "a=1; b=2; c=3; print \"a=\", a, \" b=\", b, \" c=\", c; print \"\nSumme:\", a + b + c;print \"\n\"" | bc -lq a=1 b=2 c=3 Summe:6 === Zahlen von eine Basis in eine andere Basis umrechnen === ibase = In welcher Basis verstehe ich deine Eingaben? obase = In welcher Basis soll ich die Ergebnisse ausgeben? > echo "ibase=10; obase=2; 255" | bc 11111111 > echo "255" | sed "s/.*/ibase=10; obase=2; &/" | bc 11111111 > vim calc.bc ibase=10; obase=2; > echo "255" | bc calc.bc 11111111 obase = read(); n = read(); print n; print "\n" > for BASIS in 2 3 5 7 8 12 16; do echo "${BASIS} 123" | tr ' ' '\n' | bc -q ./Zahlenbasis.bc; done 1111011 11120 443 234 173 A3 7B === 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 #!/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/]] #!/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, dann muß man aber den ''IFS'' verbiegen, um Dateien mit Leerzeichen verarbeiten zu können: #!/usr/bin/env bash # funktioniert, wie erwartet, aber Dateien mit Leerzeichen kann man so nur mit verbogenem IFS verarbeiten 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: [[find#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: [[Terminaleinstellungen#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 > arrayLaenge=${#ARRAY[@]} > echo "Das Array enthält '${arrayLaenge}' Elemente." Das Array enthält '3' Elemente. > 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!__