- 追加された行はこの色です。
- 削除された行はこの色です。
#author("2019-03-04T15:54:13+00:00","default:haikikyou","haikikyou")
#author("2019-03-05T15:17:25+00:00","default:haikikyou","haikikyou")
Bashマニュアルから(自分が)頻繁に使用するであろう機能をピックアップしてまとめる。
#contents
* コマンド [#j1138789]
** ループ [#da0f596d]
*** until [#r0605004]
until test-commands; do consequent-commands; done
条件が成り立つまで繰り返す。
#geshi(bash){{{
# --- commands ---
rm "job.done"
{ sleep 3 && touch job.done; } &
retry=0
until [ -f "job.done" ]
do
sleep 1
((retry+=1))
if [ $retry -gt 5 ]; then
echo "exceeded retry max:5."
exit 1
fi
done
echo "success"
# --- output ---
success
}}}
*** while [#ec71bf2b]
while test-commands; do consequent-commands; done
条件が成り立つ間繰り返す。
#geshi(bash){{{
# --- commands ---
touch "job.lock"
{ sleep 3 && rm job.lock; } &
retry=0
while [ -f "job.lock" ]
do
sleep 1
((retry+=1))
if [ $retry -gt 5 ]; then
echo "exceeded retry max:5."
exit 1
fi
done
echo "job done"
# --- output ---
job done
}}}
*** for [#k816bb81]
for name [ [in [words …] ] ; ] do commands; done
for (( expr1 ; expr2 ; expr3 )) ; do commands ; done
wordsの数繰り返す。&code(){(( ))}; は、他のプログラミング言語同様の構文。~
expr1が最初に評価され、expr2がゼロになるまで、commandsを繰り返し評価する。expr2がゼロでない場合は、expr3が評価される。
#geshi(bash){{{
# --- commands ---
for w in {localhost,192.168.3.4}; do
echo "$w"
done
# --- output ---
localhost
192.168.3.4
# --- commands ---
for ((i=0; i < 5;i++)); do
echo $i
done
# --- output ---
0
1
2
3
4
}}}
&label(warn){参考}; https://www.gnu.org/software/bash/manual/bash.html#Looping-Constructs
** 条件 [#hd3bad84]
*** if [#l6343e8e]
#geshi(bash){{{
if test-commands; then
consequent-commands;
[elif more-test-commands; then
more-consequents;]
[else alternate-consequents;]
fi
}}}
ifの条件式には、コマンドやBashの条件式(-aや-fなど)、算術式などを指定することができる。
#geshi(bash){{{
# --- commands ---
# Arithmetic expression
(( 0 )); echo $?
(( 1 )); echo $?
#1
ls job.done
if ( cd . && test -f job.done ); then
echo "job.done found"
fi
#2
i=0
(( i >= 0 )) ; echo $?
if (( i >= 0 )); then
echo "i is more than 0"
fi
#3
i=0
(( (i >= 0) && (i < 1) )) ; echo $?
!(( (i >= 0) && (i < 1) )) ; echo $?
if !(( (i >= 0) && (i < 1) )); then
echo "i is not (i >= 0) && (i < 1)"
else
echo "i is (i >= 0) && (i < 1)"
fi
#4
log="FATAL: unexpected error"
if [[ "$log" =~ ^FATAL ]]; then
echo "Error detected!"
fi
# --- output ---
# Arithmetic expression
1
0
#1
job.done
job.done found
#2
0
i is more than 0
#3
0
1
i is (i >= 0) && (i < 1)
#4
Error detected!
}}}
*** case [#l507c23a]
#geshi(bash){{{
case word in
[ [(] pattern [| pattern]…) command-list ;;]…
esac
}}}
- switch case文のようなコマンド。
- ;; で条件区切りとなる。;&、;;& という指定もある。;& は、フォールスルーする。;;& は、次にマッチする条件があるまで辿り、マッチすればcommand-listを実行する。
- patternは、パターンマッチングのルールによって処理される。
- | で区切れば、ORでpatternを複数書くことができる。
- どのpatternにもマッチしない場合は、終了ステータス0となり、マッチすれば、command-listの終了ステータスとなる。
#geshi(bash){{{
# --- commands ---
do_case() {
echo "cond: $cond"
case "$cond" in
start)
echo "match start"
;;
s*p)
echo "match s*p"
;;
{a,b,c})
echo "match {a,b,c}"
;;
[0-5]*|[6-9]+)
echo "match [0-5]*|[6-9]+"
;;
(~)
echo "match ~"
;&
(fallback)
echo "match fallback"
;;
($USER)
echo "match \$USER"
;;&
(/$USER)
echo "match /\$USER"
;;
($USER*)
echo "match \$USER*"
;;
*) # default
echo "cond does not match anything"
;;
esac
}
do_case2() {
case $cond in
not) test "1" = "0" ;;
esac
echo "status: $?"
}
#1
cond="start" do_case
#2
cond="stop" do_case
#3
cond="a b c" do_case
cond={a,b,c} do_case
#4
cond="123" do_case
cond="678" do_case
#5
cond="unknown" do_case
#6
cond="$HOME" do_case
#7
cond="$USER" do_case
#8
cond="not" do_case2
cond="notmatch" do_case2
# --- output ---
#1
cond: start
match start
#2
cond: stop
match s*p
#3
cond: a b c
cond does not match anything
cond: {a,b,c}
match {a,b,c}
#4
cond: 123
match [0-5]*|[6-9]+
cond: 678
cond does not match anything
#5
cond: unknown
cond does not match anything
#6
cond: /home/guest
match ~
match fallback
#7
cond: guest
match $USER
match $USER*
#8
status: 1
status: 0
}}}
*** select [#x4e2affc]
- メニューを生成するコマンド。複数のリストから選択させたい場合に便利である。
-
#geshi(bash){{{
select name [in words …]; do commands; done
}}}
選択肢を選んだ場合、itemに値が入る。REPLYには、readした値が入る。
#geshi(bash){{{
select item in dog apple orange;
do
echo you picked $item \($REPLY\)
break;
done
# ----- output -----
1) dog
2) apple
3) orange
#? 1
you picked dog (1)
}}}
選択肢にない番号を選んだ場合は、nullがセットされる
#geshi(bash){{{
select item in dog apple orange;
do
echo you picked $item \($REPLY\)
break
done
# ----- output -----
1) dog
2) apple
3) orange
#? 4
you picked (4)
}}}
何も入力しなかった場合は、再び選択を求められる
#geshi(bash){{{
select item in dog apple orange;
do
echo you picked $item \($REPLY\)
break
done
# ----- output -----
1) dog
2) apple
3) orange
#?
1) dog
2) apple
3) orange
#?
}}}
変数PS3に値を設定すると、選択時のメッセージをカスタマイズすることができる。
#geshi(bash){{{
PS3="Please select a package you will install: "
menus=(package1 package2 package3)
select item in ${menus[@]};
do
test -z "$item" && {
echo "invalid package, please select again"
continue
}
echo "ok, install $item ($REPLY)"
break
done
# ----- output -----
1) package1
2) package2
3) package3
Please select a package you will install: hoge
invalid package, please select again
Please select a package you will install: 1
ok, install package1 (1)
}}}
*** ((expression)) [#v3a6495d]
(( expression ))
let "expression"
expressionの結果がゼロでない場合、0が返る。そうでない場合は、1。&code(){true};、&code(){false}; を指定しても結果は非ゼロである。
以下一通りの算術演算の結果である。
#geshi(bash){{{
i=0; (( ++i )) ; echo $? #=> 0
i=1; (( --i )) ; echo $? #=> 1
echo $(( 0 )) #=> 0
(( 0 )) ; echo $? #=> 1
echo $(( 1 )) #=> 1
(( 1 )) ; echo $? #=> 0
echo $(( 1 - 1 )) #=> 0
(( 1 - 1 )) ; echo $? #=> 1
echo $(( 0 + 1 )) #=> 1
(( 0 + 1 )) ; echo $? #=> 0
echo $(( 1**2 )) #=> 1
(( 1**2 )) ; echo $? #=> 0
echo $(( 1 * 0 )) #=> 0
(( 1 * 0 )) ; echo $? #=> 1
echo $(( 0 / 1 )) #=> 0
(( 0 / 1 )) ; echo $? #=> 1
echo $(( 3 % 3 )) #=> 0
(( 3 % 3 )) ; echo $? #=> 1
echo $(( 3 % 1 )) #=> 0
(( 3 % 1 )) ; echo $? #=> 1
echo $(( 2 >> 1 )) #=> 1
(( 2 >> 1 )) ; echo $? #=> 0
echo $(( 1 << 1 )) #=> 2
(( 1 << 1 )) ; echo $? #=> 0
echo $(( 1 >= 0 )) #=> 1
(( 1 >= 0 )) ; echo $? #=> 0
echo $(( 0 <= 0 )) #=> 1
(( 0 <= 0 )) ; echo $? #=> 0
echo $(( 1 > 0 )) #=> 1
(( 1 > 0 )) ; echo $? #=> 0
echo $(( 0 < 1 )) #=> 1
(( 0 < 1 )) ; echo $? #=> 0
echo $(( 1 == 1 )) #=> 1
(( 1 == 1 )) ; echo $? #=> 0
echo $(( 1 != 0 )) #=> 1
(( 1 != 0 )) ; echo $? #=> 0
echo $(( 1 & 3 )) #=> 1
(( 1 & 3 )) ; echo $? #=> 0
echo $(( 1 & 0 )) #=> 0
(( 1 & 0 )) ; echo $? #=> 1
echo $(( 1 ^ 0 )) #=> 1
(( 1 ^ 0 )) ; echo $? #=> 0
echo $(( 1 ^ 1 )) #=> 0
(( 1 ^ 1 )) ; echo $? #=> 1
echo $(( 0 | 1 )) #=> 1
(( 0 | 1 )) ; echo $? #=> 0
echo $(( 1 | 1 )) #=> 1
(( 1 | 1 )) ; echo $? #=> 0
echo $(( 1 >= 0 && 1 <= 2 )) #=> 1
(( 1 >= 0 && 1 <= 2 )) ; echo $? #=> 0
echo $(( 0 >= 1 || 1 <= 2 )) #=> 1
(( 0 >= 1 || 1 <= 2 )) ; echo $? #=> 0
echo $(( 1 ? 0 : 1 )) #=> 0
(( 1 ? 0 : 1 )) ; echo $? #=> 1
echo $(( 1, 0 )) #=> 0
(( 1, 0 )) ; echo $? #=> 1
echo $(( 1, 1 )) #=> 1
(( 1, 1 )) ; echo $? #=> 0
echo $(( true )) #=> 0
(( true )) ; echo $? #=> 1
echo $(( false )) #=> 0
(( false )) ; echo $? #=> 1
}}}
*** [[ expression ]] [#j04884c5]
#geshi(bash){{{
[[ expression ]]
}}}
- [[ は組み込みコマンドである。[ と振る舞いが異なることに注意。
- [[ と ]]; の間のexpressionでは展開の扱いが異なる。
-- 単語分割、ファイル名展開は行われない。
-- チルダ展開、パラメータ・変数展開、算出式展開、コマンド置換、プロセス置換、クオート除去は作用する。
- >、< は、現在のlocaleで作用する。
- ==、!= は、右辺がパターンマーチングのルールで判定される。&code(){extglob}; オプションが有効な場合、= は、 == と等しい。&code(){nocasematch}; オプションが有効な場合、アルファベットの文字大小を無視する。終了ステータスは、== でマッチ、!= でマッチしないを満たす場合は0、そう出ない場合は1。
- =~ は、右辺の文字列をPOSIX拡張正規表現として動作する。正規表現構文が正しくない場合、終了ステータスは2となる。グルーピングにマッチした文字列は、&code(){BASH_REMATCH};配列に保存される。
#geshi(bash){{{
# --- commands ---
touch file_ab
line="ls file_*"
#1
[[ $($line) =~ (.+)_(a)b ]]
echo $?
echo "${BASH_REMATCH[@]}"
#2
[[ "$line" =~ (.+)_(a)b ]]
echo $?
echo "${BASH_REMATCH[@]}"
#3
[[ $(cat <(ls file_*) ) =~ (.+)_(a)b ]]
echo $?
echo "${BASH_REMATCH[@]}"
#4
echo ~
[[ ~ =~ /home/([^/]+) ]]
echo $?
echo "${BASH_REMATCH[@]}"
#5
pattern='/home/([^/]+)'
[[ ~ =~ $pattern ]]
echo $?
echo "${BASH_REMATCH[@]}"
#6 - ""すると、#5とは異なるので注意
pattern='/home/([^/]+)'
[[ ~ =~ "$pattern" ]]
echo $?
echo "${BASH_REMATCH[@]}"
:
[[ '/home/([^/]+)' =~ "$pattern" ]]
echo $?
echo "${BASH_REMATCH[@]}"
#7
pattern='\.'
[[ . =~ $pattern ]]
[[ . =~ \. ]]
echo $?
echo "${BASH_REMATCH[@]}"
[[ . =~ "$pattern" ]]
[[ . =~ '\.' ]]
echo $?
echo "${BASH_REMATCH[@]}"
[[ "\." =~ '\.' ]]
echo $?
echo "${BASH_REMATCH[@]}"
# --- output ---
#1
0
file_ab file a
#2
1
#3
0
file_ab file a
#4
/home/guest
0
/home/guest guest
#5
0
/home/guest guest
#6 - ""すると、#5とは異なるので注意
1
0
/home/([^/]+)
#7
0
.
1
0
\.
}}}
*** ( expression ) [#x75a8176]
expressionの値を返す。演算子の優先順位を変えたい時とか。
#geshi(bash){{{
# --- commands ---
i=-1
#1
if (( i > 0 && i < 100 || i < 0 )); then
echo "#1 true"
else
echo "#1 false"
fi
#2
if (( i > 0 && ( i < 100 || i < 0 ) )); then
echo "#2 true"
else
echo "#2 false"
fi
# --- output ---
#1
#1 true
#2
#2 false
}}}
*** ! expression [#he71520c]
expressionがtrueならばfalseを返す。
#geshi(bash){{{
# --- commands ---
#1
if (( 1 > 0 )); then
echo "true"
else
echo "false"
fi
#2
if (( !(1 > 0) )); then
echo "true"
else
echo "false"
fi
# --- output ---
#1
true
#2
false
}}}
*** expression1 && expression2、expression1 || expression2 [#dc7f9333]
expression1で全体の真、偽が決まる場合は、expression2を評価しない。
- expression1 && expression2
-- expression1がfalse、expression2は評価しない、結果はfalse
- expression1 || expression2
-- expression1がtrue、expression2は評価しない、結果はtrue
#geshi(bash){{{
(( 1 > 0 )) && { echo "expr2"; } #=> expr2
(( 1 < 0 )) && { echo "expr2"; } #=>
(( 1 > 0 )) || { echo "expr2"; } #=>
(( 1 < 0 )) || { echo "expr2"; } #=> expr2
}}}
&label(warn){参考}; https://www.gnu.org/software/bash/manual/bash.html#Conditional-Constructs
** グルーピング [#jc6384ba]
*** ( list ) [#pc7d6953]
サブシェルで実行される。変数の割り当ても親シェルには影響しない。~
親シェルのPWDはそのままで、一時的にcdして何らかの処理を実行するといった時にも使える。
#geshi(bash){{{
# --- commands ---
PARAM=10
( PARAM=20 ; echo $PARAM )
echo $PARAM
mkdir -p subdir
pwd
( cd subdir; pwd; )
pwd
# --- output ---
20
10
/home/guest/workspace/bash
/home/guest/workspace/bash/subdir
/home/guest/workspace/bash
}}}
*** { list; } [#a57f16fe]
現在のコンテキストで実行される。}の後にリダイレクトを指定すると、グルーピングされたコマンドの結果をリダイレクト先にまとめて送ることができる。
#geshi(bash){{{
# --- commands ---
PARAM=10
{ PARAM=20 ; echo $PARAM ; }
echo $PARAM
{
echo "foo"
echo "bar"
} > out.txt
cat out.txt
mkdir -p subdir
pwd
{ cd subdir; pwd; }
pwd
# --- output ---
20
20
foo
bar
/home/guest/workspace/bash
/home/guest/workspace/bash/subdir
/home/guest/workspace/bash/subdir
}}}
&label(warn){参考}; https://www.gnu.org/software/bash/manual/bash.html#Command-Grouping
** Coprocesses [#q3c85d6a]
#geshi(bash){{{
coproc [NAME] command [redirections]
}}}
- サブシェルで非同期にコマンド実行することができる。&を使ったバックグラウンド実行と似ている。coprocで実行されたシェルと実行元のシェル間にはstdinとstdoutのパイプが作られる。pipeを呼び、forkしている。
- コマンドのstdoutは NAME[0]、stdinは NAME[1]
#geshi(bash){{{
# --- commands ---
coproc MY {
read line
echo "$line, guest!"
sleep 1
echo "hello"
sleep 1
}
echo ${MY[@]}
echo "hello" >&${MY[1]}
cat - <&${MY[0]}
wait
# --- output ---
63 60
hello, guest!
hello
}}}
** GNU Parallel [#t34180a7]
コマンドを並列実行するための機能。Bashにビルドインされているものではない。
https://www.gnu.org/software/parallel/
並列実行であれば、xargs -Pもカジュアルに実行できるグレートな方法である。
https://linuxjm.osdn.jp/html/GNU_findutils/man1/xargs.1.html
* 関数 [#q23215f5]
#geshi(bash){{{
name () compound-command [ redirections ]
function name [()] compound-command [ redirections ]
}}}
- 関数の終了ステータスは、関数内の最後のコマンドの終了ステータスとなる。return [終了ステータス]で関数を抜ける。終了ステータスが指定されない場合は、returnの前にコマンドの終了ステータスとなる。
- 引数は、&code(){$1〜};の様な位置パラメータや&code(){$@};、&code(){$*};で参照可能である。関数を終了すると、これらの値は関数実行前の状態にリストアされる(関数内でshiftしたりしても、関数を抜けると呼び出し前の状態である)。
- &code(){FUNCNAME};は、実行中の関数名が入っている。FUNCNAMEは、配列でネストした関数スタックを取得できる。
- &code(){unset -f };で定義済み関数を削除できる。
- local指定で、関数ローカルな変数定義ができる。グローバルと同様な名前であっても、local指定していれば関数内ではlocalで指定した値を使用でき、グルーバルを汚染しない。変数の可視性は動的スコープ。
- declare -f、typeset -fで関数定義を確認できる。-Fオプションの場合は名前のみを表示する。
#geshi(bash){{{
# --- commands ---
:> "$PWD/output"
hello() {
echo "hello func"
}
# functionがある場合は()は省略可
function hello_with_redirections {
echo "$@"
} &>> "$PWD/output"
shortfunc() { echo "$FUNCNAME"; }
shortfunc2() { echo "this is ok"; }
# shortfunc3() { echo "this is error, ; is required" }
#1
hello
#2
hello_with_redirections "hello redirections"
#3
cat "$PWD/output"
#4
shortfunc
#5
shortfunc2
# shortfunc3
# --- output ---
#1
hello func
#2
#3
hello redirections
#4
shortfunc
#5
this is ok
}}}
関数定義を削除してみる。
#geshi(bash){{{
# --- commands ---
function myfunc() {
echo $FUNCNAME
}
# 関数定義の確認
#1
type myfunc ; echo $?
#2
type -t myfunc ; echo $?
#3
myfunc
# 関数定義を削除
#4
unset -f myfunc ; echo $?
#5
type myfunc ; echo $?
# --- output ---
#1
myfunc は関数です
myfunc ()
{
echo $FUNCNAME
}
0
#2
function
0
#3
myfunc
#4
0
#5
type: myfunc: 見つかりません
1
}}}
関数の呼び出しスタックを見る。トップレベルの関数名はmainとなる。
#geshi(bash){{{
# --- commands ---
f0() {
echo ${FUNCNAME[@]}
}
f1() {
f0
}
f2() {
f1
}
#1
f2
# --- output ---
#1
f0 f1 f2 main
}}}
変数の可視性について確認する。以下の例を見た方が理解できる。一連の関数の呼び出しにおいて、呼び出し元(caller)が変数の定義をした場合、呼び出される側では、呼び出し側で上書きされた値が見える。
#geshi(bash){{{
# --- commands ---
function f0() {
var="change to f0"
echo "6) After change: In $FUNCNAME, var=$var"
}
function f1() {
echo "1) In $FUNCNAME, var=$var"
local var="f1"
echo "2) Define local: In $FUNCNAME, var=$var"
unset var
echo "3) After unset: In $FUNCNAME, var=$var"
var="change to f1"
echo "4) After change: In $FUNCNAME, var=$var"
}
function f2() {
local var="f2"
f1
echo "5) In $FUNCNAME, var=$var"
f0
}
function f3() {
var="change to f3"
echo "8) After change: In $FUNCNAME, var=$var"
}
var="global"
f2
echo "7) In $FUNCNAME, var=$var"
f3
echo "9) In $FUNCNAME, var=$var"
# --- output ---
1) In f1, var=f2
2) Define local: In f1, var=f1
3) After unset: In f1, var=
4) After change: In f1, var=change to f1
5) In f2, var=f2
6) After change: In f0, var=change to f0
7) In , var=global
8) After change: In f3, var=change to f3
9) In , var=change to f3
}}}
関数定義の確認
#geshi(bash){{{
# --- commands ---
function my1() {
:
}
function my2() {
:
}
typeset -f
typeset -F
# --- output ---
my1 ()
{
:
}
my2 ()
{
:
}
declare -f my1
declare -f my2
}}}
* 変数 [#vcdab9f8]
#geshi(bash){{{
name=[value]
}}}
- valueが指定されない場合は、null
- チルダ展開、変数展開、コマンド置換、算術展開、クオートremovalが行われる。単語分割、ファイル名展開は行われない。
- 整数が渡される場合は、数値として評価される
*** += [#ma5ad401]
&code(){+=};は、valueへの追加、配列の場合は要素追加する。&color(red){数値が設定されていても変数属性がintegerとなっていない場合は、単なる文字列結合となるので注意。};。~
変数が配列の場合は、+=で要素を追加すると末尾に追加される。
#geshi(bash){{{
b=1
b+=1
echo $b #=> 11
# integerとして定義
declare -i a
a=1
a+=1
echo $a #=> 2
# 配列として定義
declare -a arr
arr=(1 2)
arr+=(1)
echo ${arr[*]} #=> 1 2 1
arr+=1 # この場合は、arr[0]の値に1が追加される
echo ${arr[*]} #=> 11 2 1
# 連想配列として定義
declare -A map
map=(["a"]=1 ["b"]=2)
map+=(["c"]=5)
echo ${map[@]} #=> 1 2 5
echo ${!map[@]} #=> a b c
map["e"]=6
echo ${!map[@]} #=> a b c e
echo ${map[@]} #=> 1 2 5 6
}}}
*** 参照変数 [#o13130c3]
&code(){declare -n}; で参照変数を定義することができる。unset -n で参照を削除する。-nがない場合は、参照先の変数がunsetされる。配列変数は、変数参照指定ができないが、要素には可能である。
#geshi(bash){{{
# --- commands ---
my() {
local -n ref=$1
ref=${ref^^t}
}
var="test"
# 変数名を渡し関数内で操作
my var
#1
echo $var
declare -n ref2=var
declare -n ref3=var
#2 -nオプションありでunset
declare -p ref2
unset -n ref2
declare -p ref2
#3 -nオプションなしでunset
declare -p var
declare -p ref3
unset ref3
declare -p ref3
declare -p var
# --- output ---
#1
TesT
#2 -nオプションありでunset
declare -n ref2="var"
declare -- ref2
#3 -nオプションなしでunset
declare -- var="TesT"
declare -n ref3="var"
declare -n ref3="var"
declare: var: 見つかりません
}}}
for文で制御変数を参照設定して操作することも可能である。
#geshi(bash){{{
# --- commands ---
a="echo a > out"
b="echo b >> out"
c="echo c >> out"
d="cat out"
declare -n ref
for ref in {a,b,c,d}; do
# a b c dの変数に設定されているコマンド文字列を実行
eval $ref
done
# --- output ---
a
b
c
}}}
** 位置パラメータ [#ve953f26]
- $N、${N}で引数で渡される値を取得できる。Nがシングルな数値でない場合はブレース{}がいる。
- $0は、実行されたファイル名を示す。
- setやshiftでset、unsetできる。
- 関数実行時には、一時的に位置パラメータは置き換えられる。
- $#は位置パラメータの数を示す。
- $- は、setのオプションを示す。
#geshi(bash){{{
# --- commands ---
# file: vars2.sh
arg_n=66001
function f() {
echo $0
local v= i
# 1000ずつ飛ばして表示してみる
for i in $(seq 1 1000 $arg_n); do
v="\${$i}"
printf "# $v = %s " "$(eval echo $v)"
done
}
function f2() {
echo "in f2: len(args) = $#"
while [ $# -gt 0 ]; do
echo $1
shift
done
}
#1
echo $0
#2
f $(seq 1 $arg_n | xargs printf 'A%d ')
#3
set a b c d # 位置パラメータに値を設定
while [ $# -gt 0 ]; do
echo $1
shift 2 # 1つ飛ばしで
done
#4
f2 a b c d
#5
echo $-
# --- output ---
#1
vars2.sh
#2
vars2.sh
# ${1} = A1 # ${1001} = A1001 # ${2001} = A2001 # ${3001} = A3001 # ${4001} = A4001 # ${5001} = A5001 # ${6001} = A6001 # ${7001} = A7001 # ${8001} = A8001 # ${9001} = A9001 # ${10001} = A10001 # ${11001} = A11001 # ${12001} = A12001 # ${13001} = A13001 # ${14001} = A14001 # ${15001} = A15001 # ${16001} = A16001 # ${17001} = A17001 # ${18001} = A18001 # ${19001} = A19001 # ${20001} = A20001 # ${21001} = A21001 # ${22001} = A22001 # ${23001} = A23001 # ${24001} = A24001 # ${25001} = A25001 # ${26001} = A26001 # ${27001} = A27001 # ${28001} = A28001 # ${29001} = A29001 # ${30001} = A30001 # ${31001} = A31001 # ${32001} = A32001 # ${33001} = A33001 # ${34001} = A34001 # ${35001} = A35001 # ${36001} = A36001 # ${37001} = A37001 # ${38001} = A38001 # ${39001} = A39001 # ${40001} = A40001 # ${41001} = A41001 # ${42001} = A42001 # ${43001} = A43001 # ${44001} = A44001 # ${45001} = A45001 # ${46001} = A46001 # ${47001} = A47001 # ${48001} = A48001 # ${49001} = A49001 # ${50001} = A50001 # ${51001} = A51001 # ${52001} = A52001 # ${53001} = A53001 # ${54001} = A54001 # ${55001} = A55001 # ${56001} = A56001 # ${57001} = A57001 # ${58001} = A58001 # ${59001} = A59001 # ${60001} = A60001 # ${61001} = A61001 # ${62001} = A62001 # ${63001} = A63001 # ${64001} = A64001 # ${65001} = A65001 # ${66001} = A66001
#3
a
c
#4
in f2: len(args) = 4
a
b
c
d
#5
hB
}}}
** 特殊パラメータ [#d4230903]
''$*''
位置パラメータに展開される。ダブルクォートで囲まれていない場合、個々のwordに展開される。そのコンテキストでは、単語分割とパス名展開が行われる。ダブルクオート内で囲まれている場合は、個々のパラメータをIFS変数に指定されている最初の文字で連結された1つの単語に展開される。
単語分割されるコンテキストでは、各位置パラメータは個々のwordに展開される。ダブルクオートなしの場合、wordは単語分割される。単語分割が行われないコンテキストでは、各wordをスペースで区切った1つの文字列に展開される。
"$@"が1つの文字列内で展開されるとき、最初のパラメータは前の文字列にジョインされ、最後のパラメータは、後の文字にジョインされる。
"$*"は、"$1c$2..."に等しい。cはIFSに指定されている先頭の文字。IFSがunsetされる場合はスペースで区切られる。IFSがnullならば、区切り文字なしで結合される。
#geshi(bash){{{
# --- commands ---
function f() {
echo "$1"
echo "$2"
echo "$3"
echo "in f(): args= $*"
# $1 $2 ...
f2 $*
# "$1" "$2" ...
f2 "$*"
# "s $1" ... "$4 z"
f2 "s $* z"
}
function f2() {
echo "in f2(): args= $*"
echo "num: $#"
echo "$1"
echo "$2"
echo "$3"
echo "$4"
}
function f3() {
echo "in f3()"
# $1 $2 ...
$*
}
function f4() {
echo "in f4()"
# "$1" "$2" ...
"$*"
}
function f5() {
echo "in f5()"
# "$10$2" ...
( IFS=0 && echo "$*" )
# "$1$2" ...
( IFS= && echo "$*" )
}
f a b c d
f3 "echo hello"
f4 "echo hello"
f5 "echo" "hello"
# --- output ---
a
b
c
in f(): args= a b c d
in f2(): args= a b c d
num: 4
a
b
c
d
in f2(): args= a b c d
num: 1
a b c d
in f2(): args= s a b c d z
num: 1
s a b c d z
in f3()
hello
in f4()
template.sh: 行 71: echo hello: コマンドが見つかりません
in f5()
echo0hello
echohello
}}}
''$@''
位置パラメータに展開される。単語分割されるコンテキストでは、各位置パラメータは個々のwordに展開される。ダブルクオートなしの場合、wordは単語分割される。単語分割が行われないコンテキストでは、各wordをスペースで区切った1つの文字列に展開される。
"$@"が1つの文字列内で展開されるとき、最初のパラメータは前の文字列にジョインされ、最後のパラメータは、後の文字にジョインされる。
"$@"は、"$1" "$2"に等しい。位置パラメータが無しの場合、"$@"や$@は何も無し。
#geshi(bash){{{
# --- commands ---
function f() {
echo "$1"
echo "$2"
echo "$3"
echo "in f(): args= $@"
# $1 $2 ...
f2 $@
# "$1" "$2" ...
f2 "$@"
# "s $1" ... "$4 z"
f2 "s $@ z"
}
function f2() {
echo "in f2(): args= $@"
echo "num: $#"
echo "$1"
echo "$2"
echo "$3"
echo "$4"
}
function f3() {
# $1 $2 ...
$@
}
function f4() {
# "$1" "$2" ...
"$@"
}
f a b c d
f3 "echo hello"
f4 "echo hello"
# --- output ---
a
b
c
in f(): args= a b c d
in f2(): args= a b c d
num: 4
a
b
c
d
in f2(): args= a b c d
num: 4
a
b
c
d
in f2(): args= s a b c d z
num: 4
s a
b
c
d z
hello
echo hello: コマンドが見つかりません
}}}
''$#''
位置パラメータの数に展開される。配列などは、${#array[@]}で要素数を取得できる。
#geshi(bash){{{
# file: test.sh
echo $@
echo $#
ar=(1 2 3)
echo ${#ar[@]}
# --- output ---
$ ./test.sh 1 2 3
1 2 3
3
3
}}}
''$?''
foregroundで実行される最近のコマンドの終了ステータスに展開される。
#geshi(bash){{{
# --- commands ---
function f_ret_0() {
[ 1 -ne 1 ]
# result should not be 0
}
function f2_ret_0() {
[ 1 -ne 1 ]
echo "f2"
# result should be 0
}
function f3_ret_0() {
case "$1" in
-h)
echo "option -h"
;;
esac
# result should be 0
}
function f4_ret_not_0() {
case "$1" in
-h)
echo "option -h"
[ 1 -ne 1 ]
;;
esac
# result should not be 0
}
#1
(( 1 == 1 ))
echo $?
#2: function f ends with exit status not 0
f_ret_0
echo $?
#3: function f2 ends with exit status 0
f2_ret_0
echo $?
#4: function f3 ends with exit status 0
f3_ret_0 -p
echo $?
#4: function f4 ends with exit status not 0
f4_ret_not_0 -h
echo $?
# --- output ---
#1
0
#2: function f ends with exit status not 0
1
#3: function f2 ends with exit status 0
f2
0
#4: function f3 ends with exit status 0
0
#4: function f4 ends with exit status not 0
option -h
1
}}}
''$-''
setコマンドやシェル自身によって設定される現在のオプションフラグに展開される。
#geshi(bash){{{
set -x
echo $-
set +x
echo $-
# ----- output -----
+ echo hxB
hxB
+ set +x
hB
}}}
対話シェルで実行した場合 (GNU bash, バージョン 4.4.23(1)-release-(x86_64-apple-darwin17.5.0)
#geshi(bash){{{
$ echo $-
himBH
}}}
''$$''
プロセスIDに展開される。サブシェルの中では、呼び出し元のプロセスIDに展開される。
#geshi(bash){{{
echo $BASHPID #=> 42071
echo $$ #=> 42071
bash -c 'echo $BASHPID; echo $$' #=> 42079 42079
( echo $$ ) #=> 42071
}}}
''$!''
バックグラウンドで実行された直近のジョブのプロセスIDに展開される。
#geshi(bash){{{
$ sleep 30 &
[1] 15816
$ echo $!
15816
$ echo "hello"
hello
$ echo $!
15816
$ sleep 30 &
[2] 15870
$ echo $!
15870
$ jobs -l
[1]- 15816 Running sleep 30 &
[2]+ 15870 Running sleep 30 &
}}}
例:nohupと組み合わせて使うケース
#geshi(bash){{{
#!/usr/bin/env bash
pidfile=job.pids
nohup bash -c 'i=0; while ((i<10)); do echo job1-$i; ((i++)); done' > stdout-1 2> stderr-1 &
echo $! > job.pids
nohup bash -c 'i=0; while ((i<10)); do echo job2-$i; ((i++)); done' > stdout-2 2> stderr-2 &
echo $! >> job.pids
}}}
''$0''
シェルスクリプト名に展開される。コマンドファイルで呼び出された場合は、ファイル名に展開される。-cオプションで呼び出された場合は、-c string arguments...となる場合の最初の引数が入る。それ以外では、Bashを呼び出すのに使用されたファイルの名前が入る。
#geshi(bash){{{
# hello.sh
echo $0
# ---------
./hello.sh #=> ./hello.sh
bash hello.sh #=> hello.sh
}}}
#geshi(bash){{{
bash -c 'echo $0' #=> bash
bash -c 'echo $0' hello #=> hello
}}}
#geshi(bash){{{
$ echo $0
bash
$ /bin/bash
$ echo $0
/bin/bash
$ ln -s /bin/bash hogehoge
$ ./hogehoge
$ echo $0
./hogehoge
}}}
''$_''
最後に実行されたコマンドの最後の引数に展開される。
#geshi(bash){{{
# --- commands ---
function f() {
echo "${1:-func}"
}
echo "#!/bin/bash
echo $_
" > com.sh
chmod +x com.sh
#1
ls -G > /dev/null
echo $_
#2
bash -c "/bin/echo"
echo $_
#3
bash -c "/bin/echo hello"
echo $_
#4
f
echo $_
#5
f func_arg
echo $_
#6
./com.sh
echo $_
#7
./com.sh hello
echo $_
# --- output ---
#1
-G
#2
/bin/echo
#3
hello
/bin/echo hello
#4
func
f
#5
func_arg
func_arg
#6
./com.sh
#7
hello
}}}
_ をシェルの変数名として設定することはできない、特殊パラメータとして予約されているためである。
#geshi(bash){{{
$ _=hoge
$ echo $_
}}}
** 配列の操作 [#e4d1b819]
** 連想配列の操作 [#ye165ab2]
* 展開 [#vdd07017]
展開の種類には以下がある。展開の順序を知っておくことで、最終的にどのような結果が得られるのか迷わずに済むだろう。~
以下の順序で展開が行われる。(ただし、二重引用符や単一引用符で囲まれる場合は、いくつかの展開がスキップされたりするがここでは割愛する)
+ ブレース展開
{a,b} {x..y..incr}
+ チルダ展開 、[ プロセス置換 ]
~ ~- ~+ [<(list) or >(list)]
+ パラメータと変数展開
${param} etc
+ コマンド置換
$(( expression ))
+ 算術式展開
$(command) or `command`
+ 単語の分割
word word split by $IFS
+ パス名展開
. * ? etc
全ての展開後に、クオートの除去( \ ' " )が行われる。コマンド検索が行われ、コマンド実行という流れとなる。
&label(warn){参考}; https://www.gnu.org/software/bash/manual/bash.html#Shell-Expansions
** ブレース展開 [#fc2c5271]
{x..y[..incr]}
- {} を使って行なう。ブレース展開を使うと、値の組み合わせのパターン生成に便利な手法。
- 他の展開よりも先に行われるので、変数展開を期待して埋め込んでも意図した結果にならないだろう。
- { や , はバックスラッシュでエスケープできる。
#geshi(bash){{{
param=10 #=>
echo `seq 1 10` #=> 1 2 3 4 5 6 7 8 9 10
echo {1..10..1} #=> 1 2 3 4 5 6 7 8 9 10
echo {1..10..3} #=> 1 4 7 10
echo {1..10} #=> 1 2 3 4 5 6 7 8 9 10
echo {10..1..-1} #=> 10 9 8 7 6 5 4 3 2 1
echo {10..-1} #=> 10 9 8 7 6 5 4 3 2 1 0 -1
echo {10..10..-1} #=> 10
echo {1..$param} #=> {1..10}
eval echo {1..$param} #=> 1 2 3 4 5 6 7 8 9 10
echo test{1,} #=> test1 test
echo test{1,2,3} #=> test1 test2 test3
echo test{,} #=> test test
echo test{{,}} #=> test{} test{}
echo {postgresql,pg_hba}.conf #=> postgresql.conf pg_hba.conf
}}}
&label(warn){参考}; https://www.gnu.org/software/bash/manual/bash.html#Brace-Expansion
** チルダ展開 [#r8b69aa5]
~ や ~-、~+ といった文字列の展開である。
|&code(){~}; |$HOME|
|&code(){~<user>};|ユーザ<user>の$HOME|
|&code(){~+};|$PWD|
|&code(){~-};|${OLDPWD:'~-'}|
|&code(){~N、~+N};|dirs +N|
|&code(){~-N};|dirs -N|
実際の動きを以下例で確認してみる。
#geshi(bash){{{
pwd #=> /home/guest/workspace/bash
echo $PWD #=> /home/guest/workspace/bash
echo $OLDPWD #=> /home/guest/workspace
# ~, ~user
echo ~ #=> /home/guest
echo ~guest #=> /home/guest
echo ~unknownuser #=> ~unknownuser
# dirs
echo ~+ #=> /home/guest/workspace/bash
echo ~- #=> /home/guest/workspace
unset OLDPWD #=>
echo ~- #=> ~-
dirs -l #=> /home/guest/workspace/bash
pushd /tmp #=> /tmp ~/workspace/bash
echo ~1 #=> /home/guest/workspace/bash
echo ~+1 #=> /home/guest/workspace/bash
echo ~-1 #=> /tmp
}}}
&label(warn){参考}; https://www.gnu.org/software/bash/manual/bash.html#Tilde-Expansion
** パラメータ展開 [#mf070ac4]
*** ${parameter} [#lc33dd13]
parameterの値に置換される。
#geshi(bash){{{
#!/usr/bin/env bash
PARAM="parameter"
echo $PARAM #=> parameter
}}}
*** ${parameter:-word} [#r6cf08cd]
parameter が設定されていないか空文字列であれば、 wordを展開したものに置換される。そうでなければ、 parameter の値に置換される。
変数のデフォルト値を設定したい場合などによく使う。
#geshi(bash){{{
echo $USER #=> "moritetu"
# ${parameter:-word}
SSH_USER="${USER:-"guest"}"
echo "$SSH_USER" #=> "moritetu"
SSH_OPTS="${OPTIONS:-""}"
echo "$SSH_OPTS" #=> ""
}}}
*** ${parameter:=word} [#h57e3e86]
parameterが設定されていないか空文字列であれば、 wordを展開したものがparameter に代入される。その後、parameter の値への置換が行われる。
#geshi(bash){{{
# ${parameter:=word}
# ex1
${COMMAND:="date"} #=> 2019年 1月30日 水曜日 22時10分22秒 JST
# ex2
: ${PARAM:="param1"}
echo $PARAM #=> param1
}}}
*** ${parameter:?word} [#v638fa90]
- parameterが空文字列または設定されていない場合、word を展開したものが標準エラー出力に出力される。
- wordがなければ パラメータが空文字列または設定されていないことを示すメッセージが標準エラー出力に出力される。
- 対話的シェルでなければ、 シェルは終了する。
- parameterに空文字列以外が設定されていれば、 parameter 値への置換が行われる。
#geshi(bash){{{
# ${parameter:?word}
# ex1
${not_defined_param:?} #=> not_defined_param: パラメータが null または設定されていません
# シェルは終了する
# ex2
${not_defined_param2:?"parameter is not defined"} #=> not_defined_param2: parameter is not defined
# シェルは終了する
# ex3
defined_param2="defined_param2"
echo ${defined_param2:?"parameter is not defined"} #=> defined_param2
}}}
*** ${parameter:+word} [#g28240aa]
parameter が空文字列または設定されていなければ、空文字列に置換される。そうでなければ word を展開したものに置換される。${parameter:-word} の逆。
#geshi(bash){{{
# ${parameter:+word}
echo ${parameter:+"word"} #=> ""
parameter="defined"
echo ${parameter:+"word"} #=> word
}}}
*** ${parameter:offset}、${parameter:offset:length} [#v6387b01]
- 部分文字列の展開を行なう。
- length指定がある場合は、parameter を展開したものから最大 length 文字を取り出す。length指定がない場合は、指定したインデックスから末尾までの文字を取り出す。
- 配列の場合は、指定したインデックスからlengthで指定される要素を取り出す。
#geshi(bash){{{
# ${parameter:offset}
parameter="012345" #=>
array=(0 1 2 3 4 5) #=>
echo ${parameter:2} #=> 2345
echo ${parameter:-1} #=> 012345
echo ${parameter:-2} #=> 012345
echo ${array[@]:-2} #=> 0 1 2 3 4 5
echo ${array[@]:2} #=> 2 3 4 5
echo ${parameter:2:2} #=> 23
echo ${parameter:2:-1} #=> 234
echo ${array[@]:2:1} #=> 2
echo ${array[@]:2:-1} #=> parameter.sh: 行 8: -1: substring expression < 0
}}}
*** ${!prefix*}、${!prefix@} [#jd3445b4]
prefix で始まる全ての変数の名前に展開して、 IFS 特殊変数の最初の文字によって区切る。
#geshi(bash){{{
# ${!prefix*}
parameter_1="p1" #=>
parameter_2="p2" #=>
echo ${!param*} #=> parameter_1 parameter_2
(IFS="_|" ; echo ${!param*}) #=> parameter 1 parameter 2
# ${!prefix@}
parameter_1="p1" #=>
parameter_2="p2" #=>
echo ${!param@} #=> parameter_1 parameter_2
for p in "${!param@}"; do eval echo \$$p; done #=> p1
p2
}}}
*** ${!name[*]}、${!name[@]} [#vf3ba1d3]
- 配列のキーのリストに展開される。ハッシュの場合は、キーを取り出すことができる。
- 配列の場合は、インデックスとなる。*と@の違いは、ダブルクォートで囲まれた場合の展開方法が異なる。
#geshi(bash){{{
array=(a b c d e f) #=>
# ${!name[*]}
echo ${!array[*]} #=> 0 1 2 3 4 5
# ${!name[@]}
echo ${!array[@]} #=> 0 1 2 3 4 5
# ハッシュ定義
declare -g -A map #=>
map=(["a"]=0 ["b"]=1 ["c"]=2 ["d"]=3 ["e"]=4 ["f"]=5) #=>
echo ${!map[*]} #=> a b c d e f
echo ${map[@]} #=> 0 1 2 3 4 5
echo ${!map[@]} #=> a b c d e f
}}}
*** ${#parameter} [#h4f5448b]
パラメータの長さを示す。文字列の場合は文字列の長さ、配列の場合は要素数。
#geshi(bash){{{
# ${$parameter}
strings="12345" #=>
echo ${#strings} #=> 5
strings="あいう" #=>
echo ${#strings} #=> 3
array=(0 1 2 3 4 5) #=>
echo ${#array} #=> 1
echo ${#array[@]} #=> 6
}}}
*** ${parameter#word}、${parameter##word} [#y841fdc6]
- parameterに対しwordで前方一致するパターンを取り除いた結果を返す。#は最短一致、##は最長一致のパターン。
- parameterが@や*の配列変数の場合、全ての要素に対して順番に適用される。
#geshi(bash){{{
parameter="test.sh.j2" #=>
array=(index.html.erb hello.html.erb) #=>
# ${parameter#word}
echo ${parameter#*.} #=> sh.j2
echo ${array[@]#*.} #=> html.erb html.erb
# ${parameter##word}
echo ${parameter##*.} #=> j2
echo ${array[@]##*.} #=> erb erb
}}}
*** ${parameter%word}、${parameter%%word} [#tf4bc178]
- parameterに対しwordで''後方一致''するパターンを取り除いた結果を返す。#は最短一致、##は最長一致のパターン。
- parameterが@や*の配列変数の場合、全ての要素に対して順番に適用される。
#geshi(bash){{{
parameter="test.sh.j2" #=>
array=(index.html.erb hello.html.erb) #=>
# ${parameter%word}
echo ${parameter%.*} #=> test.sh
echo ${array[@]%.*} #=> index.html hello.html
# ${parameter%%word}
echo ${parameter%%.*} #=> test
echo ${array[@]%%.*} #=> index hello
}}}
*** ${parameter/pattern/string} [#i4603bba]
- parameterのpatternの最長一致する部分をstringに置換する。
- / で始まる場合は、patternにマッチした全てが置換される。
- # で始まる場合は、parameterを展開した値の先頭にマッチ。
- %で始まる場合は、parameterを展開した値の末尾にマッチ。
- parameterが、@や*の場合は、全ての要素に適用される。
#geshi(bash){{{
parameter="# this is a comment line." #=>
array=("# this is a comment line." "# this is not a comment line.") #=>
# ${parameter/pattern/string}
echo ${parameter/i/I} #=> # thIs is a comment line.
echo ${parameter//i/I} #=> # thIs Is a comment lIne.
echo ${parameter[*]//i/I} #=> # thIs Is a comment lIne.
echo ${parameter/##/#=>} #=> #=> this is a comment line.
echo ${parameter/#this/This} #=> # this is a comment line.
echo ${parameter[@]/#this/This} #=> # this is a comment line.
echo ${parameter/%./!} #=> # this is a comment line!
echo ${parameter/%e/!} #=> # this is a comment line.
echo ${parameter[@]/%e/!} #=> # this is a comment line.
}}}
*** ${parameter^pattern}、${parameter^^pattern}、${parameter,pattern}、${parameter,,pattern} [#lce334bc]
- parameterに含まれるアルファベットの大文字小文字を変換する。
- ^ は、patternにマッチした小文字を大文字に変換し、, は、大文字を小文字に変換する。これらは、最初にマッチした部分のみ置換する。
- ^^ と ,, は、マッチした全ての文字を置換する。
- parameterが、@や* の場合は、全ての要素に適用される。
#geshi(bash){{{
parameter="hellO, WOrld" #=>
array=("hellO, WOrld" "hellO, WOrld2") #=>
# ${parameter^pattern}
echo ${parameter^l} #=> hellO, WOrld
echo ${array[@]^l} #=> hellO, WOrld hellO, WOrld2
# ${parameter^^pattern}
echo ${parameter^^l} #=> heLLO, WOrLd
echo ${array[@]^^l} #=> heLLO, WOrLd heLLO, WOrLd2
# ${parameter,pattern}
echo ${parameter,O} #=> hellO, WOrld
echo ${array[@],,O} #=> hello, World hello, World2
# ${parameter,,pattern}
echo ${parameter,,O} #=> hello, World
echo ${array[@],,O} #=> hello, World hello, World2
}}}
&label(warn){参考}; https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion
** コマンド置換 [#p8157dd3]
** 算術展開 [#of170bf3]
** プロセス置換 [#we760055]
- &code(){>(list)};は、ファイルへの出力がlistへの入力となる。
- &code(){<(list)};は、listの出力がファイルへの入力となる。
&label(sample){サンプル}; ''lsの結果で*.shファイルのみを出力''
#geshi(bash,number){{{
a=
while IFS= read -r line
do
a="$line:$a"
done < <(ls . | grep -e "\.sh$")
}}}
&label(sample){サンプル}; ''a.txtとb.txtをdiffの入力ファイルとして比較''
#geshi(bash,number){{{
diff -u <(cat a.txt) <(cat b.txt)
}}}
&label(sample){サンプル};
#geshi(bash,number){{{
exec 3>&1
exec > >(while read line; do echo "$(date): $line";done)
echo "hello"
echo "bar"
exec 1>&3 3>&-
}}}
** 単語分割 [#sa8c290a]
** ファイル名展開 [#j7d55b04]
*** パターンマッチング [#s0fe7d16]
* リダイレクション [#q2763390]
** execを使った入出力のリダイレクト [#s12f0ef4]
execコマンドでリダイレクトだけを指定すると、シェル自身のファイル記述子とデバイスの対応を変更することができる。
**** &label(sample){例}; execを使った入力のリダイレクト [#z15e5164]
#geshi(bash,number){{{
# copy stdin to descriptor 3
exec 3<&0
# redirect stdin to test.c
exec < test.c
# read a line from test.c
read line
echo $line
# restore stdin and close descriptor 3
exec 0<&3 3<&-
}}}
**** &label(sample){例}; execを使った出力のリダイレクト [#z15e5164]
#geshi(bash,number){{{
# save stdout as descriptor 3
exec 3>&1
exec > test.txt
echo "this message is written into the test.txt"
# restore stdout and close descriptor 3
exec 1>&3 3>&-
}}}
使い道はさておき、こんなのもできる。
#geshi(bash){{{
:> jobs
# stdoutを63に複製
exec 63>&1
# 63をプロセス置換でpipeの入力にする
exec 63> >(
echo "child pid: $BASHPID"
while read line; do
echo "$(date): received: $line" | tee "$line.log"
touch "$line.done"
done
)
i=0
declare -r MAX_COUNT=10
echo "parent pid: $BASHPID"
while read line; do
if [ "$line" = "q" ] || [ "$line" = "quit" ]; then
break
fi
# プロセス置換のコマンドに送る
echo "$line" >&63
i=1
until [ -f "$line.done" ] || (( i > $MAX_COUNT )); do
sleep 1
((i+=1))
done
if (( i>$MAX_COUNT )); then
echo "exceeded $MAX_COUNT seconds"
fi
done < <(tail -f jobs) # jobsファイルから次のジョブを受け取る
# stdoutを戻し63を閉じる
exec 1>&63-
# ---- output -----
parent pid: 86757
child pid: 86772
2020年 1月 1日 水曜日 23時26分25秒 JST: received: job1
2020年 1月 1日 水曜日 23時26分28秒 JST: received: job1
2020年 1月 1日 水曜日 23時26分29秒 JST: received: job1
# ----- 別シェルから -----
echo "job1" > jobs
echo "job1" > jobs
echo "job1" > jobs
}}}
特定の式や文の出力をまとめてリダイレクトしたい場合は、ブレースを使った複合コマンドでも処理できる。
&label(sample){例}; ''複合コマンドの出力をファイルにリダイレクトする''
#geshi(bash,number){{{
#!/usr/bin/env bash
# 複合コマンド
{
# stdout.txtに出力
echo "this message is written into stdout.txt"
# stderr.txtに出力
echo "this message is written into stderr.txt" >&2
} > stdout.txt 2> stderr.txt
}}}
* 参考リンク [#q8d32fb3]
- [[(PR) 入門bash 第3版>http://www.amazon.co.jp/exec/obidos/ASIN/4873112540/grepgrape-22/ref=nosim/]] - &size(11){&color(gray){on http://www.amazon.co.jp/exec/obidos/ASIN/4873112540/};};
- [[Advanced Bash-Scripting Guid>http://tldp.org/LDP/abs/html/index.html]] - &size(11){&color(gray){on http://tldp.org/LDP/abs/html/index.html};};
- [[GNU Bash manual>https://www.gnu.org/software/bash/manual/]] - &size(11){&color(gray){on https://www.gnu.org/software/bash/manual/};};
- [[Man page of BASH>https://linuxjm.osdn.jp/html/GNU_bash/man1/bash.1.html]] - &size(11){&color(gray){on https://linuxjm.osdn.jp/html/GNU_bash/man1/bash.1.html};};
- テストフレームワーク
-- [[Bats>https://github.com/sstephenson/bats]] - &size(11){&color(gray){on https://github.com/sstephenson/bats};};
-- [[shunit2>https://github.com/kward/shunit2]] - &size(11){&color(gray){on https://github.com/kward/shunit2};};
--- [[Pac Learner shUnit2 2.1.x ドキュメント>https://sites.google.com/site/paclearner/shunit2-documentation]] - &size(11){&color(gray){on https://sites.google.com/site/paclearner/shunit2-documentation};};
-- [[Baut (Bash Unittest Tool)>https://github.com/moritetu/baut]] - - &size(11){&color(gray){on https://github.com/moritetu/baut};};
- [[Bash $((算術式)) のすべて - A 基本編>https://qiita.com/akinomyoga/items/9761031c551d43307374]] - &size(11){&color(gray){on https://qiita.com/akinomyoga/items/9761031c551d43307374};};