シェルの初期化
env
を経由することにより環境変数などをリセットできる。
#!/usr/bin/env - SHELL=/bin/sh LC_ALL=C TERM=xterm COMMAND_MODE=unix2003 /bin/sh
ただし以下のように実行された場合はリセットされない。
$ sh script.sh
最低限?必要な環境変数の取得方法。システム環境変数は getconf
、ユーザー環境変数は launchctl getenv
かな。もし絶対パスでファイル指定が可能ならシェバングに ENV=/path/to/file
や BASH_ENV=/path/to/file
で source
コマンドのように環境変数などを読み込ませることができる。
name | command |
---|---|
PATH | /usr/sbin/sysctl -n user.cs_path(システム) getconf PATH(システム) launchctl getenv PATH /usr/libexec/path_helper |
TMPDIR | getconf DARWIN_USER_TEMP_DIR launchctl getenv TMPDIR |
HOME | launchctl getenv HOME |
DISPLAY | launchctl getenv DISPLAY |
PATH を改行区切りで
コロン使うと長くなるので改行区切りでなんとか。最後に %?
で1文字削る。
unset PATH; while read; do PATH+=$REPLY:; done <<@EOS; export PATH=${PATH%?} /usr/local/bin /usr/local/sbin /usr/bin /bin /usr/sbin /sbin @EOS
tr
とか。
$ PATH=$(tr '\n' ':' <<@EOS /usr/local/bin /usr/local/sbin /usr/bin /bin /usr/sbin /sbin @EOS); export PATH=${PATH%?}
$ echo $PATH /usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin
配列を特定の文字で区切って出力
配列変数がダブルクオートで囲まれていて、なおかつ *
で呼び出すと IFS
の最初の文字がデリミタになる。
$ (IFS=' '; echo "${BASH_VERSINFO[*]}") 3 2 48 1 release x86_64-apple-darwin10.0
zsh っぽく path
を配列にして PATH
に変換してみたり。
$ path=( /usr/bin /bin /usr/sbin /sbin ) $ PATH=$(IFS=:; echo "${path[*]}") $ echo $PATH /usr/bin:/bin:/usr/sbin:/sbin
サブシェルで local っぽく
サブシェル内で引数や変数が変更されても親には影響しないので local
のように使える。set -e
してる場合、この部分は失敗しても exit しないので || false
とか書いておく。
$ set -- /usr/local $ (set -- $1/foo/bar && mkdir -p $1 && cp a b c $1) || false $ echo $@ /usr/local
IFS を一時的に変更してみたり。これは tr
でできるけど。
$ echo "a,b,c" > foo.csv $ (IFS=,; for x in $(cat foo.csv); do echo $x; done) a b c
関数の色々な書き方
シェルスクリプトの関数に設定するコマンド部分は "複合コマンド" なので {}
の他に ()
を使うことができる。
$ foo ()(cd /tmp && pwd) $ pwd /usr/local $ foo /tmp $ pwd /usr/local
for
などは 予約語 ~ done
までが複合コマンドなので括弧が無くてもいい(読みづらいけど)。
$ foo () for x in "$@"; do echo $x; done $ foo a b c a b c $ foo () for x in "$@" > do > echo $x > done $ foo a b c a b c
関数名に「@」
$ @foo (){ echo "$@"; } $ @foo hoge hoge
ただし sh(POSIX モード)では使えない。
local の使い道
一度 local
するとその変数に対する処理は関数内でのみ適用される。
local name
だけの場合は値が空になる。- 元の値にする場合は
local
するときに引き継ぐようにする。 - 関数内でのみ環境変数属性を解除したり変数を削除することができる。
# 値の初期化 fn_a (){ local CFLAGS declare -p CFLAGS } # 値の連結 fn_b (){ local CFLAGS="$CFLAGS -mtune=native" declare -p CFLAGS } # 環境変数属性の解除 fn_c (){ local CFLAGS declare +x CFLAGS declare -p CFLAGS } # 変数の削除 fn_d (){ local CFLAGS unset CFLAGS declare -p CFLAGS } export CFLAGS="-march=native" printf "current: " declare -p CFLAGS printf "fn_a: " fn_a printf "fn_b: " fn_b printf "fn_c: " fn_c printf "fn_d: " fn_d printf "current: " declare -p CFLAGS
current: declare -x CFLAGS="-march=native" fn_a: declare -x CFLAGS="" fn_b: declare -x CFLAGS="-march=native -mtune=native" fn_c: declare -- CFLAGS="" fn_d: functest.sh: line 25: declare: CFLAGS: not found current: declare -x CFLAGS="-march=native"
環境変数の名前だけを取り出す
sh、bash、zsh で export -p
や declare -x
、typeset -x
の出力が異なるのでこれといった方法が思いつかなかった。sed
で整形。
$ declare -x | sed 's/^declare -x //;s/=.*$//'
環境変数を全て削除。PATH
も消えるので注意。
$ unset $(declare -x | sed 's/^declare -x //;s/=.*$//')
シェルを指定して取得した方が書式が固定されるので置換が楽かも。
$ sh -c 'export -p'| sed 's/export \([a-zA-Z0-9_]*\)=.*$/\1/'
Python では os.environ.items()
で環境変数一覧が表示でき、変数名と値がタプルになっているので変数名だけの取り出しが簡単にできる(__CF_USER_TEXT_ENCODING
が余計に入る?)。結果を sort したければ sorted(os.environ.items())
とする。
$ python <<\_EOS import os for x in os.environ.items(): print x[0] _EOS
Python にあまり詳しくないのでワンライナーならどうするんだろうとちょっと調べて書いてみた。改行とインデント使って書いた方が早いかな…。
$ python -c 'import os, sys; [sys.stdout.write(str(x[0]) + "\n") for x in os.environ.items()]'
while 内で shift する箇所が多いなら先に shift してしまう
while
内で shift
することが多い場合。(以下の例は適当)
while [ "$1" ] do case $1 in 1) command; shift; continue;; 2) command;; 3) command; shift; continue;; *) command;; esac command shift done
第1引数にダミーを突っ込んで条件式の時点で shift
する。
set -- - "$@" while shift; [ "$1" ] do case $1 in 1) command; continue;; 2) command;; 3) command; continue;; *) esac command done
for
を使って最後に shift $#
とかでもいいかも。
for x in "$@" do command done unset x shift $#
「*」と「?」
クオートされていない *
と ?
はパス名展開に使われる。これを echo
すると \ls
っぽい結果が得られる。(ただのスペース区切りだけど)
$ touch a b c def $ echo * a b c def $ echo .* . .. $ echo .* . . .. a b c def $ echo ? a b c $ echo $PWD/? /tmp/a /tmp/b /tmp/c
配列を2個ずつ3個ずつ…処理する
while
の場合は簡単。ただし shift
するときに残りの引数が足りないと shift
されないので条件式側で止めるようにしておくか、shift 3 || break
といった感じで対応しておく。
set -- {0..9} alen=3 while [ $# -ge $alen ] do echo $1 $2 $3 shift $alen done
for
の場合別の配列を作成して一定数になったら処理してリセット。それ以外なら continue
してチャージしていく感じ。while
のように無限ループにはならないけど要素数不足で処理されないものが出てくるかもしれない。
for x in {0..9} do array=(${array[@]} $x) if [ ${#array[@]} -ge 3 ] then echo ${array[@]} array=() else continue fi done
ビルド関連
ビルド時に使用するツール等は ac_cv_prog_NAME
などで指定できることがある。
gettext
PATH
上に無い gettext を使用する場合は MSGFMT
や ac_cv_prog_MSGFMT
などで指定できる(configure による)。
pkg-config
PATH
上に無い pkg-config を使用する場合は PKG_CONFIG
や ac_cv_prog_PKG_CONFIG
で指定できる(configure による)。
PKG_CONFIG_LIBDIR の取得
pkg-config --variable pc_path pkg-config
AppleScript
「Appleevent がタイムアウトしました」の対応
tell application "Finder" with timeout of 600 seconds -- default: 120s (* statement *) end timeout end tell
ignoring application responses 〜 end ignoring
もある。
open コマンドを AppleScript で
open location "file:///path/to/file"