GNU/Linux >> Linux の 問題 >  >> Linux

外部変数と埋め込みスクリプトを使用してBashスクリプトを作成する方法

スクリプトが構成ファイルに保存できない情報を要求しなければならない場合や、選択肢の数によってすべての可能性を指定できない場合があります。 Bashは、この種の問題に対処するためのインタラクティブなスクリプトを作成するのに非常に優れています。

理想的には、この記事の終わりまでに、次のことができるようになるはずです。

  • ユーザーに質問をして回答を保存する小さなプログラムを作成します(パスワードなどの機密性の高いものを含む)
  • 他のプログラムを使用して構成ファイルからデータを読み取る
  • 外部変数が定義されている場合、スクリプトが質問をスキップできるようにします
  • ボーナスとして、テキストダイアログを備えた優れたユーザーインターフェース(UI)を作成します

小さなスクリプトから始めて、RDPプロトコルを使用してリモートデスクトップに接続します。

[読むこともお楽しみいただけます:自動化のためのBashの使用]

ケーススタディ:RDPを使用してリモートサーバーに接続する

Linuxには多くのRDPクライアントがあり、本当に良いクライアントはfreerdpです。これを呼び出す1つの方法は、次のような長いフラグの行(紛らわしい短い名前)を渡すことです。

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

これを行うためのより良い方法はありますか?

質問をし、読み方を学ぶ

そこで、最初の試みとして、ユーザー、パスワード、リモートマシンを要求するfreerdpのシェルラッパー(バージョン1)を作成しました。 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})"

読む (7、13行目)変数に、変数の読み取りと言うだけです 。わかりやすくするには、 -pを渡します。 (カスタムプロンプトを表示)および -r (タイプミスをした場合はバックスラッシュを読んでください)。

読む また、画面に書き込む文字を非表示にすることもできます。このオプションは-sと呼ばれます (秘密)モード(9行目)。

私を悩ませていることの1つは、 ps -efを実行している人がいることです。 コマンドラインで私のパスワードを見ることができます。それを避けるために、私はそれをファイルに保存し、次にサブシェルを使用して、xfreerdpがそれを必要とするときにそれを読み返します。さらに、パスワードをディスクに残さないように、一時ファイルに保存します。このファイルは、スクリプトが終了するか、強制終了されると確実に削除されます。

しかし、それでも...このスクリプトは何度も何度も質問をし続けます。それをもっと賢くする方法はありますか?

リモートサーバーなどのデフォルトの一部を構成ファイルに保存できます。何も指定しない場合は、デフォルト設定を使用します。

コードの再利用のトピックについて:他の同様の状況でこのロジックの一部を再利用したい場合に備えて、リモートサーバーに接続する方法のロジックを別のファイルに入れます。したがって、新しいライブラリは次のようになります。

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

元のスクリプトのバージョン2であるRDPラッパーは、はるかにシンプルになりました:

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

それで、この変更の後、それはどのように見えますか?

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

まだまだ改善の余地がありますので、読み続けてください。

常にユーザーに選択肢を提供する:外部変数とより多くの外部プログラム

スクリプトを使用して毎日同じマシンに接続するとします。リモートユーザー、マシン、およびパスワードのみをたまに変更しない可能性があります。そのため、これらすべての設定を構成ファイルに保存して、現在のユーザーだけが読み取り可能にし、他のユーザーは誰も読み取れないようにすることができます。

〜/ .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"
}

はい、JSONは構成ファイルに最適な形式ではありませんが、これはかなり小さいものです。また、複数のリモートマシンを保存できるようになったことにも注意してください(簡単にするために、最初のマシンのみを使用してください)。

これを利用するには、ライブラリ(v2)を次のように変更します。

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

構成ファイルからパスワードを読み取ろうとしなかったことに気づきましたか?暗号化されていない限り、これが私が何度も何度も尋ね続ける唯一のクレデンシャルです。サブシェルを利用してjqを使用して取得する残りの値。

そしてもちろん、スクリプトの新しいバージョン(v3)は次のとおりです。

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

もう2つのパラメータを要求しないことに注意してください。パスワードだけ:

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

このスクリプトを強化するために他にできることはありますか?

優れたテキストUIが必要:優れたダイアログのようなものはありません

Dialogと呼ばれる簡単なツールを使用してインタラクティブなスクリプトを作成する方法は次のとおりです。可変数のマシン(構成ファイルに応じて)と、もちろんパスワードのどちらかを選択するようにユーザーに求めます。ただし、リモートユーザーが両方のマシンで同じである場合(同じ会社に接続している場合は正常です)、毎回情報を要求されることはありません。

町のプレイヤーはDialogだけではないことに注意してください。広く入手可能で、シンプルなため、たまたま気に入っています。

以下は、スクリプトのバージョン3です。コメントが多いです。変数またはファイルを読み取ってオプションを有効/無効にすることで、Dialogが機能することがわかります。試してみて、スクリプトを実行して、各部分がどのように組み合わされているかを確認してください。

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

[コンテナを使い始めますか?この無料コースをチェックしてください。コンテナ化されたアプリケーションのデプロイ:技術的な概要。 ]

まとめ

それは1つの記事でカバーするための多くの根拠でした。この記事で開発したようなスクリプトは、接続を簡素化し、ユーザーにとってインターフェースを容易にします。方法を学んだことは次のとおりです。

  • Bashの組み込みのreadを使用できます ユーザーから情報を取得するコマンド。
  • 環境からの読み取りを避けるために、繰り返しの情報がすでに利用可能かどうかを確認できます。
  • 暗号化せずにパスワードを保存することはできません。 KeepPassXCとVaultは、機密情報を間違った場所にハードコーディングしないようにするために使用できる優れたツールです。
  • より優れたUIが必要なので、Dialogやその他のすぐに利用できるツールを使用してそれを実現できます。
  • 常に入力を検証し、エラーをチェックします。

Linux
  1. シェル文字と変数を使用して作業ディレクトリを識別する方法

  2. diff および apply コマンドを使用して GIT でパッチを作成および適用する方法

  3. trace.py を使用して Python スクリプトをトレースする方法

  1. Linuxで環境変数とシェル変数を設定/作成する方法

  2. Bash +セカンダリスクリプトとメインスクリプトの両方を終了する方法は?

  3. 変数を使用してBashを使用して複数行の文字列を記述する方法は?

  1. 自動化のためのBashの使用

  2. Bash で関数を作成して呼び出す方法

  3. zshで数値変数をゼロパディングする方法(おそらくbashも?)