Help us understand the problem. What is going on with this article?

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

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シェル互換です'

TS; DR

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

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

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

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

setup.shをローカルで実行
$ ./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 は「少なくとも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
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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
ユーザーは見つかりませんでした