skripting-section:skripting-grundlagen:bash-grundlagen

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
skripting-section:skripting-grundlagen:bash-grundlagen [2019/05/21 10:07] michaelskripting-section:skripting-grundlagen:bash-grundlagen [2019/10/22 08:45] (current) michael
Line 5: Line 5:
 Skripte, die für jeden Nutzer zugänglich sein sollen kann man in ''/usr/bin/'' oder ''/usr/local/bin/'' speichern. Skripte, die administrative Eingriffe ausführen, können in ''/usr/sbin/'' gespeichert werden. Skripte, die für jeden Nutzer zugänglich sein sollen kann man in ''/usr/bin/'' oder ''/usr/local/bin/'' speichern. Skripte, die administrative Eingriffe ausführen, können in ''/usr/sbin/'' gespeichert werden.
  
 +{{:icons:bash.png?nolink&100|}}
  
 ===== Basis Knowhow und Aufbau eines Skriptes ===== ===== Basis Knowhow und Aufbau eines Skriptes =====
Line 15: Line 16:
  
 **__Syntax:__**  **__Syntax:__** 
-  * /bin/bash [/pfad/zum/script.sh] +  * ''/bin/bash [/pfad/zum/script.sh]'' 
-  * source [/pfad/zum/script.sh] +  * ''source [/pfad/zum/script.sh]'' 
-  * ./script.sh+  * ''./script.sh''
  
  
Line 46: Line 47:
 ==== Aufbau eines Skripts ==== ==== Aufbau eines Skripts ====
  
 +<sxh bash>
 +#!/bin/bash
 +# Name: add.sh
 +# Addiere zwei Werte
  
 +let summe=$1+$2
 +echo "Die Summe von $1 + $2 = $summe"
 +</sxh>
 +  
 +In der ersten Zeile befindet sich das She Bang Zeichen, das zeigt, dass es sich um ein ausführbares Programm handelt und definiert mit welchem Interpreter das Skript ausgeführt wird. Danach folgt der Dateiname (Optional) und eine Kurzbeschreibung (Optional). Ab Zeile vier folgen die Anweisungen. 
 +
 +
 +Bei „init“ Skripte sollte zur besseren Übersicht ein kompletter „head“ erstellt werden. 
 +
 +Beispiel:
 +
 +<sxh bash>
 +#!/bin/bash
 + 
 +# xxx-Script
 + 
 +### BEGIN INIT INFO
 +# Provides:          xxx
 +# Required-Start:    $local_fs
 +# Required-Stop:     $local_fs
 +# Should-Start:
 +# Should-Stop:
 +# Default-Start:     S
 +# Default-Stop:      0 6
 +# Short-Description: xxx script
 +### END INIT INFO
 +
 +# Anweisungen
 +...
 +</sxh>
  
 </WRAP> </WRAP>
Line 52: Line 87:
 ---- ----
  
 +<WRAP center box 100%>
 +==== Übergabevariablen und Rückgabewerte ====
  
 +Wenn beim Start eines Skripts Werte übergeben, oder ein Rückgabewert ausgegeben werden soll, können diese im Skript als Variablen aufgefangen werden. Dafür gibt es Automatische Variablen in der Shell. 
 +
 +
 +**__Übergabevariablen:__** 
 +  * ''$1-$x'' ⇒ Werte die dem Skript übergeben wurden. Wird von ''$1'' hochnummeriert.
 +
 +
 +
 +**__Rückgabewerte:__** 
 +  * ''$0'' ⇒ Gibt Pfad und Namen des ausgeführten Skripts aus.
 +  * ''$#'' ⇒ Anzahl der übergebenen Parameter.
 +  * ''$*'' ⇒ Zeigt alle Arumente aus der Kommandozeile in einer Zeichenkette.
 +  * ''$@'' ⇒ Zeigt alle Arumente aus der Kommandozeile als Arrays von Zeichenketten.
 +  * ''$?'' ⇒ Errorlevel. Wird das Skript ohne Fehler ausgeführt ist der Errorlevel 0. Meisst wird bei einem Fehler 127 ausgegeben.
 +  * ''$$'' ⇒ Variable gibt die Prozessnummer aus.
 +  * ''$!'' ⇒ Prozessnummer des zuletzt gestarteten Hintergrundprozesses.
 +  * ''$_'' ⇒ Letztes Argument in der Kommandozeile des zuletzt aufgerufenen Kommandos (nur Bash).
 +
 +
 +**__Beispiel:__**
 +<sxh bash>
 +#!/bin/bash
 +# Uerbergabevariablen ausgeben.
 +
 +echo "Das Skript wurde mit dem Kommando $0 gestartet"
 +echo "Die Prozessnummer des Skripts ($0) lautet: $$"
 +echo "Es wurden $# Parameter uebergeben"
 +echo "Der erste Parameter lautet: $1"
 +echo "Der zweite Parameter lautet: $2"
 +Huchhh...
 +echo "Der aktuelle Errorlevel lautet: $?"
 +exit 0
 +</sxh>
 +
 +<sxh bash; gutter:false;>
 +michael@home:~$ ./uvar.sh 3 7
 +Das Script wurde mit dem Kommando ./uvar.sh gestartet
 +Die Prozessnummer des Scripts (./uvar.sh) lautet: 3332
 +Es wurden 2 Parameter uebergeben
 +Der erste Parameter lautet: 3
 +Der zweite Parameter lautet: 7
 +./uvar.sh: Zeile 8: Huchhh...: Kommando nicht gefunden.
 +Der aktuelle Errorlevel lautet: 127
 +</sxh>
 +
 +</WRAP>
 +
 +
 +----
 +
 +<WRAP center box 100%>
 +==== Werte und Dateitypen Vergleichen mit "test" ====
 +
 +„test“ ist ein Programm zum Dateitypen überprüfen und Werte vergleichen. Das Programm wird mit einem Exit-Status gemäss „AUSDRUCK“ beendet. 
 +
 +
 +**__Syntax:__** 
 +  * ''test [ AUSDRUCK ]''
 +
 +
 +
 +**__Ausdruck:__** 
 +  * ''AUSDRUCK'' ⇒ AUSDRUCK ist wahr.
 +  * ''! AUSDRUCK'' ⇒ AUSDRUCK ist falsch.
 +  * ''AUSDRUCK1 -a AUSDRUCK2'' ⇒ AUSDRUCK1 und AUSDRUCK2 sind wahr.
 +  * ''AUSDRUCK1 -o AUSDRUCK2'' ⇒ AUSDRUCK1 oder AUSDRUCK2 sind wahr.
 +  * ''-n ZEICHENKETTE'' ⇒ Die Länge von ZEICHENKETTE ist ungleich Null.
 +  * ''ZEICHENKETTE'' ⇒ Äquivalent zu -n ZEICHENKETTE.
 +  * ''-z ZEICHENKETTE'' ⇒ Die Länge von ZEICHENKETTE ist Null.
 +  * ''ZEICHENKETTE1 = ZEICHENKETTE2'' ⇒ Die Zeichenketten sind identisch.
 +  * ''ZEICHENKETTE1 != ZEICHENKETTE2'' ⇒ Die Zeichenketten sind nicht identisch.
 +  * ''GANZZAHL1 -eq GANZZAHL2'' ⇒ GANZZAHL1 ist gleich GANZZAHL2.
 +  * ''GANZZAHL1 -ge GANZZAHL2'' ⇒ GANZZAHL1 ist grösser als oder gleich GANZZAHL2.
 +  * ''GANZZAHL1 -gt GANZZAHL2'' ⇒ GANZZAHL1 ist grösser als GANZZAHL2.
 +  * ''GANZZAHL1 -le GANZZAHL2'' ⇒ GANZZAHL1 ist kleiner als oder gleich GANZZAHL2.
 +  * ''GANZZAHL1 -lt GANZZAHL2'' ⇒ GANZZAHL1 ist kleiner als GANZZAHL2.
 +  * ''GANZZAHL1 -ne GANZZAHL2'' ⇒ GANZZAHL1 ist nicht gleich GANZZAHL2.
 +  * ''DATEI1 -ef DATEI2'' ⇒ DATEI1 und DATEI2 haben dieselbe Geräte- und Inode-Nummern.
 +  * ''DATEI1 -nt DATEI2'' ⇒ DATEI1 ist neuer (Änderungsdatum) als DATEI2.
 +  * ''DATEI1 -ot DATEI2'' ⇒ DATEI1 ist älter als DATEI2.
 +  * ''-b DATEI'' ⇒ DATEI existiert und ist ein »block special«.
 +  * ''-c DATEI'' ⇒ DATEI existiert und ist ein »character special«.
 +  * ''-d DATEI'' ⇒ DATEI existiert und ist ein Verzeichnis.
 +  * ''-e DATEI'' ⇒ DATEI existiert.
 +  * ''-f DATEI'' ⇒ DATEI existiert und ist eine reguläre DATEI.
 +  * ''-g DATEI'' ⇒ DATEI existiert und das »Set-Group-ID«-Bit ist gesetzt.
 +  * ''-G DATEI'' ⇒ DATEI existiert und gehört der effektiven Gruppen-ID.
 +  * ''-h DATEI'' ⇒ DATEI existiert und ist ein symbolischer Link (identisch zu -L).
 +  * ''-k DATEI'' ⇒ DATEI existiert und hat das »Sticky«-Bit gesetzt.
 +  * ''-L DATEI'' ⇒ DATEI existiert und ist ein symbolischer Link (identisch zu -h).
 +  * ''-O DATEI'' ⇒ DATEI existiert und gehört der effektiven Benutzer-ID.
 +  * ''-p DATEI'' ⇒ DATEI existiert und ist eine benannte Pipe.
 +  * ''-r DATEI'' ⇒ DATEI existiert und ist lesbar.
 +  * ''-s DATEI'' ⇒ DATEI existiert und hat eine Grösse grösser Null.
 +  * ''-S DATEI'' ⇒ DATEI existiert und ist ein »Socket«.
 +  * ''-t FD'' ⇒ Datei-Deskriptor FD ist auf einem Terminal geöffnet.
 +  * ''-u DATEI'' ⇒ DATEI existiert und das »Set-User-ID«-Bit ist gesetzt.
 +  * ''-w DATEI'' ⇒ DATEI existiert und ist schreibbar.
 +  * ''-x DATEI'' ⇒ DATEI existiert und ist ausführbar (oder darf gesucht werden).
 +
 +
 +<WRAP center round important 100%>
 +Ausser den Tests ''[-h]'' und ''[-L]'' werden symbolische Links von allen DATEI-Tests dereferenziert. Zu bedenken ist, dass runde Klammern für Shells mit Back‐Slashes maskiert werden müssen \(…\).
 +</WRAP>
 +</WRAP>
 +
 +----
 +
 +<WRAP center box 100%>
 +==== Escape Sequenzen ====
 +
 +Der Parameter ''echo -e'' aktiviert Escape Zeichen bei der Ausgabe. 
 +
 +**__Escape Zeichen:__**
 +
 +  * ''\\'' ⇒ Soll ein Backslash angezeigt werden, muss er mit einem \ Backslash escaped werden.
 +  * ''\a'' ⇒ Alert. Erzeugt einen Ton.
 +  * ''\b'' ⇒ Backspace.
 +  * ''\c'' ⇒ Unterdrückt die Ausgabe des Newline Zeichens.
 +  * ''\f'' ⇒ form feed. Leerseite.
 +  * ''\n'' ⇒ Newline.
 +  * ''\r'' ⇒ Carriage Return.
 +  * ''\t'' ⇒ Horizontaler Tabulator (acht Zeichen).
 +  * ''\v'' ⇒ Vertikaler Tabulator (acht Leerzeilen).
 +</WRAP>
 +
 +
 +----
  
  
Line 78: Line 243:
 read b read b
    
-if [ $operator = a ]; then+if [[ $operator = a ]]; then
          let c=a+b          let c=a+b
          clear;          clear;
          echo $a + $b = $c          echo $a + $b = $c
    
-elif [ $operator = s ]; then+elif [[ $operator = s ]]; then
          let c=a-b          let c=a-b
          clear;          clear;
          echo $a - $b = $c          echo $a - $b = $c
    
-elif [ $operator = m ]; then+elif [[ $operator = m ]]; then
          let c=a*b          let c=a*b
          clear;          clear;
          echo $a "*" $b = $c          echo $a "*" $b = $c
    
-elif [ $operator = d ]; then+elif [[ $operator = d ]]; then
          let c=a/b          let c=a/b
          clear;          clear;
Line 250: Line 415:
 </WRAP> </WRAP>
    
 +
 +
 +===== Bash Rules =====
 +
 +<WRAP center box 100%>
 +==== Big Rules ====
 +
 +  * Always double quote variables, including subshells. No naked ''$'' signs
 +    * This rule gets you pretty far. Read [[http://mywiki.wooledge.org/Quotes]] for details
 +  * All code goes in a function. Even if it's one function, ''main''.
 +    * Unless a library script, you can do global script settings and call ''main''. That's it.
 +    * Avoid global variables. Though when defining constants use ''readonly''
 +  * Always have a ''main'' function for runnable scripts, called with ''main'' or ''main "$@"''
 +    * If script is also usable as library, call it using ''[[ "$0" == "$BASH_SOURCE" ]] && main "$@"''
 +  * Always use ''local'' when setting variables, unless there is reason to use ''declare''
 +    * Exception being rare cases when you are intentionally setting a variable in an outer scope.
 +  * Variable names should be lowercase unless exported to environment.
 +  * Always use ''set -eo pipefail''. Fail fast and be aware of exit codes.
 +    * Use ''|| true'' on programs that you intentionally let exit non-zero.
 +  * Never use deprecated style. Most notably:
 +    * Define functions as ''myfunc() { ... }'', not ''function myfunc { ... }''
 +    * Always use ''<nowiki>[[</nowiki>'' instead of ''['' or test
 +    * Never use backticks, use ''$( ... )''
 +    * See [[http://wiki.bash-hackers.org/scripting/obsolete]] for more
 +  * Prefer absolute paths (leverage $PWD), always qualify relative paths with ''./''.
 +  * Always use ''declare'' and name variable arguments at the top of functions that are more than 2-lines
 +    * Example: ''declare arg1="$1" arg2="$2"''
 +    * The exception is when defining variadic functions. See below.
 +  * Use ''mktemp'' for temporary files, always cleanup with a ''trap''.
 +  * Warnings and errors should go to STDERR, anything parsable should go to STDOUT.
 +  * Try to localize ''shopt'' usage and disable option when finished.
 +
 +//If you know what you're doing, you can bend or break some of these rules, but generally they will be right and be extremely helpful.//
 +
 +</WRAP>
 +
 +----
 +
 +<WRAP center box 100%>
 +==== Best Practices and Tips ====
 +
 +  * Use Bash variable substitution if possible before awk/sed.
 +  * Generally use double quotes unless it makes more sense to use single quotes.
 +  * For simple conditionals, try using ''&&'' and ''||''.
 +  * Don't be afraid of ''printf'', it's more powerful than ''echo''.
 +  * Put ''then'', ''do'', etc on same line, not newline.
 +  * Skip ''[[ ... ]]'' in your if-expression if you can test for exit code instead.
 +  * Use ''.sh'' or ''.bash'' extension if file is meant to be included/sourced. Never on executable script.
 +  * Put complex one-liners of ''sed'', ''perl'', etc in a standalone function with a descriptive name.
 +  * Good idea to include ''[[ "$TRACE" ]] && set -x''
 +  * Design for simplicity and obvious usage.
 +    * Avoid option flags and parsing, try optional environment variables instead.
 +    * Use subcommands for necessary different "modes".
 +  * In large systems or for any CLI commands, add a description to functions.
 +    * Use ''declare desc="description"'' at the top of functions, even above argument declaration.
 +    * This can be queried/extracted with a simple function using reflection.
 +  * Be conscious of the need for portability. Bash to run in a container can make more assumptions than Bash made to run on multiple platforms.
 +  * When expecting or exporting environment, consider namespacing variables when subshells may be involved.
 +  * Use hard tabs. Heredocs ignore leading tabs, allowing better indentation.
 +
 +----
 +
 +**Good References and Help**:
 +
 +  * [[http://wiki.bash-hackers.org/scripting/start]]
 +    * Especially [[http://wiki.bash-hackers.org/scripting/newbie_traps]]
 +  * http://tldp.org/LDP/abs/html/
 +  * Tips for interactive Bash: [[http://samrowe.com/wordpress/advancing-in-the-bash-shell/]]
 +  * For reference, Google's Bash styleguide
 +</WRAP>
  
  
  • skripting-section/skripting-grundlagen/bash-grundlagen.1558426020.txt.gz
  • Last modified: 2019/05/21 10:07
  • by michael