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

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

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

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

シェルスクリプト

シェルスクリプトは、UNIX系のOSもしくはコマンドラインインタプリタ向けに記述されたスクリプト。bash/zshといったシェルによって実行されるため、このように呼ばれています。バッチ処理などに使用されており、テキストファイルに書かれた命令を順に実行します。

Q&A

2回答

3098閲覧

シェルスクリプトで別のシェルスクリプトを呼び出したとき、標準入力の有無を判定したい

s-o

総合スコア6

bash

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

シェルスクリプト

シェルスクリプトは、UNIX系のOSもしくはコマンドラインインタプリタ向けに記述されたスクリプト。bash/zshといったシェルによって実行されるため、このように呼ばれています。バッチ処理などに使用されており、テキストファイルに書かれた命令を順に実行します。

0グッド

0クリップ

投稿2021/09/13 03:34

編集2021/09/13 04:11

お世話になっております。
シェルスクリプト(A.sh)から別のシェルスクリプト(B.sh)を呼び出した場合の標準入力の処理についてです。

問題点

A.shはコマンドライン引数が必要で、 $ A.sh stringのように使います。
B.shはコマンドライン引数やオプションを必要し、またtestコマンドで標準入力がパイプかどうかを判定し処理内容を変更しています。

B.sh単体での実行は問題ありませんが、A.shから呼び出す場合、B.sh内でパイプと判定され、正しく処理できません。

こうしたい

B.sh単体およびA.shからB.shを呼び出した場合でも正しく標準入力を受け取りたい。

スクリプト

B.shは以下のように使います。

console

1# 入力ファイルを用意する場合 2$ B.sh -p P -t T in.txt > out.txt 3 4# 標準入力で処理する場合 5$ cat in.txt | B.sh -p P -t T - > out.txt

B.shは以下のように[ -p /dev/stdin ]で場合分けしています。

Bash

1#!/usr/bin/env bash 2if [ -p /dev/stdin ]; then 3 data=$( cat - ) 4else 5 data=$( cat ${infile} ) 6fi

A.shは以下のように使います。なお、実際はA.shも別のプログラム (perl)からsystemを用いて呼び出しています

console

1$ A.sh aaa.txt

A.shは以下のようなスクリプトです

Bash

1bash B.sh -p $arg_P -t $arg_T ${infile} > ${outfile}

この場合に、A.shを実行するとB.shで標準入力がパイプと判定されてしまいます。

質問が拙くて申し訳なく思います。
ご質問やご不明点がございましたらコメントください。よろしくお願いします。

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

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

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

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

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

itagagaki

2021/09/13 03:50 編集

ちょっと標準入力がある/ないという記述が曖昧でよくわかりません。 (標準入力が「ない」ということはないです)。 標準入力がパイプでない(端末かファイルか何か)なら -p /dev/stdin は偽になりますよね。 それが真になるということですか?
s-o

2021/09/13 04:08

URLありがとうございます。「標準入力」の意味を間違えていました。 後ほどURLの内容は色々と試したいと思います。解決するかもしれません。 理解不足で端末の意味がいまいちわかりませんが (キーボード入力でしょうか?)、 B.shでは、標準入力がパイプなのかもしくはコマンドライン引数のファイルなのかを判定したいです。 A.shから上記のようにB.shを呼び出すと、B.shは標準入力がパイプであるにも関わらず、-p /dev/stdinが真になります
guest

回答2

0

B.shは以下のように[ -p /dev/stdin ]で場合分けしています。

端的に言うと、この部分の設計ミスと思います。
引数で入力ファイルを指定しているのに、標準入力の状況に応じて、その指定した入力ファイルを無視させていることになるからです。

標準入力というのは、「コマンド起動側がどんなファイルに繋ぐかを調整し」「調整しない場合は上位(起動元)の接続状況を継承する」という特性を持っています。はっきり言うと、呼び出されるコマンド側で状況を判断するのには向いていません。
※「常に標準入力から読み込むけど、接続先が端末デバイスかどうかを判断する」というケースはある。

なので、cat 等の一般的なコマンドでも「ファイル名が指定されたらファイルをオープンしてそこからデータを読み込む、ファイル名無し、あるいは - の指定なら標準入力から読み込む」という設計になっています。「標準入力がパイプに接続されていれば…」とするのは、あまり筋の良い方法とは言えません。
ということで、一番手っ取り早い解決策は、cat 等のやり方を真似ることです。

それでも[ -p /dev/stdin ] の判断を優先したいのであれば、「入力ファイルを指定する場合は、コマンド起動側で、明示的に標準入力を繋ぎかえることを前提とする」しかないでしょう。
つまり、
bash B.sh -p $arg_P -t $arg_T ${infile} > ${outfile}
ではなく
bash B.sh -p $arg_P -t $arg_T ${infile} > ${outfile} < /dev/null
として、起動元の標準入力の接続状況がどうであっても、パイプ以外への接続であることを保証するわけです。
なお、infile に - をセットして「起動元の標準入力の接続先から読ませる」ということはできなくなりますので、その点はご注意を。

投稿2021/09/13 07:49

angel_p_57

総合スコア1681

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

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

0

ハイフン( - )を使って標準入力かどうか切り替えたいのであれば例えばこんな方法もあります。

bash

1#!/bin/bash -ue 2 3while getopts "ab" OPT 4do 5 case $OPT in 6 a) echo "getopts: -a" 7 ;; 8 b) echo "getopts: -b" 9 ;; 10 esac 11done 12 13shift $(($OPTIND - 1)) 14 15for argv in $@ 16do 17 echo "argv: ${argv}" 18 if [ ${argv} = "-" ]; then 19 echo "Read data from stdin..." 20 cat - 21 fi 22done 23 24

実行例はこんな感じ。 - が現れた箇所以降は getopts で値が取得できていないことに注意してください。

txt

1$ echo hoge | bash example.sh -b - -a 2getopts: -b 3argv: - 4Read data from stdin... 5hoge 6argv: -a

投稿2021/09/13 04:12

mather

総合スコア6759

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問