rcmdnk's blog

マスタリングLinuxシェルスクリプト 第2版 ―Linuxコマンド、bashスクリプト、シェルプログラミング実践入門

今更ながら知ったこと。

通常の方法

bashとかでよくやる方法は

arg1.sh
1
2
3
4
5
#!/usr/bin/env bash

for arg in "$@";do
  echo "$arg"
done

とする方法。

"$@"はスクリプトの引数を展開し、ダブルクォートしてあると 引数毎に分けてダブルクォートした状態で展開します。

1
2
3
4
$ ./arg1.sh a b c
a
b
c

"$*"とすると同じく展開しますがダブルクォートが全体について for文で回すと1つの引数として扱われます。

大概のシェルで同様の動作をします。

これは関数の引数も同じで、

arg2.sh
1
2
3
4
5
6
7
8
9
#!/usr/bin/env bash

function func {
  for arg in "$@";do
    echo "$arg"
  done
}

func a b c

としても

1
2
3
4
$ ./arg2.sh
a
b
c

となります。

以下も関数でもスクリプトの直接の引数でも同じです。

in無し

実はこれがこんな感じで書けます。

arg3.sh
1
2
3
4
5
#!/usr/bin/env bash

for arg do
  echo "$arg"
done

これでも同じ結果になります。

ここではまず in "$@"が消えていて、さらにdoの前の;も省略されています。

知らなかったんでbash特有の機能か何かとおも思ったんですが、 これは逆にPOSIX準拠な手法です。

The for Loop (POSIX Shell Command Language)

POSIXのShell Commnad Languageの中のThe for Loopの所に書いてあります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
The for Loop
The for loop shall execute a sequence of commands for each member in a list of items. The for loop requires that the reserved words do and done be used to delimit the sequence of commands.

The format for the for loop is as follows:

for name [ in [word ... ]]
do
    compound-list
done

First, the list of words following in shall be expanded to generate a list of items. Then, the variable name shall be set to each item, in turn, and the compound-list executed each time. If no items result from the expansion, the compound-list shall not be executed. Omitting:

in word...
shall be equivalent to:

in "$@"

The Open Group Base Specifications Issue 7, 2018 edition: Shell Command Language

ということで、ここでの定義ではdoが別行になっている形ですが、 下の方にin word...を省略した場合はin "$@"と同じ、とあります。

ということで

arg4.sh
1
2
3
4
5
6
#!/usr/bin/env bash

for arg
do
  echo "$arg"
done

のように書くのはPOSIX準拠な書き方で、bash以外でもzshでもfishでもdashでも使えます。

;の省略

doが別行にある場合、inがある場合でも最初の行に;は不要です。 ただし同じ行に書く場合にはinがあれば;は必須です。

これがinがない場合では;を省略できます。

省略でできる、というか、もともとPOSIXでも2016年版までは;なしのもののみだったとのこと。 また、Bourne shellでは;なしのみサポートされています。

shell - What is the purpose of the “do” keyword in Bash for loops? - Unix & Linux Stack Exchange

man bash

bashのマニュアルだと

1
2
3
4
5
6
for name [ [ in [ word ... ] ] ; ] do list ; done
   The  list  of  words following in is expanded, generating a list of items.  The variable name is set to
   each element of this list in turn, and list is executed each time.  If the in word is omitted, the  for
   command  executes  list  once  for  each  positional parameter that is set (see PARAMETERS below).  The
   return status is the exit status of the last command that executes.  If the expansion of the items fol‐
   lowing in results in an empty list, no commands are executed, and the return status is 0.

このような記述になっていて、ここにもin wordがオプションとして扱われなければpositional parameterを使うとあります。

(Positional Parameters以外にspecial Parametersという項目があり、こちらは*などのさらに展開されてPositional Parametersとして扱われるものなどがあります。)

その他の方法

上記以外にも

arg5.sh
1
2
3
#!/usr/bin/env bash

for arg; { echo "$arg"; }

というよにdo/doneの代わりに{}で囲う書き方もbashやzshで可能です。

zshであればさらに

arg6.sh
1
2
3
#!/usr/bin/env zsh

for arg { echo "$arg"; }

と、;を抜く事もできます。これはbashではエラーになります。

arg7.sh
1
2
3
4
#!/usr/bin/env bash

for arg
{ echo "$arg"; }

のように行を分ければbashでも;を省略できます。

参考

軽く歴史も含め説明があるQA。

歴史。

Sponsored Links
Sponsored Links

« GNU/BSDでのprintfでの文字列のゼロパディングの違い シェルスクリプトでは`-e`, `-n`の引数は使えない(ということはない) »

}