GNU/Linux >> Znalost Linux >  >> Linux

Jak vytvořit Bash skripty pomocí externích proměnných a vložených skriptů

Jsou chvíle, kdy si skript musí vyžádat informace, které nelze uložit do konfiguračního souboru, nebo když vám množství možností nedovolí specifikovat všechny možnosti. Bash je docela dobrý ve vytváření interaktivních skriptů pro řešení těchto druhů problémů.

V ideálním případě byste na konci tohoto článku měli být schopni udělat následující:

  • Psát malé programy, které uživatelům kladou otázky a ukládají odpovědi (včetně citlivých, jako jsou hesla)
  • Čtení dat z konfiguračních souborů pomocí jiných programů
  • Pokud jsou definovány externí proměnné, povolte skriptu přeskočit kladení otázek.
  • A jako bonus napište pěkné uživatelské rozhraní (UI) s textovými dialogy

Začněte malým skriptem pro připojení ke vzdálené ploše pomocí protokolu RDP.

[ Také by se vám mohlo líbit čtení: Použití Bash pro automatizaci ]

Případová studie:Připojte se ke vzdálenému serveru pomocí RDP

V Linuxu existuje mnoho klientů RDP a opravdu dobrý je freerdp. Jedním ze způsobů, jak to nazvat, je předat dlouhou řadu vlajek (s matoucími krátkými názvy) takto:

/usr/bin/xfreerdp /cert-ignore /sound:sys:alsa /f /u:REMOTE_USER /v:MACHINE /p:mynotsosecretpassword

Existuje lepší způsob, jak to udělat?

Klazení otázek, učení se číst

Na první pokus jsem tedy napsal (verze 1) obal shellu kolem freerdp, který vyžaduje uživatele, heslo a vzdálený počítač. Použiji vestavěný příkaz čtení Bash:

#!/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})"

Chcete-li read (řádky 7, 13) do proměnné, stačí říct číst proměnnou . Aby to bylo přátelštější, předejte -p (zobrazit vlastní výzvu) a -r (přečtěte si zpětná lomítka, pokud uděláte překlep).

read také umožňuje potlačit znaky, které píšete na obrazovku. Volba se nazývá -s (tajný) režim (řádek 9).

Jedna věc, která mi vadí, je, že kdokoli dělá ps -ef mohu vidět moje heslo na příkazovém řádku; abych se tomu vyhnul, uložím to do souboru a pak to pomocí subshell přečtu zpět, když to xfreerdp potřebuje. Kromě toho, abych své heslo nenechal ležet na disku, ukládám ho do dočasného souboru, který bude odstraněn, jakmile skript skončí nebo bude zabit.

Ale přesto... tento scénář stále znovu a znovu klade nějaké otázky. Existuje způsob, jak to udělat, no, chytřejší?

Některé výchozí hodnoty, například vzdálené servery, můžete uložit do konfiguračního souboru. Pokud žádné neposkytnete, použijte výchozí nastavení.

Také k tématu opětovného použití kódu:Vložte logiku připojení ke vzdálenému serveru do samostatného souboru pro případ, že byste chtěli část této logiky znovu použít v jiných podobných situacích. Takže nová knihovna vypadá takto:

#!/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
}

RDP wrapper, verze 2 původního skriptu, je nyní mnohem jednodušší:

#!/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"

Jak to tedy po této změně vypadá?

$ ./kodegeek_rdp2.sh
Remote RPD user: jose
Password for jose: 
Remote server: myremotemachine.kodegeek.com

Je zde více prostoru pro zlepšení, tak prosím pokračujte ve čtení.

Vždy dejte uživatelům na výběr:Externí proměnné a více externích programů

Řekněme, že svůj skript používáte k připojení ke stejnému počítači každý den. Je pravděpodobné, že jednou za čas nezměníte vzdáleného uživatele, počítač a pouze heslo. Všechna tato nastavení tedy můžete uložit do konfiguračního souboru, který může číst pouze aktuální uživatel a nikdo jiný:

(Příklad ~/.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"
}

Ano, JSON není nejlepší formát pro konfigurační soubory, ale tento je docela malý. Všimněte si také, že nyní můžete uložit více než jeden vzdálený počítač (pro zjednodušení použijte pouze první).

Chcete-li toho využít, upravte knihovnu (v2) tak, aby vypadala takto:

#!/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
}

Všimli jste si, že jsem se nepokusil přečíst heslo z konfiguračního souboru? To je jediné pověření, na které se budu ptát znovu a znovu, pokud nebude zašifrované. Zbytek hodnot získáte pomocí jq pomocí subshell.

A samozřejmě zde je nová verze (v3) skriptu:

#!/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"

Všimněte si, že se již nebudete ptát na dva parametry; stačí heslo:

$ ./kodegeek_rdp2.sh 
Password for jose@MYCOMPANY: 

Je ještě něco, co můžete udělat pro vylepšení tohoto skriptu?

Chci pěkné textové uživatelské rozhraní:Nic jako dobrý dialog

Zde je návod, jak napsat interaktivní skript pomocí jednoduchého nástroje zvaného Dialog. Žádá uživatele, aby si vybral mezi proměnným počtem strojů (v závislosti na konfiguračním souboru) a samozřejmě heslem. Pokud je však vzdálený uživatel stejný pro oba stroje (což je normální, pokud se připojujete ke stejné společnosti), nebude vyžadovat informace pokaždé.

Všimněte si, že Dialog není jediným hráčem ve městě. Prostě se mi líbí, protože je široce dostupný a kvůli jeho jednoduchosti.

Níže je verze 3 skriptu. Je to silně komentované. Můžete vidět, že Dialog funguje čtením proměnných nebo souborů pro povolení/zakázaní možností. Zkuste to a spusťte skript, abyste viděli, jak do sebe jednotlivé části zapadají:

#!/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

[ Začínáte s kontejnery? Podívejte se na tento bezplatný kurz. Nasazení kontejnerových aplikací:technický přehled. ]

Sbalit

Bylo toho hodně, co je třeba pokrýt v jednom článku. Skripty, jako je ten vyvinutý v tomto článku, zjednodušují připojení a usnadňují uživatelům rozhraní. Zde jsou věci, které jste se naučili dělat:

  • Můžete použít vestavěné read Bash příkaz k získání informací od vašich uživatelů.
  • Můžete zkontrolovat, zda již nejsou dostupné opakující se informace, abyste se vyhnuli čtení z prostředí.
  • Hesla bez šifrování neuložíte. KeepPassXC a Vault jsou vynikající nástroje, které můžete použít, abyste se vyhnuli zakódování citlivých informací na nesprávná místa.
  • Chcete hezčí uživatelské rozhraní, takže k tomu můžete použít Dialog a další snadno dostupné nástroje.
  • Vždy ověřujte své vstupy a kontrolujte chyby.

Linux
  1. Jak identifikovat pracovní adresáře pomocí Shell znaků a proměnných

  2. Jak vytvářet a aplikovat opravy v GIT pomocí příkazu diff a použít

  3. Jak napsat víceřádkový řetězec pomocí Bash s proměnnými?

  1. Bash + Jak opustit sekundární skript a hlavní skript v obou případech?

  2. Jak vytvářet a volat funkce v Bash

  3. Jak nulovat číselné proměnné v zsh (a možná také bash?)

  1. Použití Bash pro automatizaci

  2. Jak nastavit/vytvořit proměnné prostředí a prostředí v Linuxu

  3. Jak trasovat Python skripty pomocí trace.py