そのshの正体は
busyboxのshはashで配列が使えないという話です。
業務でシェル芸的ワンライナーを作ることが多くなってきてたのですが、どハマリしたのがshがbashではない環境。
具体的にはESXiのデフォルトシェルはどうやらbusyboxのashらしく、配列が使えないという話。
■そのシェルなんでしょう
今使ってるシェル、というかshで呼び出されるシェルはwhichとlsで分かるようです。
・Archの場合
$ which sh /usr/bin/sh $ ls -l /usr/bin/sh lrwxrwxrwx 1 root root 4 6月 4 17:54 /usr/bin/sh -> bash $ ls -l /usr/bin/bash -rwxr-xr-x 1 root root 866600 6月 4 17:54 /usr/bin/bash
shは/usr/bin/shで、この実体はbashへのシンボリックリンクになってます。
念の為/user/bin/bashをls -lしてみると、こちらは実行権限付きのファイルになってるので、これが実体。
・Debian(9.5)の場合
$ which sh /bin/sh $ ls -l /bin/sh lrwxrwxrwx 1 root root 4 1月 24 2017 /bin/sh -> dash $ which sh /bin/sh $ ls -l /bin/dash -rwxr-xr-x 1 root root 117208 1月 24 2017 /bin/dash
同じように調べてみると、shは/bin/shを呼び出して、/bin/shはdashへのシンボリックリンクになっています。
念の為/bin/bashをls -l してみると、こちらが実行権限付きのファイルになっています。
ちなみにDebianでもbashは入ってるので、明確にbashを指定すればbashでしか通じない文法のシェルでも問題なく動くでしょう。
$ ls -l /bin/dash -rwxr-xr-x 1 root root 117208 1月 24 2017 /bin/dash
実はシェルは他にもいろいろあるので、気になる人はこの辺見てください。(ちょっと情報古いけど)
シェル
■実は配列が使えないシェルがある
これがドハマったところでした。
実はシェルの配列ってbashの機能だったんですね。
具体的にはESXiのコマンドラインで、コマンド結果を配列に突っ込んで、順に取り出しながら代入してうんたらかんたら、みたいなことをワンライナーで書いてたときにそれは起こりました。
sh: syntax error: unexpected "("
シンタックスエラー??
書き方が悪いと思って調べながら何度修正してもだめ。というか、以前にRedHatLinux上で動かしたワンライナー配列のコマンドをもとにして書いてたので、本当に迷宮入りしそうでした。
が、実はESXiのシェルって/bin/busybox ashが実体だったらしいです。(busyboxのデフォルトシェルはashらしい。)
dash,bash,busyboxすべて入っているDebianで試してみましょう。
ashに変更
tmin@tminserver:~$ /bin/busybox ash BusyBox v1.22.1 (Debian 1:1.22.0-19+b3) built-in shell (ash) Enter 'help' for a list of built-in commands. ~ $
さてこれで配列が使えるかというと
~ $ array=(a b c d) ; for i in ${array[@]} ; do echo $i ;done sh: syntax error: unexpected "("
だめです。
bashに戻して配列を使うワンライナーを流す。配列をa b c d として、一個ずつ取り出してechoするだけのワンライナー。
~ $ /bin/bash tmin@tminserver:~$ array=(a b c d) ; for i in ${array[@]} ; do echo $i ;done a b c d
これが想定の動きです。
dashならどうか?どうやらdashはashがベースらしいですが。
tmin@tminserver:~$ /bin/dash $ array=(a b c d) ; for i in ${array[@]} ; do echo $i ;done /bin/dash: 1: Syntax error: "(" unexpected
だめですね。
つまり#!/bin/sh ではなく#!/bin/bash って書いとけばシェルなら大丈夫。ワンライナーなら確実にbashに切り替えてから実行すればおk。
でもDebianと違って、ESXiのデフォルトではbashがないのですよ!!
■解決法
1.配列+forによる繰り返しをやめてwhileでなんとかする。
何も根本的解決になってないですけど。コマンド結果を配列に入れて、一個ずつ取り出して代入してうんたらかんたら、っていうくらいのことならwhile使えばいいよ。
※問題点
複数の配列を使いたかった処理の場合これでは目的を達成できない。
達成しようとした場合、一撃で終わらせたかった処理が2撃以上になるか、シェルが冗長になるかどちらか。
とはいえ、今回自分がハマっていた処理についてはこれで行けた。(lsの結果を配列に突っ込んでうんたらかんたら、みたいなのだったと記憶している)
2.set -- $arrayを使う
https://stackoverflow.com/questions/26091758/arrays-in-shell-script-not-bashstackoverflow.com
さすがStack Over Flow。
$ array="0 1 2 3" ; set -- $array ; for i in $array ; do echo $i ;done 0 1 2 3 ~ $
でも複数配列うまく使うのはまた難しそう。