15
Help us understand the problem. What are the problem?

posted at

updated at

Organization

cURL でダウンロードした対話式シェルスクリプトが bash にパイプ渡しできない

インタラクティブな(対話型の)シェルスクリプトを、パイプで bash or sh シェルに渡してユーザに操作させたい。

yes/no といった入力を求められるスクリプトをダウンロードして無名パイプ|)で bash に渡すと、「unary operator expected」と叱られ対話できないなり。

ダウンロード&実行で失敗する
$ curl -s https://yourdomain.com/your_app/setup.sh | bash
Would you like to set up your_app now? (y/n):bash: line 10: [: =: unary operator expected

「curl ダウンロード 対話 bash スクリプト パイプ渡しできない」とググってもタイトルからわかる記事がなかったので、自分のググラビリティとして。

TL; DR (対話的シェルスクリプトのダウンロード実行)

プロセス置換の機能(Process Substitution)を使ってみる。

bash <(curl -s https://yourdomain.com/your_app/setup.sh)
応用技(bashシェルスクリプト内でshシェルにスクリプトを渡して実行する)
#!/bin/bash

echo -n '- シェル互換のテスト ...'
/bin/sh <(cat ./sample.sh) 2>/dev/null 1>/dev/null
if [ $? -ne 0 ]; then
  echo 'NG. shシェルと互換がありません'
  exit 1
fi
echo 'OK. shシェル互換です'

unary operator expected」エラーは、ユーザーから期待する少なくとも1つ以上の必須項目が足りないために発生するエラーです。リモートファイル(ダウンロードしてきたファイル)をプロセス置換によりローカルファイルのように実行します。

TS; DR

一般的に、下記フォーマットでコマンドを打つと curl でダウンロードしたスクリプトを bash コマンドにパイプで渡し、実行できます。

curl -s https://yourdomain.com/your_app/setup.sh | bash

つまり、ネットに公開されているスクリプトをダウンロードしつつシェルのスクリプトを実行する定番のパターンです。セットアップやインストールなどのスクリプトに利用している人も多いのではないでしょうか。

問題は以下のような、入力を求められる対話式のスクリプトの場合です。

setup.shをローカルで実行
$ # yes が入力値
$ ./setup.sh
Would you like to set up your_app now? (y/n): yes
...
your_app installed! Thank you!

上記を同じように、ダウンロード&実行すると、yes もしくは no を渡すことができずエラーが発生します。

$ curl -s https://yourdomain.com/your_app/setup.sh | bash
Would you like to set up your_app now? (y/n):bash: line 10: [: =: unary operator expected

[: =: unary operator expected がエラー内容で、スクリプトの 10 行目に問題があるのがわかります。

unary とは何なり

調べてみたところ、unary はラテン語の una uno などの「1つ」から来ており、「単項の」「1変数の」と言った意味があり、ここでの operator は「演算子」という意味です。

つまり unary operator とは「単項演算子」で、入力を1つ受け付けるタイプの演算子ということです。

そして、expected は「期待していた」という意味なので、何かしらの単項演算子にデータを「渡さないといけない」、もしくは「受け取らないといけない」のに「見当たらないやんけー Σ 👋 bash」っと言われているのです。

どうやら総合的に unary operator expected とエラーが出た場合は「少なくとも1つの項目が必要」という意味に捉えてよさそうです。

このエラーメッセージが出る、よくあるケースが if 文などで比較する際に「片方の型が合っていない」もしくは「未定義だった」場合などです。

今回の件は、スクリプト内の read コマンドで標準入力から yesno などのデータを受け取らずに処理を続行しているため発生しています。

そうなると回避方法として以下の方法が考えられます。

  1. 値がない場合のデフォルトを決める
  2. yesコマンドを使う
  3. 何かしらの方法で yesno を渡す

ネットには「yesコマンドyes | ./script でパイプ渡しにしろ」とあるのですが、うまく動きませんでした。

そもそも、yesno かが決め打ちになってしまうため、スクリプトによってはユーザーに選択させたいのです。

🐒   ユーザーに選択させず、決め打ちで実行したい場合は以下の記事が参考になります。

すると、StackOverflow に「curl ... | bash の形式だと read コマンドは stdin からは拾えないよん」との情報が。

With the curl ... | bash form, bash's stdin is reading the script, so stdin is not available for the read command.

そして丁寧にも解決法まで載せてくれていました。プロセス置換(Process Substitution)と言うのだそうです。

Try using a Process Substitution to invoke the remote script like a local file:

bash <( curl -s ... )

動いた!バッチリ :thumbsup: :pray:

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
15
Help us understand the problem. What are the problem?