ファイルまたはstdinからの入力を受け入れるスクリプトを作成する方法は?


57

ファイル名引数またはstdinからの入力を受け入れるスクリプトを作成するにはどうすればよいですか?

たとえば、lessこの方法を使用できます。実行することができless filename、同等にcat filename | less

そうするための簡単な「すぐに使える」方法はありますか?または、ホイールを再発明し、スクリプトに少しロジックを書く必要がありますか?


@PlasmaPower質問がSUで話題になっている限り、別のSEサイトで質問する必要はありません。多くのSEサイトは重複しています。一般に、質問がトピック外(この場合、移行に投票する)またはトピック外であるが応答があまりない(この場合、質問者はモデレーターにフラグを立てる)場合を除き、重複するサイトを提案する必要はありません。注意/移行、クロスポストではありません)。
ボブ

回答:


59

ファイルの引数がスクリプトの最初の引数である場合、引数($1)があり、ファイルであることをテストします。それ以外の場合は、stdinからの入力を読み取ります-

したがって、スクリプトには次のようなものを含めることができます。

#!/bin/bash
[ $# -ge 1 -a -f "$1" ] && input="$1" || input="-"
cat $input

たとえば、次のようにスクリプトを呼び出すことができます

./myscript.sh filename

または

who | ./myscript.sh

スクリプトの説明を編集します。

[ $# -ge 1 -a -f "$1" ]-少なくとも1つのコマンドライン引数($# -ge 1)AND(-a演算子)最初の引数がファイルの場合(-fは「$ 1」がファイルかどうかをテストします)、テスト結果はtrueです。

&&シェルの論理AND演算子です。テストがtrueの場合、ファイルを割り当てinput="$1"cat $input出力します。

||シェルの論理OR演算子です。テストが偽の場合、次のコマンド||が解析されます。入力は「-」に割り当てられます。コマンドcat -はキーボードから読み取ります。

要約すると、スクリプト引数が提供され、それがファイルである場合、変数入力はファイル名に割り当てられます。有効な引数がない場合、catはキーボードから読み取ります。


何をし && input="$1" || input="-" 、なぜそれがtestオペレーターの外にあるのですか?
cmo

私は助けになると思ういくつかの説明と編集を追加しました。
容疑者

スクリプトに複数の引数($@)がある場合はどうなりますか?
g33kz0r

12

read標準入力から読み取ります。ファイル(./script <someinput)またはパイプ(dosomething | ./script)からリダイレクトしても、動作は変わりません。

あなたがしなければならないのは、入力のすべての行をループすることです(そして、それはファイルの行の繰り返しと違いはありません)。

(サンプルコード、1行のみを処理します)

#!/bin/bash

read var
echo $var

標準入力の最初の行(<または|)をエコーします。


ありがとう!私は他の答えを選びました。私は別のスクリプトをラップしていましたが、すべての入力が受信されるまでループしたくありませんでした(多くの入力になる可能性があります...無駄になります)。
ギラッドHOCH

4

使用する予定のシェルについては触れないので、bashを想定しますが、これらはシェル全体でかなり標準的なものです。

ファイル引数

引数には変数を介してアクセスできます$1- $n$0プログラムの実行に使用されるコマンドを返します)。たとえば、catn個のファイルを区切り文字で区切って出力するスクリプトがあるとします。

#!/usr/bin/env bash
#
# Parameters:
#    1:   string delimiter between arguments 2-n
#    2-n: file(s) to cat out
for arg in ${@:2} # $@ is the array of arguments, ${@:2} slices it starting at 2.
do
   cat $arg
   echo $1
done

この場合、ファイル名をcatに渡します。ただし、ファイル内のデータを(明示的に書き込みおよび書き換えせずに)変換する場合は、ファイルの内容を変数に保存することもできます。

file_contents=$(cat $filename)
[...do some stuff...]
echo $file_contents >> $new_filename

stdinから読み取る

stdinから読み取る限り、ほとんどのシェルにはかなり標準的なreadビルトインがありますが、プロンプトの指定方法には(少なくとも)違いがあります。

Bashの組み込みコマンドのmanページはのかなり簡潔な説明がありますreadが、私は好むバッシュハッカーのページを。

単に:

read var_name

複数の変数

複数の変数を設定するには、複数のパラメーター名を指定するだけですread

read var1 var2 var3

read 次に、stdinから1つの単語を各変数に配置し、残りのすべての単語を最後の変数にダンプします。

λ read var1 var2 var3
thing1 thing2 thing3 thing4 thing5
λ echo $var1; echo $var2; echo $var3
thing1
thing2
thing3 thing4 thing5

入力される単語が変数よりも少ない場合、残りの変数は空になります(以前に設定されていても)。

λ read var1 var2 var3
thing1 thing2
λ echo $var1; echo $var2; echo $var3
thing1
thing2
# Empty line

プロンプト

-pはプロンプトに頻繁にフラグを使用します:

read -p "Enter filename: " filename

注:ZSHおよびKSH(およびおそらく他)は、プロンプトに異なる構文を使用します。

read "filename?Enter filename: " # Everything following the '?' is the prompt

デフォルト値

これは、実際にはreadトリックではありませんが、と組み合わせて頻繁に使用しreadます。例えば:

read -p "Y/[N]: " reply
reply=${reply:-N}

基本的に、変数(応答)が存在する場合、それ自体を返しますが、空の場合、次のパラメーター(「N」)を返します。


4

最も簡単な方法は、stdinを自分でリダイレクトすることです。

if [ "$1" ] ; then exec < "$1" ; fi

または、より簡潔な形式を好む場合:

test "$1" && exec < "$1"

これで、スクリプトの残りの部分は標準入力から読み取ることができます。もちろん、ファイル名の位置をとしてハードコーディングするのではなく、より高度なオプション解析で同様に行うことができます"$1"


execここで必要なものではないコマンドとして引数を実行しようとします。
スザナ

@Suzana_K:引数がない場合は、次のようになりません。その場合、子プロセスではなく、シェル自体のファイル記述子を置き換えるだけです。
R ..

if [ "$1" ] ; then exec < "$1" ; fiテストスクリプトをコピーすると、コマンドが不明であるためエラーメッセージが表示されます。簡潔な形式でも同じです。
スザナ

1
@Suzana_K:使用しているシェルは何ですか?それが本当なら、それはPOSIX shコマンド/ Bourneシェルの実用的な実装ではありません。
R ..

Linux Mint Qiana上のGNU bash 4.3.11
スザナ

3

既にこのように動作する他の何かを使用(またはチェーン)し、使用します "$@"

テキスト内のスペースの実行をタブに置き換えるツールを書きたいとしましょう

trこれを行う最も明白な方法ですが、stdin のみを受け入れるため、次のようにチェーンする必要がありcatます。

$ cat entab1.sh
#!/bin/sh

cat "$@"|tr -s ' ' '\t'
$ cat entab1.sh|./entab1.sh
#!/bin/sh

cat     "$@"|tr -s      '       '       '\t'
$ ./entab1.sh entab1.sh
#!/bin/sh

cat     "$@"|tr -s      '       '       '\t'
$ 

使用されているツールがすでにこのように動作している例では、sed代わりにこれを再実装できます。

$ cat entab2.sh
#!/bin/sh

sed -r 's/ +/\t/g' "$@"
$ 

3

次のこともできます。

#!/usr/bin/env bash

# Set variable input_file to either $1 or /dev/stdin, in case $1 is empty
# Note that this assumes that you are expecting the file name to operate on on $1
input_file="${1:-/dev/stdin}"

# You can now use "$input_file" as your file to operate on
cat "$input_file"

bashで、よりきちんとしたパラメータ置換のトリックについては、これを


1
これは素晴らしいです!私は使用uglifyjs < /dev/stdinしていますが、うまく機能します!
-fregante

0

シンプルに保ち、このコードを使用することもできます


このコードを使用してスクリプトファイルpass_it_on.shを作成すると、

#!/bin/bash

cat

走れます

cat SOMEFILE.txt | ./pass_it_on.sh

また、stdinのすべてのコンテンツが画面に出力されるだけです。


または、このコードを使用して、stdinのコピーをファイルに保存してから、画面に出力します。

#!/bin/bash

tmpFile=`mktemp`
cat > $tmpFile
cat $tmpFile    

ここに説明されている別の例があります。

http://mockingeye.com/blog/2013/01/22/reading-everything-stdin-in-a-bash-script/

#!/bin/bash

VALUE=$(cat)

echo "$VALUE"

楽しんで。

RaamEE


0

最も簡単でPOSIX準拠の方法は次のとおりです。

file=${1--}

これはと同等${1:--}です。

その後、通常どおりファイルを読み取ります。

while IFS= read -r line; do
  printf '%s\n' "$line" # Or: env POSIXLY_CORRECT=1 echo "$line"
done < <(cat -- "$file")
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.