質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
bash

bash(Bourne-again-Shell)は sh(Bourne Shell)のインプリメンテーションに様々な機能が追加されたシェルです。LinuxやMac OS XではBashはデフォルトで導入されています。

cURL

cURLはHTTP, FTPやTelnetなど複数のプロトコルを用いてデータを転送するライブラリとコマンドラインツールを提供します。

Q&A

解決済

3回答

1196閲覧

シェルスクリプトで標準入力の有無で処理を変えたい

rgb

総合スコア9

bash

bash(Bourne-again-Shell)は sh(Bourne Shell)のインプリメンテーションに様々な機能が追加されたシェルです。LinuxやMac OS XではBashはデフォルトで導入されています。

cURL

cURLはHTTP, FTPやTelnetなど複数のプロトコルを用いてデータを転送するライブラリとコマンドラインツールを提供します。

0グッド

0クリップ

投稿2022/11/10 01:43

編集2022/11/15 16:35

やりたい事

curlのラッパーを作成したいで。(都度生成したヘッダを追加する)

コード

デバッグの為、curlでなくechoにしてます

#!/bin/bash if [ -p /dev/stdin ];then echo "=pipe=" stdin=$(cat -) echo "${stdin}" #echo "${stdin}" | curl -H "foo:bar" $@ else echo "=not pipe"= #curl -H "foo:bar" $@ fi

【挙動確認】

$ ./test1.sh =not pipe= $ echo foo | ./test1.sh =pipe= foo

問題点

しかしこれだと、標準入力を用いたwhileループ内で使った時に
意図せず標準入力を全て吸い出してしまう事がわかりました。

【期待した出力】

$ seq 3 | while read c;do ./curl.sh ;done =not pipe= =not pipe= =not pipe=

→3回実行して欲しい

【実際の出力】

$ seq 3 | while read c;do ./curl.sh ;done =pipe= 2 3

→1回しか実行されていない。
→curl.sh内で標準出力の残り全部が読み込まれている

最終的にcurlコマンドと同じ使い方が出来る様にしたいです。
(運用回避はなし。自分以外も使うので、細かい運用ルールは守られない可能性があるし、事故になる)

aliasでcurlにヘッダを付ける事も考えましたが、
準備が必要になるし、できれば1スクリプトで完結させたいです。

これについて、解決法があれば教えてください。

補足

コメントアウトを外した上で下記が通常のcurlと同じ挙動をするようにしたいです。

パイプなし

$ curl.sh example.com/1/

パイプ有り

$ echo '{ "foo":"bar" }' | curl.sh -X POST -d @- example.com/1/

直接のパイプはないが標準入力がパイプ

$ seq 3 | while read c;do ./curl.sh example.com/$c/ ;done

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

otn

2022/11/10 02:21

while read c; の read c の意図が不明です。何のためにここで入力を読み捨てているのでしょう?
otn

2022/11/10 02:23

期待した出力を見ると=not pipe=しか書いてないのですが、 echo "${stdin}" の出力は不要、つまり、$(cat -) のcatでは何も読みたくないと言うこと?であれば書かなければ良いのでは?
rgb

2022/11/10 04:11

コードはデバッグ用にechoを入れているだけで、 実際にはコメントアウトしているcurlを実行したいです。 not pipeの出力はif文の分岐確認用です。 パイプに繋がれた時だけ、curlに標準入力を渡したいです。
guest

回答3

0

自己解決

https://teratail.com/questions/359213
過去のログに類似のケースが有りました。
結論としては、設計が間違えてるという回答でした。

またcatのやり方(引数のハイフンの有無で判断)を真似ろという事だったので、
今回はcurlのソースを確認しましたが、こちらもオプションを見てstdinを読みはじめる仕組みだったので、
パイプの有無ではなく引数で判断する形に変更しました。
(「@-」がある時だけstdinを見る)

自分の要件が不明瞭で失礼しました。
回答してくれた方、ありがとうございました。

投稿2022/11/10 06:15

rgb

総合スコア9

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

otn

2022/11/10 11:52

私の回答で期待通りになるで、別にそれでもいいのでは?
rgb

2022/11/10 13:11

変な締め方になりすみません。 理由については頂いた回答にレスの形で記載しました。
otn

2022/11/11 04:40 編集

「標準入力がパイプであるか」を判断するのは奇異で、なんか間違えてるというのはその通りです。 コマンドの標準入力は、普通のパターンとしては、 1.端末 2.ファイルリダイレクト 3.パイプ の3通りあります。「端末かどうか」を判断して動作を変えるコマンドは標準コマンドを始め沢山あります。 つまり、「1 と 2or3で動作を変える」は普通で、「1or2 と 3」で動作を変えるのが順当というケースはすぐには思いつきません。「1or3 と 2」の区別はある。 この問題のケースも「1 と 2or3で動作を変える」が普通でしょう。 まあ、「ファイルリダイレクトで実行することが絶対無い」のであれば同じですが。
guest

0

まぁ、【期待した出力】を得るだけであれば stdin を close すればよいかと思いますが、何の意味(意義)があるのか判然としませんね。。。

bash

1$ seq 3 | while read c;do ./test1.sh <&-;done 2=not pipe= 3=not pipe= 4=not pipe=

投稿2022/11/10 02:35

melian

総合スコア19618

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rgb

2022/11/10 04:30

回答ありがとうございます。 curlのラッパーとして使いたいので、実際のコードとしてはコメントアウトを外した上で seq 3 | while read c;do ./test1.sh http://example.com/$c/ ;done で実行したいです。 curlのラッパーなので、標準入力を閉じるとかcurlで不要な事はしないで動かしたいです。
guest

0

いまいち、ポイントが不明ですが(while read c;read cの意図が不明)、
ファイル全体でなく、1行だけ読みたいということであれば、
catでなくhead -1を使うか、readコマンドを使うか。つまり、stdin=$(cat -)でなくread stdin

追記

もしかして、

Bash

1for i in $(seq 3); do ./curl.sh example.com/$c/; done

のようなことが本当にしたいことでしょうか?

あるいは、

Bash

1seq 3 | while read c;do ./curl.sh example.com/$c/ </dev/null ;done 23seq 3 | while read c;do ./curl.sh example.com/$c/ </dev/tty ;done 45seq 3 | xargs -i sh -c './curl.sh example.com/{}/' 6か?

投稿2022/11/10 02:24

編集2022/11/14 09:47
otn

総合スコア84423

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rgb

2022/11/10 13:04

回答ありがとうございます。 コメントが遅れすみません。 スマホからなので、一度にたくさんの入力ができず。 no pipeの場合、標準入力は無いという意味なので、出力は何もないのが正しいです。 あとreadを使うと複数行を読み取れなくなるので、改行のあるjsonをpostしたい時などに失敗してしまいます。 curlと同じ挙動をするのが目的なので、課題は解決できませんでした。
otn

2022/11/10 14:48

3行の入力に対して3回curlを起動したいと書いてあったので、そういうコードにしたのですが、違うのでしょうか? JSONの区切りを認識して、何行であろうが、3つのJSONがあれば1つずつを入力に3回curlを起動し、 10個のJSONがあれば1つずつを入力に10回curlを起動し、ということでしょうか? そうであれば、質問文とすごくかけ離れた内容になりますね。 自分の問題がなんなのか認識されていないようですが、パイプかどうかなどは末節な問題で、 標準入力をどう分割してcurlに与えるのか?というのがあなたの抱えている問題です。
rgb

2022/11/11 04:27

直接スクリプトを叩いた時は標準入力無しでcurlを実行し(no pipe) パイプ経由で叩いた時は標準を渡してcurlを実行したい(pipe) のが目的です。 挙動確認の項で書いた感じです。 パイプ経由のwhile内だと、直接叩いてるつもりなのに(no pipeを出したいのに)、 意図してない動作(pipeを出す)をするから、 他に判断する方法は無いか?という質問でした。 回答して頂いた事は感謝ですし、 こちらの質問に曖昧な点があったのも事実で、 反論したいわけじゃなく、 誠意的なレスポンスをしたい、という意図でコメントさせていただきました。
otn

2022/11/11 14:38

もしかして、 for i in $(seq 3); do ./curl.sh example.com/$c/; done のようなことが本当にしたいことでしょうか? あるいは、 seq 3 | while read c;do ./curl.sh example.com/$c/ </dev/null ;done か seq 3 | while read c;do ./curl.sh example.com/$c/ </dev/tty ;done か seq 3 | xargs -i sh -c './curl.sh example.com/{}/' か?
rgb

2022/11/15 00:49

全部対応したかったです。 curlコマンドにヘッダを付けるラッパーを作って、curl代替として使うつもりでした。 なのでどのような呼び出し方でも(オプションなども含め)curlコマンドと同じ挙動をする事を要件としてました。 なので「呼び出し方を工夫すれば大丈夫」「この使い方しなければ大丈夫」みたいな解決法は選択肢に入れておらず、 (自分以外に使われる事を考えると、フールプルーフが担保できない) そのため「パイプかどうかの判断」にフォーカスして質問せてもらいました。 結果、見当違いだったという事で混乱を招きましたが。。。
otn

2022/11/15 07:35

依然として何が希望なのか不明です。 「特定ヘッダを追加したい」「どのような呼び出し方でも(オプションなども含め)curlコマンドと同じ挙動をする」という2点だけが要件であれば、 #!/bin/sh curl -H "foo:bar" "$@" の1行だけで問題ないわけですが、「パイプかどうか?」とかいう話はどういう要件から出てきた話でしょうか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問