Bashマニュアルから(自分が)頻繁に使用するであろう機能をピックアップしてまとめる。

コマンド

ループ

until

until test-commands; do consequent-commands; done

条件が成り立つまで繰り返す。

# --- 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

while test-commands; do consequent-commands; done

条件が成り立つ間繰り返す。

# --- 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

for name [ [in [words …] ] ; ] do commands; done
for (( expr1 ; expr2 ; expr3 )) ; do commands ; done

wordsの数繰り返す。*1 は、他のプログラミング言語同様の構文。
expr1が最初に評価され、expr2がゼロになるまで、commandsを繰り返し評価する。expr2がゼロでない場合は、expr3が評価される。

# --- 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

参考 https://www.gnu.org/software/bash/manual/bash.html#Looping-Constructs

条件

if

if test-commands; then
  consequent-commands;
[elif more-test-commands; then
  more-consequents;]
[else alternate-consequents;]
fi

ifの条件式には、コマンドやBashの条件式(-aや-fなど)、算術式などを指定することができる。

# --- 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

case word in
    [ [(] pattern [| pattern]) command-list ;;]esac

select

メニューを生成するコマンド。複数のリストから選択させたい場合に便利である。

# --- 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

expression

(( expression ))  
let "expression"

expressionの結果がゼロでない場合、0が返る。そうでない場合は、1。

[[ expression ]]
( expression )
! expression
expression1 && expression2
expression1 || expression2

参考 https://www.gnu.org/software/bash/manual/bash.html#Conditional-Constructs

展開

展開の種類には以下がある。展開の順序を知っておくことで、最終的にどのような結果が得られるのか迷わずに済むだろう。
以下の順序で展開が行われる。(ただし、二重引用符や単一引用符で囲まれる場合は、いくつかの展開がスキップされたりするがここでは割愛する)

  1. ブレース展開
    {a,b}  {x..y..incr}
  2. チルダ展開 、[ プロセス置換 ]
    ~  ~-  ~+   [<(list)  or  >(list)]
  3. パラメータと変数展開
    ${param} etc
  4. コマンド置換
    $(( expression ))
  5. 算術式展開
    $(command) or  `command`
  6. 単語の分割
    word word     split by $IFS
  7. パス名展開
    .  *  ?  etc

全ての展開後に、クオートの除去( \ ' " )が行われる。コマンド検索が行われ、コマンド実行という流れとなる。

参考 https://www.gnu.org/software/bash/manual/bash.html#Shell-Expansions

ブレース展開

{x..y[..incr]}
select name [in words …]; do commands; done

参考 https://www.gnu.org/software/bash/manual/bash.html#Brace-Expansion

チルダ展開

や ~-、~+ といった文字列の展開である。

~$HOME
~<user>ユーザ<user>の$HOME
~+$PWD
~-${OLDPWD:'~-'}
~N、~+Ndirs +N
~-Ndirs -N

実際の動きを以下例で確認してみる。

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)

参考 https://www.gnu.org/software/bash/manual/bash.html#Tilde-Expansion

パラメータ展開

${parameter}

parameterの値に置換される。

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)

${parameter:-word}

parameter が設定されていないか空文字列であれば、 wordを展開したものに置換される。そうでなければ、 parameter の値に置換される。

変数のデフォルト値を設定したい場合などによく使う。

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
#? 

${parameter:=word}

parameterが設定されていないか空文字列であれば、 wordを展開したものがparameter に代入される。その後、parameter の値への置換が行われる。

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)

${parameter:?word}

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

${parameter:+word}

parameter が空文字列または設定されていなければ、空文字列に置換される。そうでなければ word を展開したものに置換される。${parameter:-word} の逆。

[[ expression ]]

${parameter:offset}、${parameter:offset:length}

# --- 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
\.

${!prefix*}、${!prefix@}

prefix で始まる全ての変数の名前に展開して、 IFS 特殊変数の最初の文字によって区切る。

# --- 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

${!name[*]}、${!name[@]}

# --- 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

${#parameter}

パラメータの長さを示す。文字列の場合は文字列の長さ、配列の場合は要素数。

(( 1 > 0 )) && { echo "expr2"; }     #=> expr2
(( 1 < 0 )) && { echo "expr2"; }     #=> 
(( 1 > 0 )) || { echo "expr2"; }     #=> 
(( 1 < 0 )) || { echo "expr2"; }     #=> expr2

${parameter#word}、${parameter##word}

# --- 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

${parameter%word}、${parameter%%word}

# --- 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

${parameter/pattern/string}

coproc [NAME] command [redirections]

${parameter^pattern}、${parameter^^pattern}、${parameter,pattern}、${parameter,,pattern}

# --- 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

参考 https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion

プロセス置換

サンプル lsの結果で*.shファイルのみを出力

name () compound-command [ redirections ]
function name [()] compound-command [ redirections ]

サンプル a.txtとb.txtをdiffの入力ファイルとして比較

# --- 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

サンプル

# --- 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

execを使った入出力のリダイレクト

execコマンドでリダイレクトだけを指定すると、シェル自身のファイル記述子とデバイスの対応を変更することができる。

execを使った入力のリダイレクト
# --- commands ---

f0() {
  echo ${FUNCNAME[@]}
}

f1() {
  f0
}

f2() {
  f1
}

#1
f2

# --- output ---


#1
f0 f1 f2 main
execを使った出力のリダイレクト
# --- 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

特定の式や文の出力をまとめてリダイレクトしたい場合は、ブレースを使った複合コマンドでも処理できる。

複合コマンドの出力をファイルにリダイレクトする

# --- commands ---

function my1() {
  :
}

function my2() {
  :
}

typeset -f
typeset -F

# --- output ---

my1 () 
{ 
    :
}
my2 () 
{ 
    :
}
declare -f my1
declare -f my2

参考リンク


トップ   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
目次
ダブルクリックで閉じるTOP | 閉じる
GO TO TOP