自宅へ Android で RDP 接続

自宅へ Android で RDP 接続

自宅 Windows 端末へ RDP でリモート接続をするのに、Android 端末から ssh 接続して、ポート転送によってリモートデスクトップを行ってみた。

接続に利用した環境

端末 モデル バージョン
スマホ F-01K Android 8.1.0
Windows BTO PC Windows 10 Pro 1803

Android にインストールするアプリ

https://play.google.com/store/apps/details?id=com.microsoft.rdc.android
検証バージョン:8.1.71.387

  • ConnectBot

https://play.google.com/store/apps/details?id=org.connectbot&hl=ja
検証バージョン:「デバイスにより異なります」とあったが、Android 上では 1.9.5 だった。(更新日は 2018年11月9日)

操作方法

大雑把な流れを言うと

  1. Microsoft Remote Desktop」でアカウントと接続先サーバ情報を設定。
  2. 「ConnecctBot」で、接続サーバにアクセスするための踏み台となる SSH サーバにログインして、ローカルから接続先サーバへのポート転送を行う。
  3. Microsoft Remote Desktop」で、ポート転送されるローカルのポート番号にアクセスする。
初期設定

各環境にて読み替えが必要なものの、ここでは以下をサンプルとして記入する。

Windows 端末:192.168.0.1:3389
Android 端末:127.0.0.1:13389 → 192.168.0.1:3389 へポート転送

まず、Microsoft Remote Desktop の接続設定を行っておく。

起動したところの設定ボタン(画面左上の三の線)から「User Accounts」を選択する。
右上の「+」ボタンから、アクセス先 Windows に RDP を受け付けるアカウントを登録しておく。

Remote Desktop のホーム画面右上「+」ボタンをタップして「Desktop」を選択する。

この時、Wi-Fi 接続しているとローカルネットワークにある Windows 端末を表示してくれるみたい。
「ADD MANUALLY」をタップする。

「PC name」 に、ポート転送を行うローカルのアクセス先を入力する。
たとえば、127.0.0.1:13389 等。

「User name」 は、上記で入力した User Acccounts を選択する。

アクセス開始

「ConnectBot」で自宅 Linux サーバ等、ポート転送を有効にできる端末へログインする。

ConnectBot 右上の設定ボタン(3つの点)をタップして「ポート転送」を選択する。
右下の「+」をタップして、ポート転送設定を入力する。
鍵の名前:RDP(任意:SSHポート転送設定の名前)
タイプ:「ローカル」を選択
ソースポート:13389
転送先:192.168.0.1:3389

入力したら「ポート転送の作成」ボタンをタップして確定する。
ポート転送はもう有効になっているので、取り消し線で消されていないことを確認して画面左上の「←」で元の画面に戻る。
(取り消し線は、設定が無効であることを示すため)

接続が有効(レが入っていること)を確認したら、ホームボタンでホームに戻れば、バックグラウンド動作してくれている。

Microsoft Remote Desktop」を起動して、ポート転送元「127.0.0.1:13389」へアクセスする。
接続してよいか聞かれるので、内容を確認して「CONNECT」をタップする。

あとは、通常の RDP 接続と同じなので割愛。

アクセス終了

Microsoft Remote Desktop」を終了する。
「ConnectBot」で exit を入力するなど、セッションを終える。

終わらせておかないと、いつまでもセッションが残ったままなので注意する。


覚えだけ覚えておいて使っていなかったんだけど、出先で Windows 端末がなく、取りに戻る時間的余裕も体力もなく、のに自宅端末内にしかない資料を送付したい……ということがあって助かった経験あり。
そんなの本当に極まれなんだけど、緊急時っていうのは本当にあるもんだねと。

自宅へリモートデスクトップ SSH ポート転送版

自宅 Windows 端末へ RDP でリモート接続をするのに、今までは専用の Windows 端末を用意して、ポート番号を変更して使っていたのだけれど、とうとう壊れてしまった。
困った。
少なくとも、アクセス手段は 2 種類欲しい。

ということで、今まで資料を読んでもよく理解できなくて使ってなかった、TeraTerm のポート転送を勉強し直してみて、あっさりと繋がったのでメモ。


まず、SSH ポート転送(SSH ポートフォワーディング)の概念を勘違いしてたことに気づいた。
TeraTerm がどのポートで受けて、どこへ転送するのか。
僕の場合は、そこを押さえきれてなかったので失敗してた。

さて、今回ちゃんと繋がった SCP での接続が、こんなイメージ。

f:id:KuroNeko666:20190526152739p:plain
SCP

それから、リモートデスクトップを使う場合のイメージがこちら。

f:id:KuroNeko666:20190526153713p:plain
TeraTerm で RDP

TeraTerm 設定

これを TeraTerm で設定する場合、自分が何番のポートで受けて、どこへ繋ぐのかを入力。

接続方法としては、まず TeraTerm で接続先ネットワーク上のサーバ(今回の例だと y.y.y.y )を踏み台として使うので、ログインまで済ませておく。

TeraTermSSH転送設定は、メニューから「設定(S)」「SSH転送(O)」を選択する。

f:id:KuroNeko666:20190526154231p:plain
TeraTerm SSH転送 設定メニュー

SSHポート転送画面で、追加ボタンをクリック。

f:id:KuroNeko666:20190526154435p:plain
TeraTerm SSHポート転送 画面

SSHポート転送画面で、自分の端末の何番ポートでうけて、どの相手に転送するのかを設定する。

f:id:KuroNeko666:20190526153903p:plain
TeraTerm RDP 設定例

設定が終わったら、SSHポート転送の、ポート転送を行う向きの選択画面で OK をクリック。
SSHポート転送の、ポート転送設定一覧画面で OK をクリック。

ちなみに、Xクライアントアプリケーションの転送にチェックを入れると、こちらは TeraTerm の再起動が必要な設定なので、TERATERM.ini といったファイルへ設定を保存することと、TERATERM 起動時の読み出しが必要になってくる。
(今回のポート転送だけだったら、TeraTerm 再起動は不要)

リモートデスクトップ (RDP) 設定

上記のリモートログイン及びポート転送設定で、接続先ネットワークの端末 z.z.z.z:3389 へのアクセス経路ができているので、今度は RDP での指定方法を。

リモートデスクトップは、TeraTerm の転送元ポートを指定してあげる。

f:id:KuroNeko666:20190526155657p:plain
RDP 設定例

TeraTerm でログインした y.y.y.y:22 を通して 127.0.0.1:13389 → z.z.z.z:3389 と転送されるので、ファイアウォール等で制限されていない限り、これでリモートデスクトップが成立する。

戯言

うーん……
僕の場合は「まず、何がどう繋がっているか」が全部見えていないと、設定のできない人ってことだな。
とりあえず、自宅 PC へ接続する方法としては、これで2種類できた。
・Open VPN を使って端末自体を自宅ネットワークに属させ、プライベート IP アドレスで RDP する方法。
TeraTerm のポート転送を使って RDP する方法。

簡単に NAS へアクセスしたいので、基本はこれからも Open VPN を使うことになると思うけど、Open VPN のためにログインするサーバが故障した場合でも、SSH は別なサーバなので、副次的なアクセス手段ができたので満足。


両方死んだら……?


帰宅するまでは、どうしようもないね。

引数に与えられたコマンドを実行して、その証跡を残す関数

引数に与えられたコマンドを実行して、その証跡を残す関数の作成。

まず、やりたいことができるかどうかの確認。
引数に与えられたコマンドを実行できるか。

$ aaa="echo sample message"
$ eval $aaa
sample message

基本は、これでやれそう。

次に、証跡を残すこと。
実行結果を変数に入れて、適当に出力できるようにしてみる。

$ type eval
eval はシェル組み込み関数です
$
$ aaa="echo sample message"
$ bbb=$(eval $aaa)
$ echo $bbb
sample message

やりたいことは、ほぼ完成。
エラー処理各種を試す。

$ aaa="echo sample message 1>&2"
$ bbb=$(eval $aaa)
sample message

標準エラー出力は xxx=$() という形式では含んでくれないので、含めておこうか。
ついでに、正常な場合の Exit Status も確認。

$ aaa="echo sample message 1>&2"
$ bbb=$(eval $aaa 2>&1)
$ ccc=$?
$ logger -t test $bbb
$ tail -1 /var/log/messages
May 25 08:55:37 tsrw01 test: sample message
$ echo $ccc
0

コマンドが異常終了した場合の Exit Status も確認

$ ls /etc/dummy
ls: '/etc/dummy' にアクセスできません: そのようなファイルやディレクトリはありません
$ echo $?
2
$ 
$ aaa="ls /etc/dummy"
$ bbb=$(eval $aaa 2>&1)
$ ccc=$?
$ echo "$(date +%Y/%m/%d %H:%M:%S) $bbb" > /tmp/ddd
$ cat /tmp/ddd
2019/05/25 09:04:53 ls: '/etc/dummy' にアクセスできません: そのようなファイルやディレクトリはありません
$ echo $ccc
2

ということで、内容を見やすく整理しつつ関数にまとめる。

#!/bin/bash

pLogFile=cmd.log

fnLog() {
    echo "$(date +'%Y/%m/%d %H:%M:%S') $*" >> ${pLogFile}
}

fnCmd() {
    fnLog "Command line: $*"

    # Command exec
    pOutput=$(eval "$*" 2>&1)
    pExitStatus=$?

    # Log output
    fnLog "=== standard output / standard error output ========="
    echo "${pOutput}" >> ${pLogFile}
    fnLog "====================================================="
    fnLog "Exit status: ${pExitStatus}"

    return ${pExitStatus}
}

こうしておくと、実行したコマンドの出力結果を自由に出力しつつ、戻り値も確認できる。

ログの中身は各行に必ず日時がないとヤダ……という場合は、こんな感じ。

#!/bin/bash

pLogFile=cmd.log

fnLog() {
    echo "$(date +'%Y/%m/%d %H:%M:%S') $*" >> ${pLogFile}
}

fnCmd() {
    fnLog "Command line: $*"

    # Command exec
    pOutput=$(eval "$*" 2>&1)
    pExitStatus=$?

    # Log output
    fnLog "=== standard output / standard error output ========="
    echo "${pOutput}" | while read line
        do
            fnLog "$line"
        done
    fnLog "====================================================="
    fnLog "Exit status: ${pExitStatus}"

    return ${pExitStatus}
}


個人的には、スクリプトログなんて実行が失敗した時しか見ないんだから、コマンドの出力結果に日時情報を入れるだけムダだし、紛らわしくて困ると思うんだけど……。
パッと見のキレイさの方が重要みたい。
どうせなら、4桁くらいで固定で行番号を入れてみる?

#!/bin/bash

pLogFile=cmd.log

fnLog() {
    echo "$(date +'%Y/%m/%d %H:%M:%S') $*" >> ${pLogFile}
}

fnCmd() {
    fnLog "Command line: $*"

    # Command exec
    pOutput=$(eval "$*" 2>&1)
    pExitStatus=$?

    # Log output
    fnLog "=== standard output / standard error output ========="
    i=1
    echo "${pOutput}" | while read line
        do
            fnLog $(printf "%04d: %s" $i "$line")
            i=$(($i + 1))
        done
    fnLog "====================================================="
    fnLog "Exit status: ${pExitStatus}"

    return ${pExitStatus}
}

fnCmd "ls /etc/hosts*"

これを chk.sh に書き込んで実行してみた結果。

$ bash ./chk.sh
$ cat cmd.log
2019/05/25 09:39:11 Command line: ls /etc/hosts*
2019/05/25 09:39:12 === standard output / standard error output =========
2019/05/25 09:39:12 0001: /etc/hosts
2019/05/25 09:39:12 0002: /etc/hosts.allow
2019/05/25 09:39:12 0003: /etc/hosts.deny
2019/05/25 09:39:12 =====================================================
2019/05/25 09:39:12 Exit status: 0
$ 

うーん……
他の分も、同じコマンドを実行させるように sample1.sh / sample2.sh として書き込んで実行してみる。

sample1.sh は、実行結果に日時情報のないもの。

$ [ -f cmd.log ] && rm cmd.log
$ bash ./sample1.sh
$ cat cmd.log
2019/05/25 09:43:56 Command line: ls /etc/hosts*
2019/05/25 09:43:56 === standard output / standard error output =========
/etc/hosts
/etc/hosts.allow
/etc/hosts.deny
2019/05/25 09:43:56 =====================================================
2019/05/25 09:43:56 Exit status: 0


sample2.sh は、実行結果に日時情報を入れたもの。

$ [ -f cmd.log ] && rm cmd.log
$ bash ./sample2.sh
$ cat cmd.log
2019/05/25 09:47:00 Command line: ls /etc/hosts*
2019/05/25 09:47:00 === standard output / standard error output =========
2019/05/25 09:47:00 /etc/hosts
2019/05/25 09:47:00 /etc/hosts.allow
2019/05/25 09:47:00 /etc/hosts.deny
2019/05/25 09:47:00 =====================================================
2019/05/25 09:47:00 Exit status: 0

実行結果に、日時情報と行番号を入れたものを再度。

$ bash ./chk.sh
$ cat cmd.log
2019/05/25 09:39:11 Command line: ls /etc/hosts*
2019/05/25 09:39:12 === standard output / standard error output =========
2019/05/25 09:39:12 0001: /etc/hosts
2019/05/25 09:39:12 0002: /etc/hosts.allow
2019/05/25 09:39:12 0003: /etc/hosts.deny
2019/05/25 09:39:12 =====================================================
2019/05/25 09:39:12 Exit status: 0
$ 


……こうして並べてみると、印象がすごく違うね。

シェルスクリプト ログ出力関数あれこれ

スクリプトの実行ログ関係

シェルスクリプトを実行した際、デバッグするなら bash -x xxxx.sh という感じで、デバッグ実行することが多い。
でも、一部の管理職には不評なんだよね。
実行ログで出してくれと。

そんな理由で、スクリプトログを出力する関数を使うことが多いため、自分で使いやすい関数を汲んでおきたくなった。
毎回、現場に合わせて作るのも結構辛くなってきたので。

仕様

・指定のディレクトリにスクリプトログを置く。
・ログファイル名は、スクリプト名の拡張子を .log に置き換え、日付を含めたもの。
・ログは、yyyy/mm/dd hh:mm:ss のあとにメッセージを入れる。
・出力先は、変数指定がなければ自動で決める。


この仕様から、細かい条件を各環境に合わせれば良い。


自分の趣味を色濃く反映するものとして、
・関数名は fnLogger とする。
・出力先変数は pLogFile とする。

関数は英語で function と言うので、関数定義には fn を付けている。
変数なら、特に例外規定がなければ Parameter とか Variable なので p を。


fXxxx だと、フラグ関係に。
nXxxx だと、数値関係に。


それから、使い慣れているキャメルケースで変数名を定義する。
(スネークケースは好きではないが、使わないとは言っていない)


他のスクリプト等で変数が重複する確率が低いので、特に規定がない場合はこれを使っていたりする。
word とかの MS-Office 製品で、説明資料を作るときに下手に _ が入っていると、波線で見えなくなることがあるんだよね。
いや、全部大文字にしていれば問題ないんだけど、スネークケースを使う人って、なぜか小文字にしたがる……。


ということで、シェルスクリプトの変数は、会社で使うなら全部大文字が良いです。

スクリプト(関数)

基本形となる関数が、こちら。

fnLogger() {
    pLogFile="${pLogFile:=$(/usr/bin/basename ${0} .sh)_$(date +'%Y%m%d').log}"
    echo "$(date +'%Y/%m/%d %H:%M:%S') $*" >> ${pLogFile}
}

上記 pLogFile という変数を、関数外で定義しておけば、ログ出力先はその指定に従う。



フラグ次第で、出力先を変えるタイプ。

### Parameter and Variable

## Directory
pLogDir=/var/log/script

## File
pLogFile=$(/usr/bin/basename ${0} .sh)_$(date +'%Y%m%d').log

## Path
pLogPath=${pLogDir}/${pLogFile}

## Flag
# 0 = Script log
# 1 = /var/log/messages
fOutput=0

### Function
fnLogger() {
    if [ "${fOutput}" -eq "0" ]; then
        echo "$(date +'%Y/%m/%d %H:%M:%S') $*" >> ${pLogPath}
    else
        logger "$*"
    fi
}


作りこもうとすると、ログを出力したいだけでもこんなに長くなってしまうんだよね……
まぁ、他のファイルで定義しておいて、それを . で読み込むことが多くなってきたから気にならないんだけど。


いくつか出力先を選びたいなら、こんな手法も。

### Parameter and Variable

## Parameter
pID=$(/usr/bin/basename ${0} .sh)
pYYYYMMDD=$(date +'%Y%m%d')

## Directory
pLogDir=/var/log/script

## File
pLogStdFile=${pID}_${pYYYYMMDD}.log
pLogErrFile=${pID}_${pYYYYMMDD}_Err.log

## Path
pLogStdPath=${pLogDir}/${pLogStdFile}
pLogErrPath=${pLogDir}/${pLogErrFile}

## Flag
# 0 = Script log - Standard
# 1 = Script log - Error
# 2 = /var/log/messages
fOutput=0

### Function
fnLogger() {
    p1st=$1
    shift

    case ${p1st} in
        0 ) echo "$(date +'%Y/%m/%d %H:%M:%S') $*" >> ${pLogStdPath};;
        1 ) echo "$(date +'%Y/%m/%d %H:%M:%S') $*" >> ${pLogErrPath};;
        2 ) logger "$*";;
    esac
}

### Main

# 0 Standard
fnLogger 0 "Message"

# 1 Error
fnLogger 1 "Error Message"

# 2 /var/log/messages
fnLogger 2 "/var/log/messages output message"


パッと見て、0~2 の数字も一緒に出力されそうに見えるけど、実行結果はこんな感じ。

$ bash ./sample.sh
$ 
$ cat sss_20190519.log
2019/05/19 14:04:13 Message
$ 
$ cat sss_20190519_Err.log
2019/05/19 14:04:13 Error Message
$ 
$ tail -1 /var/log/messages
May 19 14:04:13 (ホスト名) (ユーザ名): /var/log/messages output message


shift コマンドが引数をひとつ消してくれる効果を持っている。


それから、コードを利用したログ出力方法もこんな一案。
コード自体を変数名としたもの。

$ cat mcode.txt
A000E="Sample essage."
A001E="Message 1st"
A002E="Message 2nd"
$ 
$ cat sample2.sh
#!/bin/bash

. ./mcode.txt

fnLogCode() {
    echo "${1}: $(eval echo '$'${1})"
}

fnLogCode A000E
$ 
$ bash ./sample2.sh
A000E: Sample essage.
$ 


コードは csv 形式で記載されているもの。

$ cat code.csv
I0000,Sample message
I0001,Information message.
W0001,Warning message.
E0001,Error message.

これを抽出する方法を考えてみる。まずはシンプルに grep で。

$ grep I0000 code.csv
I0000,Sample message
$ 
$ grep I0000 code.csv | cut -d "," -f 1
I0000
$ 
$ grep I0000 code.csv | cut -d "," -f 2
Sample message
$ 
### Parameter and Variable

## Parameter
pID=$(/usr/bin/basename ${0} .sh)
pYYYYMMDD=$(date +'%Y%m%d')

## Directory
pLogDir=/var/log/script

## File
pLogCSVFile=code.csv
pLogStdFile=${pID}_${pYYYYMMDD}.log

## Path
pLogStdPath=${pLogDir}/${pLogStdFile}
pLogCSVPath=${pLogDir}/${pLogCSVFile}

### Function
fnLogger() {
    [ -r "${pLogCSVPath}" ] || return 1
    pMessage=$(grep $1 ${pLogCSVPath} | cut -d "," -f 2)
    [ "${pBaseMessage}" == "" ] && return 1

    echo "$(date +'%Y/%m/%d %H:%M:%S') ${pMessage}" >> ${pLogStdPath};;
}

これだとメッセージ内容に , を含められないので、含められるようにしたバージョン。

### Parameter and Variable

## Parameter
pID=$(/usr/bin/basename ${0} .sh)
pYYYYMMDD=$(date +'%Y%m%d')

## Directory
pLogDir=/var/log/script

## File
pLogCSVFile=code.csv
pLogStdFile=${pID}_${pYYYYMMDD}.log

## Path
pLogStdPath=${pLogDir}/${pLogStdFile}
pLogCSVPath=${pLogDir}/${pLogCSVFile}

### Function
fnLogger() {
    [ -r "${pLogCSVPath}" ] || return 1
    tMessage=$(grep $1 ${pLogCSVPath})
    [ "${tBaseMessage}" == "" ] && return 1

    pMessage=${tMessage#*,}

    echo "$(date +'%Y/%m/%d %H:%M:%S') ${pMessage}" >> ${pLogStdPath};;
}

考えていくと、いろいろとやり方はあるもんだね。

DNS over TLS/HTTPS

昨年から DNS over TLS (DoT)、DNS over HTTPS (DoH) にまつわる動きが急速に活発になっています。

この一文から始まる、以下のリンクを読んだ。

https://eng-blog.iij.ad.jp/archives/2954

関連するこちらを読むと DNS over TLS/HTTPS サービスの提供が増えてきたらしい。
https://public.dns.iij.jp/


パブリック DNS を提供する事業者が増えてきたけど、次期 Android Ver 9 でも DNS over TLS/HTTPS を利用できるようになるらしいし、潮流なんだろうか。

備忘なブレース展開と変数展開

ブレース展開やら変数展開を時々忘れるうえに意味も取り違えてたりするので、備忘代わりに。

ブレース展開

まず、ブレース展開は {} とかで展開する機能。
ブレース(Braces) は { と } の呼称(日本語だと中かっこと呼ぶ感じ)なので、ブレース内をいろいろ触るものと覚えておけば良い感じ。


よく使うのは、次の2通り。


一つ目は、羅列。

# cp /etc/hosts{,_$(date +%Y%m%d)}

これは、次のコマンドに同じ。

# cp /etc/hosts /etc/hosts_$(date +%Y%m%d)

不安な場合は、先に echo しておくと良い。

$ echo /etc/hosts{,_$(date +%Y%m%d)}
/etc/hosts /etc/hosts_20190505

それと2つ目、連続する数字の出力。

$ echo {0..10}
0 1 2 3 4 5 6 7 8 9 10

と書くと、0~10 の数字が連続で出力される。

$ echo {000..10}
000 001 002 003 004 005 006 007 008 009 010

と書くと、000~010 という 0 パディングされた数字が連続で出力される。

$ echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z

だと、a~z までの小文字だし、

$ echo {A..Z}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

だと、A~Z までの大文字になる。

途中からの出力 {8..12} や、途中までの出力 {a..f} 等も OK だった。

$ echo {8..12}
8 9 10 11 12
$ echo {a..f}
a b c d e f
$ echo {A..D}{00..15}
A00 A01 A02 A03 A04 A05 A06 A07 A08 A09 A10 A11 A12 A13 A14 A15 B00 B01 B02 B03 B04 B05 B06 B07 B08 B09 B10 B11 B12 B13 B14 B15 C00 C01 C02 C03 C04 C05 C06 C07 C08 C09 C10 C11 C12 C13 C14 C15 D00 D01 D02 D03 D04 D05 D06 D07 D08 D09 D10 D11 D12 D13 D14 D15
$ echo data_{A..C}_201905{01..5}.txt
data_A_20190501.txt data_A_20190502.txt data_A_20190503.txt data_A_20190504.txt data_A_20190505.txt data_B_20190501.txt data_B_20190502.txt data_B_20190503.txt data_B_20190504.txt data_B_20190505.txt data_C_20190501.txt data_C_20190502.txt data_C_20190503.txt data_C_20190504.txt data_C_20190505.txt
$ echo {8..03}
08 07 06 05 04 03

変数展開

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/contents.html

これの 「Shell Command Language」内「2.6.2 Parameter Expansion」の部分。
Posix の定義としては「変数拡張」と和訳したほうがヨサゲに見える……
とはいえ、英文から日本語の直訳だと意味が通じにくい部分があるので、ある程度意訳しておく。


以下が、変数展開の記述方法と、その出力結果を意訳したもの。


なお、表名が長いくせに分かりにくいので、先に解説を……
「parameter が Null 以外で設定済」とは、たとえば parameter="A" の状態。定義してあるけど、Null(何もない)以外の状態であるということ。
「parameter が Null で設定済」とは、parameter="" の状態。定義してあるけど、Null(何もない)状態であるということ。
「parameter が未設定」とは、そのものズバリ。まだ定義していない状態。

変数展開記述 parameter が Null 以外で設定済 parameter が Null で設定済 parameter が未設定
${parameter:-word} parameter を利用 word を利用 word を利用
${parameter-word} parameter を利用 null を利用 word を利用
${parameter:=word} parameter を利用 word をアサイ word をアサイ
${parameter=word} parameter を利用 null を利用 word をアサイ
${parameter:?word} parameter を利用 エラー終了 エラー終了
${parameter?word} parameter を利用 null を利用 エラー終了
${parameter:+word} word を利用 null を利用 null を利用
${parameter+word} word を利用 word を利用 null を利用


それぞれの意味は、以下の通り

記述内容 意味
parameter を利用 parameter に定義済みの、値が出力される
word を利用 指定した word で、出力される
null を利用 Null で出力される
word をアサイ parameter に word が代入されつつ、出力される
エラー終了 スクリプトがそこで終了する


ちょっと読み方が特殊だけど、たとえば parameter が Null か未設定のとき、指定した文字列を使いたいというときは ${parameter:-word} を使う。これは、parameter の中身をいじらない。
自分の使い方としては、引数の指定とか。

$ Default="filename"
$ SourceFile=${1:-${Default}}

こうすると、引数の1番目に何も指定されてなくても別途指定しているデフォルト値を利用できる。
もちろん他の使い方もある。

さらに同じ条件だけど parameter の中身に指定した文字列を入れておきたいときは ${parameter:=word} を使う。

#!/bin/bash
set -u
value=$1
num=$((${value:=0} + 1))
echo $num

こんな感じかなぁ?
引数に入力した値の確認とか、計算結果のエラーチェックとか、まったくやっていないけど、使い方があんまり思い浮かばない。もうちょっとマシなサンプルを書ければよかったけど。


それから、変数の中に入っている文字列の長さ(数)を取得する指定は、次の通り。
${#parameter}

$ parameter="example"
$ echo ${#parameter}
7


それから、パターンでの文字列削除方法は、次の通り。

${parameter%word} 最短後置パターンの削除
${parameter%%word} 最長後置パターンの削除
${parameter#word} 最短前置パターンの削除
${parameter##word} 最長前置パターンの削除

パターンにマッチさせる使い方として、ディレクトリを例にする。

SamplePath=/var/log/messages

最短後置パターン(ファイル名もしくは最後のディレクトリ名を消す)

$ echo ${SamplePath%/*}
/var/log

最長後置パターン(この例だと全部消える)

$ echo ${SamplePath%%/*}

最短前置パターン(この例だと最初の / だけ消える)

$ echo ${SamplePath#*/}
var/log/messages

最長前置パターン(ファイル名もしくは最後のディレクトリ名だけ残す)

$ echo ${SamplePath##*/}
messages

ちなみに、ディレクトリ名を消すコマンドに basename がある。
basename コマンドの優位点は、残るファイル名にも加工を掛けられること。
たとえば /usr/local/script/example.sh の、example 部分だけを使いたいとき。

ScriptLog=$(/usr/bin/basename $0 .sh).log

こんな感じで、実行パスからログファイル名を生成するなんてこともできる。


それから、Bash 限定と思われるけど、変数内で置換もできる。

$ Parameter="abce"
$ echo ${Parameter/e/d}
abcd

これは一か所だけなので、全部置換したい場合にはこちら。

$ Parameter="abcefg alphabet"
$ echo ${Parameter//e/d}
abcdfg alphabdt

OpenVPN で RDP 通信 トラフィック計算

自宅に VPN 接続して、しばらく RDP でいろいろ弄っていた。
これは固定回線だから気にならないけど、テザリングを利用した場合にどれくらいになるのか…その目安を知っておきたいとおもって調べてみた。

まずは、自宅のトラフィックを参照してみる。

VPN 用の Raspberry Pi だと、こんな感じ。

f:id:KuroNeko666:20190502131538p:plain
VPN 通信量

インターネット側との通信でも、それほど変化はない感じ。

f:id:KuroNeko666:20190502132057p:plain
インターネット側

ということで、僕の作業だと最大で 2.62Mbps 程度のトラフィックがでるみたい。
それから、これは 5 分間隔の情報であって、bps は秒の単位。
つまり 5 分のうちの 1 秒間に平均 2.62Mbit の通信が発生していることを意味している。

( 2.62 (Mbps) * 60 (秒) * 5 (分)) / 8 (bit) という計算。
だいたい 5 分で 19.65 MBのデータ通信。

さらに計算すると、僕の場合 60 分だと最大で 235.8 MB のデータをやり取りする可能性もある。

5GB/月 の通信量で契約しているので、5,000~5120MB *1 として計算。
5,000 MB / 236 MB = 21.186
最悪な条件で計算すると、21 時間程度しかもたない計算。

うわー……

いや、最悪な条件での計算だからもう少し余裕はあるけど、RDP での接続は最低限で済ませられるようにしておいた方が心にも余裕がでるね。

6月に入院生活を送る予定があるから、なおさら。

*1:Byte計算のとき、1024 か 1000 かで変わる。PC だと 1,024 だけど、通信の世界では 1,000で固定