こちらの記事でサラッと流してましたが、sudo コマンドの中でfor を実行した際エラーに遭遇したのでメモっておきます。
事象
以下は、homeディレクトリ配下の各ユーザーディレクトリ分ループしてssh鍵をコピーするコマンドになります。
普通にカレントシェルでforを実行する分には正常に動くコマンドです。
$ sudo sh -c "for i in `ls -1 /home/`;do cp --parents /home/$i/.ssh/id_rsa . ; done"
sh: -c: 行 1: 予期しないトークン `ec2-user-2' 周辺に構文エラーがあります
sh: -c: 行 1: `ec2-user-2'
$
原因
デバッグのため -xv オプションを付けて実行してみます。
## -v
## Print shell input lines as they are read. (シェルの行を実行時にそのまま表示する
## -x
## Print commands and their arguments as they are executed. (シェルの行を展開した上で表示する
$
$ sudo sh -xvc "for i in `ls -1 /home/`;do cp --parents /home/$i/.ssh/id_rsa . ; done"
for i in ec2-user
ec2-user-2
sh: -c: 行 1: 予期しないトークン `ec2-user-2' 周辺に構文エラーがあります
sh: -c: 行 1: `ec2-user-2'
$
どうやら for の引数となっている ls -1 のコマンドが実行されてしまった上で、その結果がsh -c に渡されているようです。
↑を見る限り、ec2-user のケツで改行されており本来のfor構文であれば改行のあと do が入らなきゃいけない部分に ec2-user-2 という文字列がはいってしまい構文エラーとなっている模様。
対策
sh -c に渡す文字列をダブルクォーテーションではなくシングルクォーテーションで囲みます。
これによりfor部分の変数の展開を防ぐことができ、以下のように意図どおりに動作できました。
$ sudo sh -vxc 'for i in `ls -1 /home/`;do cp --parents /home/$i/.ssh/id_rsa . ; done'
for i in `ls -1 /home/`;do cp --parents /home/$i/.ssh/id_rsa . ; done
ls -1 /home/
++ ls -1 /home/
+ for i in '`ls -1 /home/`'
+ cp --parents /home/ec2-user/.ssh/id_rsa .
+ for i in '`ls -1 /home/`'
+ cp --parents /home/ec2-user-2/.ssh/id_rsa .
+ for i in '`ls -1 /home/`'
+ cp --parents /home/ec2-user-3/.ssh/id_rsa .
$
Tips
突き詰めると、この事象は sudo というより sh -c の挙動になります。
以下の例だとわかりやすいかもしれません。
shに渡す際、囲む文字がダブルクォーテーションであると、$a がまず展開された上で sh に渡されます。 カレントシェルでは $a には何も設定していないので $a の展開結果は空のまま、ただのecho が実行されます。
$ sh -xvc "a=hoge ; echo $a"
a=hoge ; echo
+ a=hoge
+ echo
$
シングルクォーテーションであれば、$a が$a という文字のまま sh -c に渡されるので $aに hoge が設定され、 echo hoge が実行されます。
$ sh -xvc 'a=hoge ; echo $a'
a=hoge ; echo $a
+ a=hoge
+ echo hoge
hoge
$
以上です〜ノシ
参考
(´・ω・`)ゞアリガトゴザイマス.。.・゚
リンク
リンク