インタラクティブな(対話型の)シェルスクリプトを、パイプで
bash
orsh
シェルに渡してユーザに操作させたい。
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)
#!/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つ以上の必須項目が足りないために発生するエラーです。リモートファイル(ダウンロードしてきたファイル)をプロセス置換によりローカルファイルのように実行します。
- 参考文献:「Execute Bash script remotely via cURL」@ StackOverflow
TS; DR
一般的に、下記フォーマットでコマンドを打つと curl
でダウンロードしたスクリプトを bash
コマンドにパイプで渡し、実行できます。
curl -s https://yourdomain.com/your_app/setup.sh | bash
つまり、ネットに公開されているスクリプトをダウンロードしつつシェルのスクリプトを実行する定番のパターンです。セットアップやインストールなどのスクリプトに利用している人も多いのではないでしょうか。
問題は以下のような、入力を求められる対話式のスクリプトの場合です。
$ # 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
文などで比較する際に「片方の型が合っていない」もしくは「未定義だった」場合などです。
- Bash script error [: !=: unary operator expected] @ StackOverflow
- Getting unary operator error on Shell script Linux @ StackOverflow
今回の件は、スクリプト内の read
コマンドで標準入力から yes
や no
などのデータを受け取らずに処理を続行しているため発生しています。
そうなると回避方法として以下の方法が考えられます。
- 値がない場合のデフォルトを決める
- yesコマンドを使う
- 何かしらの方法で
yes
やno
を渡す
ネットには「yes
コマンドで yes | ./script
でパイプ渡しにしろ」とあるのですが、うまく動きませんでした。
- How do I script a “yes” response for installing programs? @ StackOverflow
そもそも、yes
か no
かが決め打ちになってしまうため、スクリプトによってはユーザーに選択させたいのです。
🐒 ユーザーに選択させず、決め打ちで実行したい場合は以下の記事が参考になります。
- rustup を非対話的環境でインストールする方法 @ Qiita
すると、StackOverflow に「curl ... | bash
の形式だと read
コマンドは stdin
からは拾えないよん」との情報が。
With the
curl ... | bash
form, bash's stdin is reading the script, so stdin is not available for theread
command.
- Execute Bash script remotely via cURL @ StackOverflow
そして丁寧にも解決法まで載せてくれていました。プロセス置換(Process Substitution)と言うのだそうです。
Try using a Process Substitution to invoke the remote script like a local file:
bash <( curl -s ... )
- Execute Bash script remotely via cURL | comment @ StackOverflow
動いた!バッチリ