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 上での開発が前提なら選択肢に入りそうです。

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=

シェルスクリプト内のコマンドのエラー出力をファイルに一括リダイレクト

通常、シェルスクリプトのエラー出力をファイルに保存したい場合、以下のように、「コマンド 2>ファイル名」 でファイルに追記でリダイレクトすることがあります。

ファイル名 : myscript.sh

#!/bin/bash

ls /hoge 2>> err.log
cat /hoge 2>> err.log

上記スクリプトを実行し、err.logを確認します。

$ ./myscript.sh
$ cat err.log
ls: '/hoge' にアクセスできません: No such file or directory
cat: /hoge: No such file or directory

ただし、シェルスクリプトが長くなってくると、全てのコマンドにリダイレクトを記述するのは面倒です。そういった場合は、exec を使って一括でリダイレクトすることが可能です。

#!/bin/bash

exec 2> err2.log
ls /hoge
cat /hoge

$ ./myscript.sh
$ cat err2.log
ls: '/hoge' にアクセスできません: No such file or directory
cat: /hoge: No such file or directory

ブラウザ上でのWYSIWYG HTMLエディター

このサイトで使用しているWordPressでも記事作成するときに使用しているようなWYSIWYGのHTMLエディターを自分のWebアプリに組み込もうとしてみた。

まずどのようなHTMLがあるか調べてみたところ、以下の物を発見。

一番上のfckeditorをまずは使ってみたので、メモ。 ※事前にApacheやIISといったWebサーバを用意しておいてください

fckeditorのzipファイルをダウンロードし、zipファイル を展開。展開してできたディレクトリを"fckeditor"というディレクトリ名でWebサーバに配置。 次にエディタを表示させるhtmlファイル(index.html)を以下のように記述。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script type="text/javascript" src="fckeditor/fckeditor.js"></script>
<script type="text/javascript">
      window.onload = function()
      {
        var oFCKeditor = new FCKeditor( 'mytext' ) ;
        oFCKeditor.BasePath = "fckeditor/" ;
        oFCKeditor.ReplaceTextarea() ;
      }
</script>
<title>fckeditor</title>
</head>
<body>
fckeditor
<br>
<textarea id="mytext" rows="50" cols="40"></textarea>
</body>
</html>

ここで、

var oFCKeditor = new FCKeditor( 'mytext' ) ;

の'mytext' と

<textarea id="mytext" rows="50" cols="40"></textarea>

の"mytext"を合わせることで、テキストエリアをHTMLエディタに切り替えることができます。

fckeditorとhtmlファイルの配置は以下の通り。

/ - index.html
  - fckeditor/ - fckconfig.js
               - fckeditor.js
               -  ・・・・・ いろいろなファイルやディレクトリ

あとはWebブラウザでこのhtmlファイルにアクセスすればOK。エディタとしての機能は充実していて、使いやすそうだがちょっと動作が重たい気がします。

VARDIA RD-S601

このページへのアクセスが多いので、その後のレポートにリンクを張っておきます

東芝のHDD/DVDレコーダ RD-S601を購入しました。もちろんこの機種を選んだ理由はパソコンの連携ができるからです。

まずはリモートへの動画転送。最初はネットdeダビング機能を使って転送しようとしたが、DLNAに対応しているとのことでRDLNAというソフトを使って簡単に転送できました。

次にメールでの予約。Docomoのこのiアプリを使うことでこれも素直に実現。いくぶん便利にはなるのですが、チャンネル名がわからなかったり、その他オプションのデフォルト値を設定できなかったりとあまり満足のいくものではありません。いずれWebベースのツールを作ろうかな。

RDのWebサーバに アクセスすれば、予約やら録画の確認などいろいろできますが、このポートをインターネットにそのまま公開するのは恐いので、インターネット経由でのアクセス用にはapacheのproxy機能を使用してhttpsで実現。これでパスワードを平文で流さなくてすみます。プロキシ部分の設定はこんな感じ。

UseCanonicalName on
ProxyRequests Off
<Proxy *>
   Order deny,allow
   Allow from all
</Proxy>
ProxyPass / http://192.168.1.4:80/
ProxyPassReverse / http://192.168.1.4:80/

最初は以下のように階層を掘って(hogeの部分 VARDIAに転送していましたが、

ProxyPass /hoge/ http://192.168.1.4:80/
ProxyPassReverse /hoge/ http://192.168.1.4:80/

VARDIAが出力するHTMLに絶対リンクがあるため、うまく動きませんでした。よって、新しくダイナミックDNSでDNS名を取得し、名前ベースのVIrtualHostを使ってサーバ名直下で運用することでうまくいきました。ただし、ネットdeモニターのようにJAVAでサーバに直接接続しにいく機能は使えないようです。

これでインターネットからいつでもVARDIAにアクセスできるようになったのですが、VARDIAの電源がOFFの場合はWebサーバにアクセスできません。よってリモートからVARDIAの電源をWOLでONにするスクリプトを記述して自宅サーバに設置。内容は以下のようにいたって簡単なものです。

 <?php
        echo "RD WOL<BR>\n";
        $a = exec("/usr/bin/wakeonlan 00:xx:xx:xx:xx:xx");
        echo $a;
        echo "<BR>\n";
?>

今回はPHPで記述しました。これをApacheに設置して、携帯などからURLを叩けば電源を入れることができます。

ちなみにwakeonlanというコマンド(Debianではapt-getでインストールできた)でWOLのパケットが投げられ、電源をONにします。 00:xx:xx:… はVARDIAのMACアドレスです。

さて、これで電源ONもリモート操作できるようになったのですが、VARDIAの起動は時間がかかるし、内部処理中(□が表示窓に表示されている)の場合はなぜかONにできないため、遠隔にいると状態がわかりません。なので、以下のスクリプトを書いて設置しました。

<?php
        echo "Ping<BR>\n";
        $a = exec(" ping -c 1 192.168.1.4 | grep icmp_seq");
        echo $a;
        echo "<BR>\n";
        echo "TCP Port 80 Scan<BR>\n";
        $a = exec("/usr/bin/nmap -sT 192.168.1.4 -p 80 | /bin/egrep open");
        echo $a;
        echo "<BR>\ndone";
?>

これで、pingでネットワークを確認、nmapでポート80が空いているかが確認できます。

ひとまずはこれで設定終

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