Mazn.net

やってみて 調べてみて 苦労しなけりゃ 箱は動かじ

bashの変数のスコープまとめ

bash-scope



たまにbashスクリプトを書くと忘れてしまってるので、bashの変数のスコープについてのメモ。

  1. 通常の変数はシェル変数と呼ばれ、関数外でも関数内でも宣言可
    1. シェル変数のスコープはグローバル変数となり、同スクリプト内(同プロセス内)のどこからでも参照・変更できる
      • 関数内でdeclareを使って変数を宣言する場合、グローバルにするには -g オプションが必要
      • 別のスクリプトファイルであっても、sourceコマンドで読み込んだ(呼び出した)場合は、そのプロセスは呼び出し元のスクリプト内に記述された事と同等の扱いとなる
    2. サブシェル として呼び出した場合、サブシェル内で同変数の参照はできるが、変更しても呼び出し元の変数には影響を与えない
      • 呼び出し時にパイプを使用すると、サブシェル内で実行されるので注意
    3. 別のスクリプト(別プロセス)を呼び出した場合、呼び出した先のスクリプト内で呼び出し元のシェル変数は参照も変更もできない
  2. local宣言された変数はローカル変数と呼ばれ、関数内でのみ宣言可
    1. 関数内で宣言されたローカル変数は、その関数内および同スクリプト内の呼び出し先関数内で自由に参照・変更ができるが、呼び出し先にない関数からは参照も変更もできない
      • 「ローカル変数はその関数内でのみ有効」と説明する人もいるが、ローカル変数であっても、同関数内で呼び出した先の関数内では参照・変更できるため注意。bashではその関数内だけに完全に閉じた変数を宣言する機能は今(ver5)のところなさそう。
    2. サブシェルとして呼び出した場合、サブシェル内で同変数の参照はできるが、変更しても呼び出し元の変数には影響を与えない
      • 呼び出し時にパイプを使用すると、サブシェル内で実行されるので注意
    3. 別のスクリプト(別プロセス)を呼び出した場合、呼び出した先のスクリプト内で呼び出し元のローカル変数は参照も変更もできない
    4. 関数呼び出し元に同名の変数が宣言済みの場合、呼び出し元の同名の変数の値は参照も変更もできなくなる
      • ただしbash 5.0以降では、shopt -s localvar_inherit を設定すると呼び出し元の同名の変数の値が自動で引き継がれ参照できるようになる
  3. export宣言された変数は環境変数と呼ばれる
    1. シェル変数の場合は1-3動作が、ローカル変数の場合は2-3の動作が変わり、別のスクリプトを呼び出した場合、呼び出した先のスクリプト内で呼び出し元のシェル変数やローカル変数を参照できるようになる。ただし変更はできない
      • ただし配列や連想配列は、exportしても参照できない (exportできない)

以下のテストスクリプトを使って動作確認してみました。実行環境のbashのバージョンは5.1.3です。

bash-scope-test.sh

#!/bin/bash

primal_function() {
	declare -g -x export_global="primal value"
	local -x export_local="primal value"
	declare -g -x -a export_global_array=("primal" "value")
	local -x -a export_local_array=("primal" "value")
	declare -g global="primal value"
	declare -g -a global_array=("primal" "value")
	local local="primal value"
	local -a local_array=("primal" "value")

	echo "---- call function ----"
	called_function
	echo "---- call function (returned) ----"
	echo "       export_global=$export_global"
	echo "        export_local=$export_local"
	echo " export_global_array=${export_global_array[*]}"
	echo "  export_local_array=${export_local_array[*]}"
	echo "              global=$global"
	echo "        global_array=${global_array[*]}"
	echo "               local=$local"
	echo "         local_array=${local_array[*]}"

	# reset
	export_global="primal value"
	export_local="primal value"
	export_global_array=("primal" "value")
	export_local_array=("primal" "value")
	global="primal value"
	global_array=("primal" "value")
	local="primal value"
	local_array=("primal" "value")

	echo
	echo "---- call function as subshell ----"
	(called_function)
	echo "---- call function as subshell (returned) ----"
	echo "       export_global=$export_global"
	echo "        export_local=$export_local"
	echo " export_global_array=${export_global_array[*]}"
	echo "  export_local_array=${export_local_array[*]}"
	echo "              global=$global"
	echo "        global_array=${global_array[*]}"
	echo "               local=$local"
	echo "         local_array=${local_array[*]}"
	# reset
	export_global="primal value"
	export_local="primal value"
	export_global_array=("primal" "value")
	export_local_array=("primal" "value")
	global="primal value"
	global_array=("primal" "value")
	local="primal value"
	local_array=("primal" "value")

	echo
	echo "---- call function as pipe ----"
	called_function | cat
	echo "---- call function as pipe (returned) ----"
	echo "       export_global=$export_global"
	echo "        export_local=$export_local"
	echo " export_global_array=${export_global_array[*]}"
	echo "  export_local_array=${export_local_array[*]}"
	echo "              global=$global"
	echo "        global_array=${global_array[*]}"
	echo "               local=$local"
	echo "         local_array=${local_array[*]}"
	# reset
	export_global="primal value"
	export_local="primal value"
	export_global_array=("primal" "value")
	export_local_array=("primal" "value")
	global="primal value"
	global_array=("primal" "value")
	local="primal value"
	local_array=("primal" "value")

	echo
	echo "---- call other script ----"
	./other-script.sh
	echo "---- call other script (returned) ----"
	echo "       export_global=$export_global"
	echo "        export_local=$export_local"
	echo " export_global_array=${export_global_array[*]}"
	echo "  export_local_array=${export_local_array[*]}"
	echo "              global=$global"
	echo "        global_array=${global_array[*]}"
	echo "               local=$local"
	echo "         local_array=${local_array[*]}"
	# reset
	export_global="primal value"
	export_local="primal value"
	export_global_array=("primal" "value")
	export_local_array=("primal" "value")
	global="primal value"
	global_array=("primal" "value")
	local="primal value"
	local_array=("primal" "value")

	echo
	echo "---- call redefined function ----"
	called_redefined_function
	echo "---- call redefined function (returned) ----"
	echo "       export_global=$export_global"
	echo "        export_local=$export_local"
	echo " export_global_array=${export_global_array[*]}"
	echo "  export_local_array=${export_local_array[*]}"
	echo "              global=$global"
	echo "        global_array=${global_array[*]}"
	echo "               local=$local"
	echo "         local_array=${local_array[*]}"
	# reset
	export_global="primal value"
	export_local="primal value"
	export_global_array=("primal" "value")
	global="primal value"
	global_array=("primal" "value")
	local="primal value"
	local_array=("primal" "value")

	echo
	echo "---- call redefined function as localvar_inherit ----"
	shopt -s localvar_inherit
	called_redefined_function
	echo "---- call redefined function as localvar_inherit (returned) ----"
	echo "       export_global=$export_global"
	echo "        export_local=$export_local"
	echo " export_global_array=${export_global_array[*]}"
	echo "  export_local_array=${export_local_array[*]}"
	echo "              global=$global"
	echo "        global_array=${global_array[*]}"
	echo "               local=$local"
	echo "         local_array=${local_array[*]}"
	shopt -u localvar_inherit
	# reset
	export_global="primal value"
	export_local="primal value"
	export_global_array=("primal" "value")
	export_local_array=("primal" "value")
	global="primal value"
	global_array=("primal" "value")
	local="primal value"
	local_array=("primal" "value")	
}

# caelld from primal_function()
called_function() {
	echo "       export_global=$export_global"
	echo "        export_local=$export_local"
	echo " export_global_array=${export_global_array[*]}"
	echo "  export_local_array=${export_local_array[*]}"
	echo "              global=$global"
	echo "        global_array=${global_array[*]}"
	echo "               local=$local"
	echo "         local_array=${local_array[*]}"

	export_global="**called value**"
	export_local="**called value**"
	export_global_array=("**called" "value**")
	export_local_array=("**called" "value**")
	global="**called value**"
	global_array=("**called" "value**")
	local="**called value**"
	local_array=("**called" "value**")
}

# caelld from primal_function()
called_redefined_function() {
	# local export_global
	# local export_local
	# local -a export_global_array
	# local -a export_local_array
	# local global
	# local -a global_array
	# local local
	# local -a local_array

	declare -g -x export_global
	local -x export_local
	declare -g -x -a export_global_array
	local -x -a export_local_array
	declare -g global
	declare -g -a global_array
	local local
	local -a local_array

	echo "       export_global=$export_global"
	echo "        export_local=$export_local"
	echo " export_global_array=${export_global_array[*]}"
	echo "  export_local_array=${export_local_array[*]}"
	echo "              global=$global"
	echo "        global_array=${global_array[*]}"
	echo "               local=$local"
	echo "         local_array=${local_array[*]}"

	export_global="**redefined value**"
	export_local="**redefined value**"
	export_global_array=("**redefined" "value**")
	export_local_array=("**redefined" "value**")
	global="**redefined value**"
	global_array=("**redefined" "value**")
	local="**redefined value**"
	local_array=("**redefined" "value**")
}

# called from main()
other_function() {
	echo "       export_global=$export_global"
	echo "        export_local=$export_local"
	echo " export_global_array=${export_global_array[*]}"
	echo "  export_local_array=${export_local_array[*]}"
	echo "              global=$global"
	echo "        global_array=${global_array[*]}"
	echo "               local=$local"
	echo "         local_array=${local_array[*]}"

	export_global="**other value**"
	export_local="**other value**"           # this is not declared, so it becomes global
	export_global_array=("**other" "value**")
	export_local_array=("**other" "value**") # this is not declared, so it becomes global
	global="**other value**"
	global_array=("**other" "value**")
	local="**other value**"                  # this is not declared, so it becomes global
	local_array=("**other" "value**")        # this is not declared, so it becomes global
}

primal_function

echo
echo "---- main() ----"
echo "       export_global=$export_global"
echo "        export_local=$export_local"
echo " export_global_array=${export_global_array[*]}"
echo "  export_local_array=${export_local_array[*]}"
echo "              global=$global"
echo "        global_array=${global_array[*]}"
echo "               local=$local"
echo "         local_array=${local_array[*]}"

echo "---- call other function in main() ----"
other_function

echo "---- call other function in main() (returned) ----"
echo "       export_global=$export_global"
echo "        export_local=$export_local"
echo " export_global_array=${export_global_array[*]}"
echo "  export_local_array=${export_local_array[*]}"
echo "              global=$global"
echo "        global_array=${global_array[*]}"
echo "               local=$local"
echo "         local_array=${local_array[*]}"

other-script.sh

#!/bin/bash

echo "       export_global=$export_global"
echo "        export_local=$export_local"
echo " export_global_array=${export_global_array[*]}"
echo "  export_local_array=${export_local_array[*]}"
echo "              global=$global"
echo "        global_array=${global_array[*]}"
echo "               local=$local"
echo "         local_array=${local_array[*]}"

export_global="**other script value**"
export_local="**other script value**"
export_global_array=("**other" "script value**")
export_local_array=("**other" "script value**")
global="**other script value**"
global_array=("**other" "script value**")
local="**other script value**"
local_array=("**other" "script value**")

実行結果

$ ./bash-scope-test.sh
---- call function ---- ★通常の関数呼び出しは全部参照可能
export_global=primal value
export_local=primal value
export_global_array=primal value
export_local_array=primal value
global=primal value
global_array=primal value
local=primal value
local_array=primal value
---- call function (returned) ---- ★通常の関数呼び出し先では呼び出し元を変更可
export_global=**called value**
export_local=**called value**
export_global_array=**called value**
export_local_array=**called value**
global=**called value**
global_array=**called value**
local=**called value**
local_array=**called value**

---- call function as subshell ---- ★サブシェルでの呼び出しは全部参照可能
export_global=primal value
export_local=primal value
export_global_array=primal value
export_local_array=primal value
global=primal value
global_array=primal value
local=primal value
local_array=primal value
---- call function as subshell (returned) ---- ★サブシェルでの呼び出し先では呼び出し元を変更不可
export_global=primal value
export_local=primal value
export_global_array=primal value
export_local_array=primal value
global=primal value
global_array=primal value
local=primal value
local_array=primal value

---- call function as pipe ---- ★パイプでの呼び出しはサブシェルと同等
export_global=primal value
export_local=primal value
export_global_array=primal value
export_local_array=primal value
global=primal value
global_array=primal value
local=primal value
local_array=primal value
---- call function as pipe (returned) ----
export_global=primal value
export_local=primal value
export_global_array=primal value
export_local_array=primal value
global=primal value
global_array=primal value
local=primal value
local_array=primal value

---- call other script ---- ★他のスクリプト呼び出しではexportされた変数しか参照できない
export_global=primal value
export_local=primal value
export_global_array= ★配列はexportできない
export_local_array= ★配列はexportできない
global=
global_array=
local=
local_array=
---- call other script (returned) ---- ★他のスクリプト内で呼び出し元を変更不可
export_global=primal value
export_local=primal value
export_global_array=primal value
export_local_array=primal value
global=primal value
global_array=primal value
local=primal value
local_array=primal value

---- call redefined function ----
export_global=primal value ★ 呼び出し先で同名のグローバル変数の定義は、呼び出し元の変数がそのまま参照される
export_local= ★ 全く異なる関数内で定義されたローカル変数は参照できない
export_global_array=primal value
export_local_array=
global=primal value
global_array=primal value
local=
local_array=
---- call redefined function (returned) ----
export_global=**redefined value** ★ 呼び出し先で同名のグローバル変数の定義は、呼び出し元の変数がそのまま変更できる
export_local=primal value
export_global_array=**redefined value**
export_local_array=primal value
global=**redefined value**
global_array=**redefined value**
local=primal value
local_array=primal value

---- call redefined function as localvar_inherit ----
export_global=primal value ★localvar_inheritは、グローバルな変数の動作には影響なし
export_local=primal value ★localvar_inheritは、local宣言された変数に呼び出し元の同名の変数の値が代入される
export_global_array=primal value
export_local_array=primal value
global=primal value
global_array=primal value
local=primal value
local_array=primal value
---- call redefined function as localvar_inherit (returned) ----
export_global=**redefined value**
export_local=primal value ★localvar_inheritでも、local宣言された変数は呼び出しもとの同名の変数の値は変更されない
export_global_array=**redefined value**
export_local_array=primal value
global=**redefined value**
global_array=**redefined value**
local=primal value
local_array=primal value

---- main() ----
export_global=primal value ★関数内で定義されたグローバル変数はスクリプト内ではどこからでも参照可能
export_local=
export_global_array=primal value
export_local_array=
global=primal value
global_array=primal value
local=
local_array=
---- call other function in main() ----
export_global=primal value ★関数内で定義されたグローバル変数はスクリプト内ではどこからでも参照可能
export_local=
export_global_array=primal value
export_local_array=
global=primal value
global_array=primal value
local=
local_array=
---- call other function in main() (returned) ----
export_global=**other value**
export_local=**other value** ★other_function()内ではprimal_function()内のlocal変数は参照できないため、直接変数を変更するとグローバルとみなされてしまう
export_global_array=**other value**
export_local_array=**other value**
global=**other value**
global_array=**other value**
local=**other value**
local_array=**other value**

なお関数にlocal宣言はありません。そのため変数と異なり関数内で関数を定義した場合、関数のスコープは全てグローバルとなります。関数はexportでき、変数と同様に呼び出し先のスクリプト内でも使用できます。

シェルスクリプト内でのaliasの展開タイミング

シェルスクリプト(bash)内では、デフォルトでaliasを使用できません。使用するにはshoptコマンドを使って、明示的に有効化する必要があります。

aliastest.sh

#!/usr/bin/bash
shopt -s expand_aliases

alias myecho='echo myecho'
myecho
$ ./aliastest.sh
myecho

ただしシェルスクリプト利用時は、aliasが展開されるタイミングを考慮しないと少しハマります。

aliasはスクリプト読み込み時 (関数の定義読み込み時) に展開されます

これはbashのmanにも以下のように説明されています。

Aliases are expanded when  a  command  is read, not when it is executed.

そのため、関数内でaliasを使用する場合、aliasの定義のタイミングが重要になります。以下のようにmyfunc1()内の後ろでmain_aliasを定義した場合、command not found となります。
※ 以下は、myfunc1()の実行結果が把握しやすいように、sedで整形しています。

aliastest1.sh

#!/usr/bin/bash
shopt -s expand_aliases

myfunc1() {
    echo "----myfunc1()内でmain_alias実行-------------"
    main_alias
    echo "--------------------------------------------"
}

alias main_alias="echo main alias"
myfunc1
$ ./aliastest1.sh
----myfunc1()内でmain_alias実行-------------
./aliastest1.sh: line 6: main_alias: command not found
--------------------------------------------

main_aliasの定義をmyfunc1関数の前に書けば正常に動作します。

aliastest2.sh

#!/usr/bin/bash
shopt -s expand_aliases
alias main_alias="echo main alias"

myfunc1() {
    echo "----myfunc1()内でmain_alias実行-------------"
    main_alias
    echo "--------------------------------------------"
}

myfunc1
$ ./aliastest2.sh
----myfunc1()内でmain_alias実行-------------
main alias
--------------------------------------------

関数の定義読み込み時に展開されるため、関数内でaliasを定義しても
 「その関数は実行されていない = aliasが定義されていない」
ためエラーとなります。

aliastest3.sh

#!/usr/bin/bash
shopt -s expand_aliases
alias main_alias="echo main alias"

myfunc1() {
    echo "----myfunc1()内でmain_alias実行-------------"
    main_alias
    echo "--------------------------------------------"
    myfunc1_alias="echo myfunc1 alias"
    echo "----myfunc1()内でmyfunc1_alias実行----------"
    myfunc1_alias
    echo "--------------------------------------------"
}

myfunc1
$ ./aliastest3.sh
----myfunc1()内でmain_alias実行-------------
main alias
--------------------------------------------
----myfunc1()内でmyfunc1_alias実行----------
./aliastest3.sh: line 11: myfunc1_alias: command not found
--------------------------------------------

また関数内でのaliasの定義は、同じシェル内で実行されていれば実行元の関数にも反映されます。

aliastest4.sh

#!/usr/bin/bash
shopt -s expand_aliases
alias main_alias="echo main alias"

echo "---- main()内で定義されているalias一覧 1--------"
alias
echo "------------------------------------------------"

myfunc1() {
    alias myfunc1_alias="echo myfunc1 alias"
}

myfunc1

echo "---- main()内で定義されているalias一覧 2--------"
alias
echo "------------------------------------------------"
$ ./aliastest4.sh
---- main()内で定義されているalias一覧 1--------
alias main_alias='echo main alias'
------------------------------------------------
---- main()内で定義されているalias一覧 2--------
alias main_alias='echo main alias'
alias myfunc1_alias='echo myfunc1 alias'
------------------------------------------------

スコープはシェル変数と同じのようです。( ) やパイプを使ってサブシェルで実行したり、他のファイルを実行した場合(子プロセスの場合)にも引き継ぐことはできません。
※ パイプを使うと、パイプの左の式もサブシェルで実行されます

aliastest5.sh

#!/usr/bin/bash
shopt -s expand_aliases
alias main_alias="echo main alias"

myfunc1() {
    alias myfunc1_alias="echo myfunc1 alias"
}

myfunc1 | true
echo "---- main()内で定義されているalias一覧 1--------"
alias
echo "------------------------------------------------"

(myfunc1)
echo "---- main()内で定義されているalias一覧 2--------"
alias
echo "------------------------------------------------"

./aliastest5_sub.sh
echo "---- main()内で定義されているalias一覧 3--------"
alias
echo "------------------------------------------------"

aliastest5_sub.sh

#!/usr/bin/bash
shopt -s expand_aliases

alias sub_alias="echo sub alias"
$ ./aliastest5.sh
---- main()内で定義されているalias一覧 1--------
alias main_alias='echo main alias'
------------------------------------------------
---- main()内で定義されているalias一覧 2--------
alias main_alias='echo main alias'
------------------------------------------------
---- main()内で定義されているalias一覧 3--------
alias main_alias='echo main alias'
------------------------------------------------

子プロセスとして./aliastest5_sub.sh で呼び出すのではなく source コマンドを使って実行すれば、サブシェルにならないため alias を引き継ぐことができます。

aliastest6.sh

#!/usr/bin/bash
shopt -s expand_aliases
alias main_alias="echo main alias"

source ./aliastest5_sub.sh
echo "---- main()内で定義されているalias一覧----------"
alias
echo "------------------------------------------------"
$ ./aliastest6.sh
---- main()内で定義されているalias一覧----------
alias main_alias='echo main alias'
alias sub_alias='echo sub alias'
------------------------------------------------

無料のAIコーディングアシストサービス比較

GitHub Copilotを含め、いくつかのAIコーディングアシストサービスが存在します。ただ、GitHub Copilotは個人ユーザでは10ドル/月 (年払いだと110ドル) のコストがかかり、個人の趣味レベルで利用するにはちょっと躊躇してしまいました。

そこで2024年9月現在、無料の似たサービスがないか探してみました。

  • Codeium
    • ◯ コーディングアシストは無料で利用可能
    • ◯ VSCodeと連携
    • プライバシーに考慮されているのが◯
  • Amazon Q Developer
    • ◯ コーディングアシストは無料で利用可能
    • ◯ VSCodeと連携
  • Google Colab AI
    • △ VSCodeと連携
    • △ ブラウザベースのサービスであり、利用時間制約あり

今回は取り上げていませんが、参考までに他にこのようなサービスやツールもあるようです。

  • OpenAI Codex
  • TabNine
    • 有料プランのみ
  • Code Llama
    • Meta社開発したプログラミングコードとコードに関する自然言語を生成できるLLM
    • 自前でセットアップが必要
  • Hugging Face
    • AI・機械学習向けのデータセットを共有できるリポジトリと、クラウド実行環境を提供しているサイトで、コード生成AIもその一つとして使用可能
    • 無料で使用可能

Codeium

Codeium は 2021年設立のアメリカのスタートアップ企業 Cedeium社 が提供するサービスです。

料金

Indivisual(無料), Pro, Teams, Enterprise の4プランがあり、Pro, Teams プランは月払いと年払いがあります。Enterprise は料金非公開で要相談とのこと。こちらも無料の Indivisual で、コードのアシスト自体は無制限で使えるようです。また、「No training on non-permissive data」と、プライベートのコードを使った学習はしない点が良さそうです。

  • Indivisual (無料)
    • Unlimited rapid AI autocomplete suggestions
    • Unlimited in-editor AI chatsUnlimited AI command instructions
    • No training on non-permissive data
    • Basic context awarneess
    • Limited indexing limits
    • Attribution filtering
    • Encryption in transit
    • Optional zero-day retentionDiscord community

Amazon Q Developer (旧Codewhisperer)

Amazon Q DeveloperはAWS社が提供するサービスです。2024/4/30にCodewhisperer から Amazon Q Developer に名前が変更され、AIコーディングアシスト機能はこのAmzon Q Devloperというサービスの一部となっているようです。

料金

無料枠と、Proの2プランがあります。無料枠の内容は以下の通りで、IDEとも連携できるようで、コード提案自体機能には特に制限はなさそうです。

  • 無料枠
    •  IDE と CLI でのコード提案で、より迅速にコードを作成できます
    •  パブリック CLI 補完が無料です
    •  リファレンストラッキングでコードライセンスを確認できます
    •  作業している場所 (IDE、CLI、AWS マネジメントコンソール、Slack など) で使用できます
    •  高度な機能への 1 か月あたりのアクセスが制限されています
      • 統合開発者環境 (IDE) でのチャット、コードのデバッグ、テストの追加など (1 か月あたり 50 回まで) ソフトウェア開発用の Amazon Q Developer エージェントでタスクを迅速化 (1 か月あたり 5 回まで)
      • Amazon Q Developer エージェントを使用してコードを変換することで、わずかな時間でアプリをアップグレードできます (1 か月あたりに送信するコードに 1,000 行の制限あり) セキュリティ脆弱性スキャンを使用して、コードのセキュリティを強化 (上限: 50 件のプロジェクトスキャン/月)
      • AWS アカウントのリソースに関する回答を取得 (上限: 25 件のクエリ/月)
      • コンソールのよくあるエラーの診断 (含まれています) その他

Google Colab AI

Google社 が提供するサービス Colaboratory というサービスのコーディングアシストサービスの一部が Colab AI です。2023/12/19 に無料で使えるようになりました。

ただし、Colaboratory というブラウザから使うサービスが前提の機能のようで、

  • 手元の VSCode から直接使えない ※VSCodeからリモート接続で使うことはできそう
  • Colaboratory 自体に無料板あるものの、長時間連続して使えない

といった制約があるようで、上記2つのサービスと比較すると毛色は違いますが、Colaboratory 上での開発が前提なら選択肢に入りそうです。

jqコマンドでjsonからcsvを作成

以下3つの配列からなるjsonから、csvを作成(変換)してみます。

sample1.json

[
    {
        "name": "name1",
        "ip":
        [
            "192.168.0.1/32",
            "192.168.0.2/32"
        ],
        "dstname": "dst1"
    },
    {
        "name": "name2",
        "ip":
        [
            "192.168.1.1/32",
            "192.168.1.2/32"
        ],
        "dstname": "dst2"
    },
    {
        "name": "name3",
        "dstname": "dst3"
    }
]

まずは、簡単な所から。

各配列の「.name」と「.dstname」の一覧をCSV化

$ cat sample1.json | jq -r '.[] | 
[
.name,
.dstname
] | @csv'

<出力>
"name1","dst1"
"name2","dst2"
"name3","dst3"

.ip の値もcsvに出力してみましょう。.ipは配列なので、複数のIPが記載されている場合はスペース区切りで結合し、IPの記載がない場合は空文字に変換。

$ cat sample1.json | jq -r '.[] |
[
.name,
(if .ip then .ip | join(" ") else "" end),
.dstname
] | @csv'

<出力>
"name1","192.168.0.1/32 192.168.0.2/32","dst1"
"name2","192.168.1.1/32 192.168.1.2/32","dst2"
"name3","","dst3"

次は、jsonの階層が深くなって、最上位の階層にメタ情報が付与されているjsonを考えてみます。

sample2.json

[
    {
        "rulename": "firstcollection",
        "rules":
        [
            {
                "name": "name1",
                "ip":
                [
                    "192.168.0.1/32",
                    "192.168.0.2/32"
                ],
                "dstname": "dst1"
            },
            {
                "name": "name2",
                "ip":
                [
                    "192.168.1.1/32",
                    "192.168.1.2/32"
                ],
                "dstname": "dst2"
            },
            {
                "name": "name3",
                "dstname": "dst3"
            }
        ]
    }
]

ここでは、.rulename というメタ情報もcsvの1カラムに出力することを目標とします。

出力イメージ

"firstcollection","name1","192.168.0.1/32 192.168.0.2/32","dst1"
"firstcollection","name2","192.168.1.1/32 192.168.1.2/32","dst2"
"firstcollection","name3","","dst3"

案1 .rulenameと.rulesを結合

.rulename と .rules 配下の各配列を結合後、csvに変換します。

まずは結合だけをやってみます。rulename: と rule: は結合後のキーの名前なので任意の名前を指定できますが、今回は元のjsonのキー名をほぼそのまま使っています。

$ cat sample2.json | jq -r '.[] | { rulename: .rulename, rule: .rules[] }'
{
"rulename": "firstcollection",
"rule": {
"name": "name1",
"ip": [
"192.168.0.1/32",
"192.168.0.2/32"
],
"dstname": "dst1"
}
}

{
"rulename": "firstcollection",
"rule": {
"name": "name2",
"ip": [
"192.168.1.1/32",
"192.168.1.2/32"
],
"dstname": "dst2"
}
}

{
"rulename": "firstcollection",
"rule": {
"name": "name3",
"dstname": "dst3"
}
}

.rulename と .rules[] の各配列が結合されました。

余談ですが、結合時に欲張って .rules[].name や .rule[].dstname などを指定すると、それぞれの値が組み合わされて(直積)、意味のない集合になってしまうので注意が必要です。今回は3要素しか結合してないのですぐに処理が終わりましたが、要素が増えると爆発的に組み合わせが増えてCPUを食いつぶします。

$ cat sample2.json | jq -r '.[] | { rulename: .rulename, name: .rules[].name, dstname: .rules[].dstname }'
{
"rulename": "firstcollection",
"name": "name1",
"dstname": "dst1"
}
{
"rulename": "firstcollection",
"name": "name1", ★ name1とdst2という異なる配列の値が結合される
"dstname": "dst2" ★
}
{
"rulename": "firstcollection",
"name": "name1",
"dstname": "dst3"
}
{
"rulename": "firstcollection",
"name": "name2",
"dstname": "dst1"
}
{
"rulename": "firstcollection",
"name": "name2",
"dstname": "dst2"
}
{
"rulename": "firstcollection",
"name": "name2",
"dstname": "dst3"
}
{
"rulename": "firstcollection",
"name": "name3",
"dstname": "dst1"
}
{
"rulename": "firstcollection",
"name": "name3",
"dstname": "dst2"
}
{
"rulename": "firstcollection",
"name": "name3",
"dstname": "dst3"
}

話を元に戻します。結合後のjsonをcsvに変換するのは、最初と同じです。

$ cat sample2.json | jq -r '.[] | { rulename: .rulename, rule: .rules[] } |
[
.rulename, .rule.name,
(if .rule.ip then .rule.ip | join(" ") else "" end),
.rule.dstname
] | @csv'

<出力>
"firstcollection","name1","192.168.0.1/32 192.168.0.2/32","dst1"
"firstcollection","name2","192.168.1.1/32 192.168.1.2/32","dst2"
"firstcollection","name3","","dst3"

案2 .rulenameを変数に入れて、配列化時に埋め込む

.rulenameと.rulesを結合するのではなく、.rules内に.rulenameの値を入れ込む方法でも実現できます。以下は .rulename の値を $rulename という変数に入れ、[ ] 内で再利用しています。

$ cat sample2.json | jq -r '.[] | .rulename as $rulename | .rules[] | 
[
$rulename,
.name,
(if .ip then .ip | join(" ") else "" end),
.dstname
] | @csv'

<出力>
"firstcollection","name1","192.168.0.1/32 192.168.0.2/32","dst1"
"firstcollection","name2","192.168.1.1/32 192.168.1.2/32","dst2"
"firstcollection","name3","","dst3"

こちらの方が記述がシンプルですね。

案3 文字列結合を使って、自力でcsv化

.rulename と、.rules[] の各要素を、”+” を使って手動で結合する方法(荒業?)です。@csv を使っていないためcsvの値がダブルクォーテーションで囲まれていないですが、これでも目的は達成できそうです。

$ cat sample2.json | jq -r '.[] |
.rulename + "," +
(
.rules[] |
.name + "," +
(if .ip then .ip | join(" ") else "" end) + "," +
.dstname
)'

<出力>
firstcollection,name1,192.168.0.1/32 192.168.0.2/32,dst1
firstcollection,name2,192.168.1.1/32 192.168.1.2/32,dst2
firstcollection,name3,,dst3

bashでラッパー関数の引数をうまく処理する

シェルスクリプトで何かのコマンドを実行したい場合、そのコマンドの実行前後に他の処理を入れたり、コマンドに特定のオプションをつけたりしたいことが出てくることがあります。他にも、メインの関数の前に他の関数を挟んだりしたいこともあります。この場合、本来実行したいコマンドや関数のラッパー関数を作ることになります。

ラッパー関数を作りたい場合その関数の引数全部を「$@」という特別な変数を参照できますが、この変数は配列です。そのため「$@」を他の変数にそのまま代入したい場合、そのまま参照すると想定外の挙動になるので、以下のように代入しましょう。

ARGS=("$@")

上記で代入するとARGSは配列になるので、各引数も以下のように取り出せます。

ARG1=${ARGS[0]}
ARG1=${ARGS[1]}

ラッパー関数なので、本来実行したい関数やコマンドにそのまま引数で渡したい場合も、配列として全要素を渡しましょう。

myfunc.sh

#!/bin/bash

function main {
    echo "---- main ----"
    local ARGS=("$@")
    echo "arg1=${ARGS[0]}"
    echo "arg2=${ARGS[1]}"
    echo "arg3=${ARGS[2]}"
    echo "arg4=${ARGS[3]}"
}

function myfunc {
    echo "---- myfunc ----"
    local ARGS=("$@")
    echo "arg1=${ARGS[0]}"
    echo "arg2=${ARGS[1]}"
    echo "arg3=${ARGS[2]}"
    echo "arg4=${ARGS[3]}"

    main "${ARGS[@]}"
}

myfunc "a b" c "d" "e f g"
$ ./myfunc.sh
---- myfunc ----
arg1=a b
arg2=c
arg3=d
arg4=e f g
---- main ----
arg1=a b
arg2=c
arg3=d
arg4=e f g
やりがちなミス

ARGS代入時に$@をダブルクォーテーションで囲み忘れると、引数にスペースが入っている場合に想定外の動作をするのでご注意ください。

myfunc.sh

#!/bin/bash
function myfunc {
    local ARGS=($@)
    echo "arg1=${ARGS[0]}"
    echo "arg2=${ARGS[1]}"
    echo "arg3=${ARGS[2]}"
    echo "arg4=${ARGS[3]}"
    echo "arg5=${ARGS[4]}"
    echo "arg6=${ARGS[5]}"
    echo "arg7=${ARGS[6]}"
}

myfunc "a b" c "d" "e f g"
$ ./myfunc.sh
arg1=a
arg2=b
arg3=c
arg4=d
arg5=e
arg6=f
arg7=g

逆にARGSに配列として代入しないと、以下のように引数全体が一つの文字列になってしまいます。

myfunc.sh

#!/bin/bash
function myfunc {
    local ARGS="$@"
    echo "args=${ARGS}"
    echo "arg1=${ARGS[0]}"
    echo "arg2=${ARGS[1]}"
}

myfunc "a b" c "d" "e f g"
$ ./myfunc.sh
args=a b c d e f g
arg1=a b c d e f g
arg2=

Chromecast with Google TVで、ワイヤレスイヤホンを接続

Chromecast with Google TV (Android 12)で、ワイヤレスイヤホン(Bluetooth)を接続ができず困っていたのですが、解決方法がわかりました。


具体的にはペアリングの画面で、「ペアを設定する」をクリックして失敗していたのですが、実はこれがうまくクリックできていなかったのが原因のようです。クリックするには、カーソル下を押して一旦キャンセルに移動後、再度ペアを設定するを選択すると、うまくクリックできペアリングできました。



20240915212341

WSLHostPatcherでWSL2上のサーバに外部からアクセスさせる

WSL2上で何かサーバーを起動すると、自動でWindows側からはlocalhostからアクセスできるようにポートフォワーディングが設定されます。しかし、設定対象がlocalhostなのでWindowsの外部からアクセスできません。

そこで、ネット上はいくつかの方法が紹介されています。

これらの方法は手間がかかったり、デメリットがあったりと一長一短です。

そこで、今回紹介する方法はWSLHostPatcherというツールです。

WSL2のlocalhost宛の通信を自動でポートフォワーディングするデフォルトの機能は、Windows上で動いているwslhost.exeプロセスで実現されています。本ツールはこのプロセスの動作をいじって(フックして)、localhost以外のIP宛の通信も自動でポートフォワーディングします。

使い方はすごく簡単です。https://github.com/CzBiX/WSLHostPatcher/releasesからWSLHostPatcher.zipをダウンロードし、中に入っているWSLHostPatcher.exeを実行するだけです。

実行後、WSL2上で起動したサーバは自動でポートフォワーディング設定され、Windows外部からもアクセスできるようになります。なお初めて使う場合は、Windows側でアラートが出るので許可する必要があります。
※ 実行前から起動しているサーバは自動で設定されません。サーバを再起動してください。

なお、このツールはWSL2を停止すると機能が無効になります。そのため、WSL2を再起動したりする場合は再度実行してあげてください。

また、もちろんですがWindows外からアクセスされるようになるため、セキュリティにはご注意ください。


Firefox107でサイドバー(Bookmark)の表示崩れ

Firefoxをバージョン107にアップデートすると、Bookmarkや履歴などを表示するサイドバーの表示が崩れてしまいました。具体的にはサイドバーを開くと、以下のように表示領域全体に表示されなくなりました。



本事象ですが、どうやら昔からFirefoxをアップデート使っていた場合に発生する可能性があるらしく、昔の設定がFirefoxのプロファイルに残っているのが原因のようです。

設定はFirefoxのプロファイルが保存されているディレクトリにあるxulstore.jsonというファイルに保存されています。Firefoxを一旦閉じてこのファイルを削除し、再度Firefoxを起動すれば無事正常な表示に戻りました。
※ ファイルを削除するとFirefoxの見た目が一旦リセットされてしまいますので、ご注意ください



参考

Cygwinでssh-agent

~/.bash_profile に以下を記述しましょう。ターミナル(bash)起動時に自動起動します。

SSHAGENT=/usr/bin/ssh-agent
SSHAGENTARGS="-s"
if [ -z "$SSH_AUTH_SOCK" -a -x "$SSHAGENT" ]; then
    eval `$SSHAGENT $SSHAGENTARGS`
    trap "kill $SSH_AGENT_PID" 0
fi

Firefoxのコンテナータブの順番を変えよう!

Firefoxにはコンテナーという機能があり、Firefoxの中を論理的に分けることができます。

これにより、タブ毎にCookieの扱いを独立させることができるため、一つのFirefoxで仕事用、プライベート用といった複数のGoogleアカウントにログインしたりすることができます。また、サイトを跨ったユーザのトラッキングを防止する効果もありセキュリティ向上も見込めます。

コンテナータブは以下のように、Firefoxの設定で自由に追加・修正できるのですが、このコンテナ数が増えてくると順番を入れ替えたくなってきました。

Firefox Multi-Account Containers という拡張機能を使えば、この拡張機能内では自由にコンテナの順番が変えられるので重宝していました。

拡張機能の設定画面例

順番の変更はあくまで拡張機能内だけ有効で、上のFirefoxの設定から見えるコンテナータブの順番まで変更されず、タブ追加ボタン「+」を押すとバラバラの順番でコンテナ一覧が表示され目的のコンテナを探すのに一苦労してました。

そこで、コンテナの順番を手動でカスタマイズする方法を紹介します。前提とする環境はWindows 10 + Firefox 101.0.1 です。

本手順は最悪Firefoxの設定を壊す可能がありますので、バックアップを取るなど、自己責任でお願いします。

まずは、Firefoxを必ず終了し、Firefoxのプロファイルのフォルダを開いてください。プロファイルのフォルダは、デフォルトでは、C:\Users\ユーザ名\AppData\Roaming\Mozilla\Firefox\Profiles\ランダム文字列.default といったパスにあると思います。

このフォルダの下にcontainers.jsonというファイルがあります。これが設定ファイルなので、適当にバックアップをとっておいてください。

このファイルを開くと以下のように、JSONフォーマットでコンテナタブの設定が書かれています。

{"version":4,"lastUserContextId":3,"identities":[{"userContextId":1,"public":true,"icon":"chill","color":"blue","telemetryId":1,"name":"サーフィン" ・・以下略・・

このままだと見にくいので、整形すると以下のようになります。赤、青、緑の色をつけた部分が一つのコンテナタブの設定になっており、この順番が表示順となっています。

ちなみに私はjqコマンドで整形しましたが、面倒なら、JSONを整形できるWebサービスを使って整形するのが簡単です。

{
   "version":4,
   "lastUserContextId":3,
   "identities":[
      {
         "userContextId":1,
         "public":true,
         "icon":"chill",
         "color":"blue",
         "telemetryId":1,
         "name":"サーフィン"
      },
      {
         "userContextId":2,
         "public":true,
         "icon":"circle",
         "color":"purple",
         "name":"仕事"
      },
      {
         "userContextId":3,
         "public":true,
         "icon":"circle",
         "color":"purple",
         "name":"Amazon"
      }
   ]
}

ここまで理解すれば後は簡単です。各コンテナのタブの設定をコピペして順番を入れ替えましょう。例えば、仕事とAmazonを入れ替えると以下の通りです。JSONなので配列の途中はカンマ(,)を入れ、最後はカンマ(,)を削除することに注意してください。また、userContetId等の値を変更するのはNGです。

{
   "version":4,
   "lastUserContextId":3,
   "identities":[
      {
         "userContextId":1,
         "public":true,
         "icon":"chill",
         "color":"blue",
         "telemetryId":1,
         "name":"サーフィン"
      },
      {
         "userContextId":3,
         "public":true,
         "icon":"circle",
         "color":"purple",
         "name":"Amazon"
      },
      {
         "userContextId":2,
         "public":true,
         "icon":"circle",
         "color":"purple",
         "name":"仕事"
      } 
   ]
}

後は、この設定を元のcontainer.jsonに上書きすれば設定は完了です。ちなみに上書きする時に元の未整形のJSONに戻す必要はありません。

Firefox便利な設定(about:config)まとめ

Firefoxは、アドレス欄に「about:config」と入力すると、様々な設定を行うことができます。ここでは、私がよく使用する便利な最低限の設定をメモ代わりに残しておきます。Firefoxのバージョンは101.0.1 (Windows版) を使用しました。

JavaScriptによるポップアップウインドウを新しいタブで開く

Firefoxのタブグループの設定で以下にチェックを入れても、



サイトによっては、強制的に新しいウィンドウが開いてしまいます。これをタブで開く設定に変更します。

browser.link.open_newwindow.restriction → 0 に設定する(デフォルト=2)

最後のタブを閉じてもウィンドウを閉じない

デフォルトでは、最後のタブを閉じるとウィンドウが閉じてしまい、またFirefoxを起動する必要があります。ただ、ブラウザって一旦開くと閉じることってなかなかないので、ウィンドウを自動で閉じないようにします。

browser.tabs.closeWindowWithLastTab → false に設定する

検索ボックスでの検索時に結果を新しいタブで開く

あるサイトを見ていて、わからない事が出てくると検索しますが、デフォルトだと今見てるタブが検索結果画面に変わってしまいます。これを新しいタブで検索結果を開くように変更します。

browser.search.openintab → true

SSDの寿命を伸ばす

FirefoxeでYouTubeなどを見てると、ディスクに頻繁な書き込みが発生するそうです。なので、ディスクキャッシュサイズを128KBに変更します。

browser.cache.disk.max_entry_size → 128

その他

これは、about:configの設定ではないのですが、リンクをホイールクリック(ミドルクリック)を使って新しいタブで開いた場合、すぐにそのタブをアクティブにしたい(フォーカスしたい)場合は、タブグループの以下の設定にチェックを入れます。



覚えやすいIPv6アドレス - たった4桁!

インターネットの疎通確認をする際、IPv4なら以下のようなわかりやすいIPに対してpingを実行してみることがあります。

最も覚えやすいCloudflareのDNSキャッシュサーバ1.1.1.1のIPv6版はどうかなと調べてみましたが、

  • 2606:4700:4700::1111 (リンク先に飛ぶにはIPv6環境が必要です。なんとIP直打ちでもサーバ証明書の警告がでません!)

と、難しくはないですがさっと思い出せるIPではありませんでした。GoogleのDNSキャッシュサーバも

  • 2001:4860:4860::8888

と、同様にさっと思い出せる自信がありません。

ということで、もっと覚えやすいシンプルなIPがないか探してみました。ありました!

  • 2600:: (リンク先に飛ぶにはIPv6環境が必要です。IP直打ちのためサーバ証明書の警告がでます。公式のURLはこちらです。)

です。このIPの持ち主はT-Mobileで、httpも受け付けてくれるので、curlで以下のように応答も受け取れます。

$ curl [2600::]
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>nginx/1.16.1</center>
</body>
</html>

ちなみに、上記の通り、httpでIPにアクセスすると他のページにリダイレクトされるので、実際のコンテンツまで取得したい場合は、以下のようにアクセスしましょう。
※ httpsにリダイレクトされるのですが、サーバ証明書がホスト名と一致せずに警告が出るので、-kをつけて警告を無視しています。

$ curl -L -k [2600::]
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
~ 略 ~

WHR-G301NにOpenWrtを導入

家に古いバッファローのルータWHR-G301Nが転がっていたので、OpenWrtを入れてみましたので、その時の手順をここにメモっておきます。

環境

作業に使用した環境は以下の通りです。

  • 作業PC
    • Windows 10
      • Windowsのファイアウォール機能は無効化 ★無効化しておかないとタイミング有無に関係なくファームウェア転送に失敗しました。私はこれでハマったのでご注意ください。
  • L2スイッチ
    • ルーターとWindows PCを直接つなぐと、ルータ電源投入時にリンクアップに時間がかかってしまい、ファームウェア転送のタイミングがシビアになるらしいので、L2スイッチを間に挟みました。
  • OpenWrt
    • バージョン 17.01.2 (こちらからwhr-g301n-squashfs-tftp.binをダウンロード)
    • 2022年5月現在、OpenWrtはもっと新しいバージョンがリリースされていますが、本ルータはフラッシュが4MiBしかなく、インストールできません。また、19.xxのバージョンも入れてみましたが、ルータの設定を変更しても保存されない事象が発生してしまったため、最終的に17.01.2に落ち着きました。

既存ファームウェアのバックアップ

元のファームウェアに戻したい時のことを考えて、ファームウェアをバックアップしました。手順は、DD-WRT OpenWrt 適材適所で両方使いたい人向け @ ウィキ の記述を参考にしました。

ずっと家に放置していたルータで、どんな設定かもわからなかったので、まずはルータを初期化しました。リセット方法はバッファローの公式YouTube動画を参考にしました。底面にリセットスイッチがあるのですが、縦置き用の台を本体に取り付けてると見えないので外しましょう。私は力ずくで引っ張ると外れましたが、場合によっては爪が折れる可能性もあるので、ご注意ください。

後は動画の通り、電源が入ってる状態で底面のリセットボタンをDIAGランプが点滅するまで長押しました。その後DIAGランプは点灯→消灯すると完了です。

次に、ルーターとPCを繋ぎます。PCには、ルータのデフォルトのネットワーク設定によって、192.168.11.XX (私の場合は192.168.11.2)が付与されました。コマンドプロンプトを管理者として実行し、ipconfigを実行した結果の例です。
※ この時点では、管理者として実行する必要はありませんが、後ほどのarpテーブルの操作で管理者権限が必要なので、忘れないようにこの時点で管理者として実行しました。

C:\> ipconfig
~略~
イーサネット アダプター ethernet:

   接続固有の DNS サフィックス . . . . .:
   IPv4 アドレス . . . . . . . . . . . .: 192.168.11.2
   サブネット マスク . . . . . . . . . .: 255.255.255.0
   デフォルト ゲートウェイ . . . . . . .: 192.168.11.1

私の環境では、アダプター名が "ethernet" になっていますが、環境によっては、"イーサネット 1"といった表示になっていると思います。このアダプター名は後で使うので、記憶しておきましょう。

http://192.168.11.1にブラウザからアクセスできることを確認します。IDはroot、パスワードは空欄でログインできます。

では、次にルーターのデバッグモードに入ります。ブラウザから、http://192.168.11.1/cgi-bin/cgi?req=frm&frm=py-db/55debug.html にアクセスします。IDはbufpy、パスワードはotdpopyです。
※ ルーターを初期化していない場合は、192.168.11.1はルーターのIPに読み替えてください。
※ ルーターにパスワードを設定している場合、デバッグモートのパスワードは、otdpopy+管理者パスワードになります。

以下のような画面が表示されるので、telnetdをクリック後、"start"ボタンを押します。

startボタンを押すとブラウザの画面は、通常のルーターの管理画面に戻ります。

Windowsのコマンドプロンプトからtelnetで192.168.11.1に接続します。
※ telnetコマンドがインストールされていない場合は、コントロールパネルのプログラムと機能よりインストールしておきましょう。(詳細割愛)

C:\> telnet 192.168.11.1
BusyBox v1.00 (2013.07.16-01:07+0000) Built-in shell (msh)
Enter 'help' for a list of built-in commands.

#

パーティションを確認します。

# cat /proc/mtd
dev:    size   erasesize  name
mtd0: 0003e000 00001000 "uboot"
mtd1: 00002000 00001000 "uboot_environ"
mtd2: 000e0000 00010000 "linux"
mtd3: 002c0000 00010000 "rootfs"
mtd4: 00010000 00010000 "user_property"
mtd5: 00010000 00010000 "ART"
mtd6: 003a0000 00010000 "firmware"

各パーティションをddコマンドを使ってダンプします。なお、ルータ内はストレージの空きが少ないので、一気にダンプするのではなく、各パーティションをダンプ→PCにダウンロード→ダンプ削除 の手順でバックアップしていきます。

まずは、書き込み可能なディレクトリに移動します。

# cd /tmp

mtd0をmtd0.ddというファイル名でダンプします。

# dd if=/dev/mtdblock/0 of=mtd0.dd
496+0 records in
496+0 records out

ダンプしたファイルをブラウザを使ってダウンロードします。mtd0.dd をダウンロードするには、http://192.168.11.1//html/py-db/tmp/mtd0.dd にアクセスします。アカウントは先程と同じく、IDがbufpy、パスワードはotdpopyです。

ダウンロードが終わりましたら、mtd0.dd を削除します。

# rm mtd0.dd

mtd1~6までを同様の手順でバックアップします。(手順割愛)

次に、ルーターのLAN側インターフェスのMACアドレスを確認します。以下はAA:AA:AA:AA:AA:AAと表記していますが、もちろんルーターによって値は変わりますので適宜読み替えてください。
※ ルータ側面に貼られているシールのSSIDの値がこのMACアドレスになっているようなので、そこから判断しても構いません。

# ipconfig br0
br0       Link encap:Ethernet  HWaddr AA:AA:AA:AA:AA:AA
          inet addr:192.168.11.1  Bcast:192.168.11.255  Mask:255.255.255.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:15843 errors:0 dropped:0 overruns:0 frame:0
          TX packets:19273 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:2127383 (2.0 MiB)  TX bytes:10746171 (10.2 MiB)

tftpでファームウェアインストールできるように、accept_open_rt_fmtを1に設定します。

# ubootenv set accept_open_rt_fmt 1

telnetでの作業は終わりです。exitで抜けましょう。

# exit

ホストとの接続が切断されました。

OpenWrtのインストール

いざOpenWrtのインストールです。作業はC:\openwrtフォルダで実施していきますので、なければ作っておいてください。

このフォルダに、ファームウェア lede-17.3vlnjs2H.01.2-ar71xx-generic-whr-g301n-squashfs-tftp.bin をダウンロードしておきます。
※ Wifiでインターネットに繋いでて、LANケーブルでルーターに接続している場合、有線側の優先度が高くなるため、インターネットアクセスがエラーになります。ファームウェアをダウンロードしていない場合は、一旦LANケーブルを抜いてみてください。

管理者として実行されているコマンドプロンプトで、C:\openwrt に移動します。

C:\> cd C:\openwrt
C:\openwrt> dir
 ドライブ C のボリューム ラベルは Windows です
 ボリューム シリアル番号は XXXX-XXXX です

 C:\openwrt のディレクトリ

2022/05/07  21:33    <DIR>          .
2022/05/07  21:33    <DIR>          ..
2022/05/07  21:33         3,473,444 lede-17.01.2-ar71xx-generic-whr-g301n-squashfs-tftp.bin
               1 個のファイル           3,473,444 バイト
               2 個のディレクトリ  27,196,813,312 バイトの空き領域

ルーターのMACアドレス(AA:AA:AA:AA:AA:AA)を、192.168.11.1でWindowsのARPテーブルに登録します。ここで、MACアドレスの表記は、":"ではなく、"-" で区切る必要があるので注意。
※ MACアドレスとアダプタ名(ethernet)は適宜読み替えてください。

C:\openwrt>netsh interface ipv4 set neighbors "ethernet" 192.168.11.1 AA-AA-AA-AA-AA-AA

ARPテーブルに登録されたことを確認します。

C:\openwrt>arp -a

インターフェイス: 192.168.11.2 --- 0xa
  インターネット アドレス 物理アドレス           種類
  192.168.11.1          aa-aa-aa-aa-aa-aa     静的   ★ここが静的になっていること
~略~

コントロールパネルからネットワークアダプタの設定を開き、PCのIPアドレスを192.168.11.2/255.255.255.0に固定します。

Windowsセキュリティを開いて、使用しているネットワークのファイアウォールを無効化します。

これで準備は完了です。コマンドプロンプトに以下のコマンドを打ち込んでおきます。(実行はまだ)
※ tftp コマンドがインストールされていない場合は、telnetコマンド同様にコントロールパネルのプログラムと機能よりインストールしておきましょう。(詳細割愛)

C:\openwrt>tftp -i 192.168.11.1 PUT C:\openwrt\lede-17.01.2-ar71xx-generic-whr-g301n-squashfs-tftp.bin

ルータの電源を一旦抜き、再度挿した後、5~8秒後ぐらいに上記コマンドを実行します。
※ 電源投入後、L2スイッチのランプがリンクアップした後、一瞬消えます。私はそのタイミングで実行すると成功しました。

以下の通り「転送を正常に完了しました」が表示されれば成功です。DIAGの赤いLEDが点滅後、緑の点滅に変わり、点灯に変われば書き込み完了です。(DIAGは消灯しませんでした)

C:\openwrt>tftp -i 192.168.11.1 PUT C:\openwrt\lede-17.01.2-ar71xx-generic-whr-g301n-squashfs-tftp.bin
転送を正常に完了しました: 2 秒間に 3473444 バイト、1736722 バイト/秒

もし転送が正常に完了したのに、バッファローの管理画面が表示されてしまう場合は、前述のaccept_open_rt_fmtを1に設定し忘れていないか確認しましょう。

後始末とOpenWrtへのログイン

ARPテーブルに登録したルーターのMACアドレスは削除します。

C:\openwrt>netsh interface ipv4 delete neighbors "ethernet" 192.168.11.1
OK

ファイアウォールも必要に応じて、有効に戻しておきましょう。さらに、PCに設定したIP (192.168.11.2) も自動に戻しておきます。

これで、OpenWrtのデフォルトのネットワークセグメントのIPがDHCPで自動でPCに割り振られるはずです。私の環境では、192.168.1.233 が設定されました。

ブラウザでhttp://192.168.1.1にアクセスして、OpenWrtの画面が表示されることを確認して作業完了です。
※ デフォルトではrootにパスワードは設定されていません。

ログイン後の画面例も載せておきます。

PPPoEのPADIパケットを送信

LinuxでPPPoEがうまく繋がらなかったので、PPPoEの最初のパケット(PADI)をコマンドで投げてみた。

# pppoe -I eth0 -A
Got a Relay-ID: 01 00 00 00 xx xx xx xx xx xx
Access-Concentrator: e14xxxxxx-xxxxxxxxxx
Got a cookie: c9 ef 55 8a xx xx xx xx xx xx xx xx xx xx xx xx
AC-Ethernet-Address: 2c:xx:xx:xx:xx:xx
--------------------------------------------------

Access-ConnectratorがPPPoEアクセス先(アクセス集線装置)の名、AC-Ethernet-Addressが応答を返したMACアドレスかな?本環境は、間にルータ(PPPoEパススルー有効)がいるので、これはルータのMACアドレスでした。

ちなみに、ルータのPPPoEパススルーを無効にすると、PADOパケットがリレーされなくなり、コマンドは応答しなくなります。

# pppoe -I eth0 -A
pppoe: Timeout waiting for PADO packets

Android 12アップデート後、Google系サービスが使えない

Pixel 3aをAndroid 12にアップデートすると、Google系サービスが使えなくなり、Chromeがクラッシュしたり、Google Playで"認証してください"といったエラーが出たりするようになりました。もちろんGmailも見られないし、Googleアカウントの追加も削除もできません。

こちらのHELPでも大量の報告があがっており、2022/1/9現在 Google Playを無効化するとよいとの情報もありますが、当方の環境では無効化できず、大変困ってる状況です。

(2022/1/11 追記)

その後、以下の方法で改善しましたので、手順をメモっておきます。

Google Play開発者サービスの強制停止

設定から、アプリ→xxx個のアプリをすべて表示→Google Play開発者サービス を選択肢、「強制停止」を押します。

強制停止がグレイアウトされていて押せない場合、設定から、セキュリティ→デバイス管理アプリ→デバイスを探す が有効になっている可能性があります。これをOFFにすることで、私の環境ではGoogle Play開発者サービスの強制停止ができるようになりました。

Google Play開発者サービスのアップデートの無効化

強制停止に成功したら、次は「無効にする」を押します。

Google Play開発者サービスのアップデートのアンイストール

Google Play開発者サービスの設定画面の右上の「︙」から「アップデートのアンイストール」押します。

Google Play開発者サービスのキャッシュやデータの削除

「ストレージとキャッシュ」より、「キャッシュを削除」を押します。

さらに、容量を管理を押して、一番下の「データをすべて消去」を押します。

Google Play開発者サービスの有効化

最後Google Play開発者サービスを有効にします。

以上で、無事にGoogle系サービスが復活しました。この時、大量に未受信のメールや各種通知が届くのでご注意ください。

最後、必要に応じて無効化した「デバイスを探す」を有効にしておきましょう。

Backing Fileを持つqcow2イメージを単体のqcow2ファイルにする

Backing Fileを持つqcow2ファイル(差分)を、Backing Fileとマージして一つのファイルにする方法についてのメモ。

Backing FileをA.qcow2, 差分ファイルをB.qcow2とします。単純に差分BをAにマージしたいだけなら

# qemu-img commit B.qcow2

でよいですが、これだとBacking File(ベースとなるイメージ)にBの内容がマージされるため、A自体が書き換わってしまいます。

そのため、BのBacking FileをAからCに変更後、commitします。

まずは、AをCとしてコピーします。

# cp A.qcow2 C.qcow2

BのBacking FileをAからCに変更します。

# qemu-img rebase -b C.qcow2 B.qcow2

変更後、commit します。

# qemu-img commit B.qcow2

これで、C.qcow2 に B.qcow2の内容がマージされますので C.qcow2 を単体のqcow2ファイルとして利用できます。

Thunderbird(IMAP)のサブフォルダの新着を確認する

ThunderbirdでIMAPを使用していると、受信トレイに入っている新着メールは確認できるのですが、GmailのようなIMAPサーバ側の設定で自動でサブフォルダにメールを振り分けていると、Thnuderbird側で新着メールは確認できません。これはフォルダを右ウリックしプロパティを開いて「このフォルダーに新着メッセージがないか確認する」にチェックすると自動で新着を確認してくれるのですが、フォルダが多いと面倒です。

デフォルトで全フォルダをチェックしたい場合、Thunderbirdのデフォルト設定を変更すると実現できます。具体的には「ツール」→「オプション」の「一般」の「設定エディタ」を開き、「mail.server.default.check_all_folders_for_new」で検索し、値を「true」にしましょう。
※ 私は現在バージョン78を使ってますが、古いバージョンでは、「mail.check_all_imap_folders_for_new」という設定値のようです。

これで、フォルダ振り分けしたメールの新着も確認できるようになりました。

このブログについて
プライバシーポリシー・お問い合わせ等
購読する(RSS)
記事検索
アーカイブ
カテゴリー
  • ライブドアブログ