Es gibt Zeiten, in denen ein Skript nach Informationen fragen muss, die nicht in einer Konfigurationsdatei gespeichert werden können, oder wenn die Anzahl der Auswahlmöglichkeiten es Ihnen nicht erlaubt, alle Möglichkeiten anzugeben. Bash ist ziemlich gut darin, interaktive Skripte zu erstellen, um diese Art von Problemen anzugehen.
Idealerweise sollten Sie am Ende dieses Artikels in der Lage sein, Folgendes zu tun:
- Schreiben Sie kleine Programme, die dem Nutzer Fragen stellen, und speichern Sie die Antworten (einschließlich sensibler wie Passwörter)
- Daten aus Konfigurationsdateien mit anderen Programmen lesen
- Erlauben Sie dem Skript, Fragen zu überspringen, wenn externe Variablen definiert sind
- Und schreiben Sie als Bonus eine schöne Benutzeroberfläche (UI) mit Textdialogen
Beginnen Sie mit einem kleinen Skript, um über das RDP-Protokoll eine Verbindung zu einem Remote-Desktop herzustellen.
[Sie könnten auch gerne lesen:Bash für die Automatisierung verwenden ]
Fallstudie:Mit RDP eine Verbindung zu einem Remote-Server herstellen
Unter Linux gibt es viele RDP-Clients, und ein wirklich guter ist freerdp. Eine Möglichkeit, es aufzurufen, besteht darin, eine lange Reihe von Flags (mit verwirrenden Kurznamen) wie folgt zu übergeben:
/usr/bin/xfreerdp /cert-ignore /sound:sys:alsa /f /u:REMOTE_USER /v:MACHINE /p:mynotsosecretpassword
Gibt es einen besseren Weg, dies zu tun?
Fragen stellen, lesen lernen
Also habe ich für einen ersten Versuch (Version 1) einen Shell-Wrapper um freerdp geschrieben, der nach dem Benutzer, dem Passwort und dem Remote-Computer fragt. Ich werde den in Bash integrierten Lesebefehl verwenden:
#!/bin/bash
# author Jose Vicente Nunez
# Do not use this script on a public computer.
tmp_file=$(/usr/bin/mktemp 2>/dev/null) || exit 100
trap '/bin/rm -f $tmp_file' QUIT EXIT INT
/bin/chmod go-wrx "${tmp_file}" > /dev/null 2>&1
read -r -p "Remote RPD user: " REMOTE_USER|| exit 100
test -z "$REMOTE_USER" && exit 100
read -r -s -p "Password for $REMOTE_USER: " PASSWD|| exit 100
test -z "$PASSWD" && exit 100
echo
echo > "$tmp_file"|| exit 100
read -r -p "Remote server: " MACHINE|| exit 100
test -z "$REMOTE_USER" && exit 100
/usr/bin/xfreerdp /cert-ignore /sound:sys:alsa /f /u:"$REMOTE_USER" /v:"${MACHINE}" /p:"(/bin/cat ${tmp_file})"
Zum read
(Zeile 7, 13) in eine Variable, sagen Sie einfach Variable lesen . Um es benutzerfreundlicher zu gestalten, übergeben Sie -p
(einen benutzerdefinierten Prompt anzeigen) und -r
(Lesen Sie umgekehrte Schrägstriche, wenn Sie einen Tippfehler machen).
read
ermöglicht Ihnen auch, die Zeichen zu unterdrücken, die Sie auf dem Bildschirm schreiben. Die Option heißt -s
(geheimer) Modus (Zeile 9).
Eine Sache, die mich stört, ist, dass jeder ein ps -ef
ausführt kann mein Passwort auf der Kommandozeile sehen; Um dies zu vermeiden, speichere ich es in einer Datei und lese es dann mithilfe einer Subshell zurück, wenn xfreerdp es benötigt. Um zu vermeiden, dass mein Passwort auf der Festplatte herumliegt, speichere ich es außerdem in einer temporären Datei, die sicher entfernt wird, sobald das Skript beendet oder beendet ist.
Aber trotzdem ... dieses Skript stellt immer wieder einige Fragen. Gibt es eine Möglichkeit, es intelligenter zu machen?
Sie könnten einige der Standardeinstellungen, wie die Remote-Server, in einer Konfigurationsdatei speichern. Wenn Sie keine angeben, verwenden Sie die Standardeinstellungen.
Auch zum Thema Wiederverwendung von Code:Legen Sie die Logik zum Herstellen einer Verbindung zu einem Remote-Server in einer separaten Datei ab, falls Sie einen Teil dieser Logik in anderen ähnlichen Situationen wiederverwenden möchten. Die neue Bibliothek sieht also so aus:
#!/bin/bash
# author Jose Vicente Nunez
# Common logic for RDP connectivity
function remote_rpd {
local remote_user=$1
local pfile=$2
local machine=$3
test -z "$remote_user" && exit 100
test ! -f "$pfile" && exit 100
test -z "$machine" && exit 100
/usr/bin/xfreerdp /cert-ignore /sound:sys:alsa /f /u:"$remote_user" /v:"${machine}" /p:"(/bin/cat ${pfile})" && return 0|| return 1
}
Der RDP-Wrapper, Version 2 des ursprünglichen Skripts, ist jetzt viel einfacher:
#!/bin/bash
# author Jose Vicente Nunez
# Do not use this script on a public computer.
# shellcheck source=/dev/null.
. "rdp_common.sh"
tmp_file=$(/usr/bin/mktemp 2>/dev/null) || exit 100
trap '/bin/rm -f $tmp_file' QUIT EXIT INT
/bin/chmod go-wrx "${tmp_file}" > /dev/null 2>&1
read -r -p "Remote RPD user: " REMOTE_USER|| exit 100
read -r -s -p "Password for $REMOTE_USER: " PASSWD|| exit 100
echo
echo "$PASSWD" > "$tmp_file"|| exit 100
read -r -p "Remote server: " MACHINE|| exit 100
remote_rpd "$REMOTE_USER" "$tmp_file" "$MACHINE"
Wie sieht es also nach dieser Änderung aus?
$ ./kodegeek_rdp2.sh
Remote RPD user: jose
Password for jose:
Remote server: myremotemachine.kodegeek.com
Es gibt noch Raum für Verbesserungen, also lesen Sie bitte weiter.
Geben Sie Benutzern immer die Wahl:Externe Variablen und mehr externe Programme
Angenommen, Sie verwenden Ihr Skript, um sich jeden Tag mit derselben Maschine zu verbinden. Es besteht die Möglichkeit, dass Sie Ihren Remote-Benutzer, Ihren Computer und nur das Kennwort von Zeit zu Zeit nicht ändern. Sie können also all diese Einstellungen in einer Konfigurationsdatei speichern, die nur vom aktuellen Nutzer und sonst niemandem gelesen werden kann:
(Beispiel für ~/.config/scripts/kodegeek_rdp.json
)
{
"machines": [
{
"name": "myremotemachine.kodegeek.com",
"description": "Personal-PC"
},
{
"name": "vmdesktop1.kodegeek.com",
"description": "Virtual-Machine"
}
],
"remote_user": "jose@MYCOMPANY",
"title" : "Remote desktop settings"
}
Ja, JSON ist nicht das beste Format für Konfigurationsdateien, aber dieses hier ist ziemlich klein. Beachten Sie außerdem, dass Sie jetzt mehr als einen Remote-Computer speichern können (verwenden Sie der Einfachheit halber nur den ersten).
Um davon zu profitieren, ändern Sie die Bibliothek (v2) so, dass sie so aussieht:
#!/bin/bash
# author Jose Vicente Nunez
# Common logic for RDP connectivity
if [[ -x '/usr/bin/jq' ]] && [[ -f "$HOME/.config/scripts/kodegeek_rdp.json" ]]; then
REMOTE_USER="$(/usr/bin/jq --compact-output --raw-output '.remote_user' "$HOME"/.config/scripts/kodegeek_rdp.json)"|| exit 100
MACHINE="$(/usr/bin/jq --compact-output --raw-output '.machines[0]| join(",")' "$HOME"/.config/scripts/kodegeek_rdp.json)"|| exit 100
export REMOTE_USER
export MACHINE
fi
function remote_rpd {
local remote_user=$1
local pfile=$2
local machine=$3
test -z "$remote_user" && exit 100
test ! -f "$pfile" && exit 100
test -z "$machine" && exit 100
/usr/bin/xfreerdp /cert-ignore /sound:sys:alsa /f /u:"$remote_user" /v:"${machine}" /p:"(/bin/cat ${pfile})" && return 0|| return 1
}
Ist Ihnen aufgefallen, dass ich nicht versucht habe, das Passwort aus einer Konfigurationsdatei zu lesen? Das ist der einzige Berechtigungsnachweis, den ich immer wieder fragen werde, es sei denn, er ist verschlüsselt. Die restlichen Werte erhalten Sie mit jq unter Verwendung einer Subshell.
Und natürlich gibt es hier eine neue Version (v3) des Skripts:
#!/bin/bash
# author Jose Vicente Nunez
# Do not use this script on a public computer.
# shellcheck source=/dev/null
. "rdp_common2.sh"
tmp_file=$(/usr/bin/mktemp 2>/dev/null) || exit 100
trap '/bin/rm -f $tmp_file' QUIT EXIT INT
/bin/chmod go-wrx "${tmp_file}" > /dev/null 2>&1
if [ -z "$REMOTE_USER" ]; then
read -r -p "Remote RPD user: " REMOTE_USER|| exit 100
fi
read -r -s -p "Password for $REMOTE_USER: " PASSWD|| exit 100
echo
echo "$PASSWD" > "$tmp_file"|| exit 100
if [ -z "$MACHINE" ]; then
read -r -p "Remote server: " MACHINE|| exit 100
fi
remote_rpd "$REMOTE_USER" "$tmp_file" "$MACHINE"
Beachten Sie, dass Sie nicht mehr nach zwei Parametern fragen; nur das Passwort:
$ ./kodegeek_rdp2.sh
Password for jose@MYCOMPANY:
Gibt es irgendetwas anderes, was Sie tun können, um dieses Skript zu verbessern?
Ich möchte eine schöne Text-UI:Nichts geht über einen guten Dialog
So schreiben Sie ein interaktives Skript mit einem einfachen Tool namens Dialog. Es fordert den Benutzer auf, zwischen einer variablen Anzahl von Maschinen (abhängig von der Konfigurationsdatei) und natürlich dem Passwort zu wählen. Wenn der Remote-Benutzer jedoch für beide Computer derselbe ist (was normal ist, wenn Sie sich mit derselben Firma verbinden), wird er nicht jedes Mal nach den Informationen fragen.
Beachten Sie, dass Dialog nicht der einzige Spieler in der Stadt ist. Ich mag es einfach, weil es weit verbreitet ist und weil es einfach ist.
Unten ist Version 3 des Skripts. Es wird stark kommentiert. Sie können sehen, dass Dialog funktioniert, indem Sie entweder Variablen oder Dateien lesen, um Optionen zu aktivieren/deaktivieren. Probieren Sie es aus und führen Sie das Skript aus, um zu sehen, wie die einzelnen Teile zusammenpassen:
#!/bin/bash
# author Jose Vicente Nunez
# Do not use this script on a public computer.
# https://invisible-island.net/dialog/
SCRIPT_NAME="$(/usr/bin/basename "$0")"
DATA_FILE="$HOME/.config/scripts/kodegeek_rdp.json"
test -f "$DATA_FILE"|| exit 100
: "${DIALOG_OK=0}"
: "${DIALOG_CANCEL=1}"
: "${DIALOG_HELP=2}"
: "${DIALOG_EXTRA=3}"
: "${DIALOG_ITEM_HELP=4}"
: "${DIALOG_ESC=255}"
tmp_file=$(/usr/bin/mktemp 2>/dev/null) || declare tmp_file=/tmp/test$$
trap '/bin/rm -f $tmp_file' QUIT EXIT INT
/bin/chmod go-wrx "${tmp_file}" > /dev/null 2>&1
TITLE=$(/usr/bin/jq --compact-output --raw-output '.title' "$DATA_FILE")|| exit 100
REMOTE_USER=$(/usr/bin/jq --compact-output --raw-output '.remote_user' "$DATA_FILE")|| exit 100
# Choose a machine
MACHINES=$(
tmp_file2=$(/usr/bin/mktemp 2>/dev/null) || declare tmp_file2=/tmp/test$$
/usr/bin/jq --compact-output --raw-output '.machines[]| join(",")' "$DATA_FILE" > $tmp_file2|| exit 100
declare -i i=0
while read -r line; do
machine=$(echo "$line"| /usr/bin/cut -d',' -f1)|| exit 100
desc=$(echo "$line"| /usr/bin/cut -d',' -f2)|| exit 100
toggle=off
if [ $i -eq 0 ]; then
toggle=on
((i=i+1))
fi
echo "$machine" "$desc" "$toggle"
done < "$tmp_file2"
/bin/cp /dev/null $tmp_file2
) || exit 100
# shellcheck disable=SC2086
/usr/bin/dialog \
--clear \
--title "$TITLE" \
--radiolist "Which machine do you want to use?" 20 61 2 \
$MACHINES 2> ${tmp_file}
return_value=$?
case $return_value in
"$DIALOG_OK")
remote_machine="$(/bin/cat ${tmp_file})"
;;
"$DIALOG_CANCEL")
echo "Cancel pressed.";;
"$DIALOG_HELP")
echo "Help pressed.";;
"$DIALOG_EXTRA")
echo "Extra button pressed.";;
"$DIALOG_ITEM_HELP")
echo "Item-help button pressed.";;
"$DIALOG_ESC")
if test -s $tmp_file ; then
/bin/rm -f $tmp_file
else
echo "ESC pressed."
fi
;;
esac
if [ -z "${remote_machine}" ]; then
/usr/bin/dialog \
--clear \
--title "Error, no machine selected?" --clear "$@" \
--msgbox "No machine was selected!. Will exit now..." 15 30
exit 100
fi
# Ask for the password
/bin/rm -f ${tmp_file}
/usr/bin/dialog \
--title "$TITLE" \
--clear \
--insecure \
--passwordbox "Please enter your remote password for ${remote_machine}\n" 16 51 2> $tmp_file
return_value=$?
passwd=$(/bin/cat ${tmp_file})
/bin/rm -f "$tmp_file"
if [ -z "${passwd}" ]; then
/usr/bin/dialog \
--clear \
--title "Error, empty password" --clear "$@" \
--msgbox "Empty password!" 15 30
exit 100
fi
# Try to connect
case $return_value in
"$DIALOG_OK")
/usr/bin/mkdir -p -v "$HOME"/logs
/usr/bin/xfreerdp /cert-ignore /sound:sys:alsa /f /u:"$REMOTE_USER" /v:"${remote_machine}" /p:"${passwd}"| \
/usr/bin/tee "$HOME"/logs/"$SCRIPT_NAME"-"$remote_machine".log
;;
"$DIALOG_CANCEL")
echo "Cancel pressed.";;
"$DIALOG_HELP")
echo "Help pressed.";;
"$DIALOG_EXTRA")
echo "Extra button pressed.";;
"$DIALOG_ITEM_HELP")
echo "Item-help button pressed.";;
"$DIALOG_ESC")
if test -s $tmp_file ; then
/bin/rm -f $tmp_file
else
echo "ESC pressed."
fi
;;
esac
[ Erste Schritte mit Containern? Schauen Sie sich diesen kostenlosen Kurs an. Containerisierte Anwendungen bereitstellen:Eine technische Übersicht. ]
Abschluss
Das war eine Menge Boden in einem Artikel zu behandeln. Skripte wie das in diesem Artikel entwickelte vereinfachen Verbindungen und machen die Benutzeroberfläche für Benutzer einfacher. Hier sind die Dinge, die Sie gelernt haben:
- Sie können Bashs eingebautes
read
verwenden Befehl, um Informationen von Ihren Benutzern zu erhalten. - Sie können prüfen, ob sich wiederholende Informationen bereits verfügbar sind, um ein Ablesen aus der Umgebung zu vermeiden.
- Sie speichern keine Passwörter ohne Verschlüsselung. KeepPassXC und Vault sind hervorragende Tools, mit denen Sie verhindern können, dass vertrauliche Informationen an den falschen Stellen hartcodiert werden.
- Sie möchten eine schönere Benutzeroberfläche, also können Sie Dialog und andere leicht verfügbare Tools verwenden, um dies zu erreichen.
- Bestätigen Sie Ihre Eingaben immer und überprüfen Sie sie auf Fehler.