GNU/Linux >> Belajar Linux >  >> Linux

Cara membuat skrip Bash menggunakan variabel eksternal dan skrip yang disematkan

Ada kalanya skrip harus meminta informasi yang tidak dapat disimpan dalam file konfigurasi atau ketika jumlah pilihan tidak memungkinkan Anda menentukan setiap kemungkinan. Bash cukup bagus dalam membuat skrip interaktif untuk mengatasi masalah seperti ini.

Idealnya, pada akhir artikel ini, Anda harus dapat melakukan hal berikut:

  • Tulis program kecil yang mengajukan pertanyaan kepada pengguna dan simpan jawabannya (termasuk yang sensitif, seperti sandi)
  • Membaca data dari file konfigurasi dengan menggunakan program lain
  • Izinkan skrip melewati pertanyaan jika variabel eksternal ditentukan
  • Dan sebagai bonus, tulis antarmuka pengguna yang bagus (UI) dengan dialog teks

Mulailah dengan skrip kecil untuk terhubung ke desktop jarak jauh menggunakan protokol RDP.

[ Anda mungkin juga senang membaca: Menggunakan Bash untuk otomatisasi ]

Studi kasus:Hubungkan ke server jauh menggunakan RDP

Di Linux, ada banyak klien RDP, dan yang sangat bagus adalah freerdp. Salah satu cara untuk menyebutnya adalah dengan melewati garis panjang bendera (dengan nama pendek yang membingungkan) seperti ini:

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

Apakah ada cara yang lebih baik untuk melakukan ini?

Bertanya, belajar membaca

Jadi untuk percobaan pertama, saya menulis (versi 1) pembungkus shell di sekitar freerdp yang menanyakan pengguna, sandi, dan mesin jarak jauh. Saya akan menggunakan perintah baca bawaan 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})"

Untuk read (baris 7, 13) ke dalam variabel, cukup ucapkan baca variabel . Untuk membuatnya lebih ramah, berikan -p (tampilkan prompt khusus) dan -r (baca garis miring terbalik jika Anda salah ketik).

read juga memungkinkan Anda untuk menekan karakter yang Anda tulis di layar. Opsi ini disebut -s mode (rahasia) (baris 9).

Satu hal yang mengganggu saya adalah siapa pun yang melakukan ps -ef dapat melihat kata sandi saya di baris perintah; untuk menghindari itu, saya menyimpannya ke dalam file, dan kemudian, menggunakan subkulit, saya membacanya kembali ketika xfreerdp membutuhkannya. Selanjutnya, untuk menghindari meninggalkan kata sandi saya tergeletak di disk, saya menyimpannya ke dalam file sementara, yang saya pastikan akan dihapus setelah skrip selesai atau dimatikan.

Tapi tetap saja... script ini terus menanyakan beberapa pertanyaan berulang-ulang. Apakah ada cara untuk membuatnya, yah, lebih pintar?

Anda dapat menyimpan beberapa default, seperti server jarak jauh, dalam file konfigurasi. Jika Anda tidak memberikannya, gunakan setelan default.

Juga tentang topik penggunaan kembali kode:Letakkan logika cara menyambung ke server jarak jauh dalam file terpisah jika Anda ingin menggunakan kembali beberapa logika ini dalam situasi serupa lainnya. Jadi perpustakaan baru terlihat seperti ini:

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

Pembungkus RDP, versi 2 dari skrip asli, sekarang jauh lebih sederhana:

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

Jadi setelah perubahan ini, bagaimana tampilannya?

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

Ada lebih banyak ruang untuk perbaikan, jadi silakan terus membaca.

Selalu beri pengguna pilihan:Variabel eksternal dan lebih banyak program eksternal

Katakanlah Anda menggunakan skrip Anda untuk terhubung ke mesin yang sama setiap hari. Kemungkinannya adalah Anda tidak akan mengubah pengguna jarak jauh, mesin, dan hanya kata sandi Anda sesekali. Jadi, Anda dapat menyimpan semua setelan tersebut dalam file konfigurasi, yang hanya dapat dibaca oleh pengguna saat ini dan tidak ada orang lain:

(Contoh ~/.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"
}

Ya, JSON bukan format terbaik untuk file konfigurasi, tetapi yang ini cukup kecil. Selain itu, perhatikan bahwa Anda kini dapat menyimpan lebih dari satu mesin jarak jauh (untuk mempermudah, gunakan hanya yang pertama).

Untuk memanfaatkannya, ubah library (v2) agar terlihat seperti ini:

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

Apakah Anda memperhatikan saya tidak mencoba membaca kata sandi dari file konfigurasi? Itulah satu-satunya kredensial yang akan terus saya tanyakan berulang-ulang kecuali jika dienkripsi. Nilai lainnya yang Anda dapatkan menggunakan jq, menggunakan subkulit.

Dan, tentu saja, ini adalah versi baru (v3) skrip:

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

Perhatikan bahwa Anda tidak lagi meminta dua parameter; hanya kata sandinya:

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

Apakah ada hal lain yang dapat Anda lakukan untuk menyempurnakan skrip ini?

Saya ingin UI teks yang bagus:Tidak ada dialog yang bagus

Berikut cara menulis skrip interaktif dengan alat mudah bernama Dialog. Ini meminta pengguna untuk memilih antara sejumlah variabel mesin (tergantung pada file konfigurasi) dan, tentu saja, kata sandi. Namun, jika pengguna jarak jauh sama untuk kedua mesin (yang normal jika Anda terhubung ke perusahaan yang sama), ia tidak akan meminta informasi setiap saat.

Perhatikan bahwa Dialog bukan satu-satunya pemain di kota. Kebetulan saya menyukainya karena tersedia secara luas dan karena kesederhanaannya.

Di bawah ini adalah versi 3 dari skrip. Itu banyak dikomentari. Anda dapat melihat bahwa Dialog bekerja dengan membaca variabel atau file untuk mengaktifkan/menonaktifkan opsi. Cobalah dan jalankan skrip untuk melihat bagaimana setiap bagian cocok bersama:

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

[ Memulai container? Lihat kursus gratis ini. Menyebarkan aplikasi kemas:Tinjauan teknis. ]

Menutup

Itu banyak alasan untuk dibahas dalam satu artikel. Skrip seperti yang dikembangkan dalam artikel ini menyederhanakan koneksi dan membuat antarmuka lebih mudah bagi pengguna. Berikut adalah hal-hal yang Anda pelajari cara melakukannya:

  • Anda dapat menggunakan read bawaan Bash perintah untuk mendapatkan informasi dari pengguna Anda.
  • Anda dapat memeriksa apakah informasi berulang sudah tersedia untuk menghindari membaca dari lingkungan.
  • Anda tidak menyimpan sandi tanpa enkripsi. KeepPassXC dan Vault adalah alat luar biasa yang dapat Anda gunakan untuk menghindari hardcoding informasi sensitif di tempat yang salah.
  • Anda menginginkan UI yang lebih bagus, sehingga Anda dapat menggunakan Dialog dan alat lain yang tersedia untuk mewujudkannya.
  • Selalu validasi masukan Anda dan periksa kesalahan.

Linux
  1. Cara Mengidentifikasi Direktori Kerja Menggunakan Karakter dan Variabel Shell

  2. Cara Membuat dan Menerapkan Patch di GIT menggunakan diff and apply Command

  3. Cara Melacak Skrip Python menggunakan trace.py

  1. Cara Mengatur/Membuat Variabel Lingkungan dan Shell di Linux

  2. Bagaimana cara menulis beberapa baris string menggunakan Bash dengan variabel?

  3. Bagaimana variabel numerik zero-pad di zsh (dan mungkin juga bash?)

  1. Menggunakan Bash untuk otomatisasi

  2. Bash + Bagaimana Cara Keluar Dari Skrip Sekunder Dan Dari Skrip Utama Pada Kedua Kalinya?

  3. Cara Membuat dan Memanggil Fungsi di Bash