LoginSignup
3
4

More than 1 year has passed since last update.

引数と標準入力、曖昧にして使ってない?

Last updated at Posted at 2022-04-28

bashなどを使用する上で
引数と標準入力の使いわけ意識してますか?

echoやcatで渡しているのは、
引数?標準入力?

ここを理解して使っていくことで
幅が広がること間違いなしでしょう(受け売り)

【基礎】引数と標準入力

まずは単純なパターンから
catでファイル開く場合
引数で渡しても、標準入力で渡しても表示されます。

$ ls
a.txt  b.txt
# 引数
$ cat a.txt
aaaaa
# 標準入力
$ cat < a.txt
aaaaa

引数と標準入力のどちらかしか受け付けないコマンド

echoは引数しか受け付けない

よくある例として
echoは引数しか受け付けません。

$man echo
...
DESCRIPTION
       Echo the STRING(s) to standard output.
# 引数で渡すと成功
$ echo "aaa"
aaa
## 標準入力で渡すとエラー
$ echo < "aaa"
-bash: aaa: そのようなファイルやディレクトリはありません

catは引数があればファイルを引数として受け取り、引数がなければ標準入力を受け取る

catは、引数も標準入力も
ファイルを受け付けるコマンドです。

$man cat
...
DESCRIPTION
       Concatenate FILE(s), or standard input, to standard output.
# ファイルの中身が連結され表示
$ cat a.txt b.txt
aaaaa
bbbbbb

# 標準入力で渡すと、複数を渡すことができない
$ cat <a.txt <b.txt
bbbbbb

【問題】 ファイルの中身を連結して出力せよ

Q. 下記のファイルの中身を連結して出力せよ

$ ls
a.txt  b.txt

lsやcatを使用すれば実現できそうですね

下記のような場合はどのような出力になるのでしょうか

$ ls | cat 

a.txtとb.txtのそ各ファイルの中身がcatで連結されればいいなぁ
と思いますが、答えは...

#ファイル名が出力されてしまいます。
$ ls | cat 
a.txt
b.txt

詳しく見ていくと

  • パイプは標準入力として、次のコマンドにわたす
  • lsの結果は標準入力として、まとめてcatにわたす
  • catにわたされるのは文字列

このため
ファイルは連結されず、lsの結果のみが表示されてしまいました。

覚えておきたい表現

ここでbashで使える表現について覚えておきましょう。

-(ハイフン) : どこで標準入力を渡すのかを指定

パイプなどで渡された標準入力を
どこで使用するかを、指定できるのが ー(ハイフン)です。
コマンドにより、対応しているものと対応していないものがあります。

例えば
下記では渡しているb.txtを
a.txtの前に読み込むのか後に読み込むのかを指定することで
catの結果が変わっています。

$ cat b.txt |cat - a.txt
bbbbbb
aaaaa

#先程の結果と逆に。
$ cat b.txt |cat a.txt -
aaaaa
bbbbbb

$() : コマンド置換

文字列で渡されたコマンドを
コマンドとして実行させたい場合はコマンド置換$()を使用します。

バッククォートでも可能です。
よく使うやつですね。可読性が下がるので$()の方がおすすめです。

$ echo "ファイルは、$(ls)です"
ファイルは、a.txt
b.txtです

$ echo "ファイルは、`ls`です"
ファイルは、a.txt
b.txtです

問題の回答

ここまでわかれば、
lsの結果をcatでくっつけることも可能です。

echo ls | $(cat -)

lsした結果を、標準入力としてcatにわたし、
それをコマンドとして実行すれば、
それぞれのファイルの中身を連結してくれるはず!

$ echo ls | $(cat -)
a.txt  b.txt

....あらららーー?
またしても、lsの結果だけ表示されてますね。

この問題は、catにわたるのはまとまった標準入力であり
catは、複数のファイルを渡す場合は、引数として
わたさなければならない点にあります。

引数しか受け付けないコマンドに、標準入力をわたす

上記のcatのように、引数としてわたしたい場合
多くのパターンがありますがよく使うのは

  • xargs
  • sed  

xargs : 一行ずつ読み込んで実行する

渡された標準入力やファイルに対して
一行ずつコマンドを実行するコマンドです。

オプションが多いのですが最低限覚えておきたいのは

-I :後ろの文字列に標準入力を入れる+標準入力を一行ずつ処理+1プロセスずつ実行
-a :ファイルを読み込む場合必要
-P :同時プロセス数の上限。デフォルトは1
-t :実行するコマンドを標準出力する

$ ls | xargs -IXXX sh -c 'cat XXX'
aaaaa
bbbbbb

lsで渡された結果を標準入力として1行ずつ読み込み
I以下のXXX部分で読み込み、
shで実行しています。

無事、望み通りのものが出てきましたね。

sed : 置換だけではなく、コマンド実行にも使える

よく文字置換として使用されるsedですが
eオプションを使うことで一行ずつ実行することができます。

$ls | sed 's/^/cat /e'
aaaaa
bbbbbb

#これでもいけます
$ls | sed 's/^/cat /' |bash

こちらも望み通りの結果が出てきています。
改行が気になる場合は、sedなどで消せば良いでしょう。

まとめ

  • 引数や標準入力に対応しているコマンドかはmanを見れば大抵わかる
  • どこで標準入力を渡すのかを指定する-(ハイフン)
  • 文字列をコマンドとして実行させる$()(コマンド置換)
  • 引数のみ受け付けるコマンドに、標準入力を渡して実行させるならxargsやsed

この辺りを使用することで、ログ解析からの動作が非常に簡単になります。
身につけておきましょう。

awkでログの特定の列だけを抜き出してコマンド実行なども
汎用性の高い組み合わせです。

ログのIPアドレスからsshで接続し、コマンドを実行。

$ cat instance.txt
api001a.ad-service    i-XXXXXX      10.3.50.128
automation-api001a.service    i-YYYYYY      10.3.59.51  
automation-api002c.service    i-ZZZZZZ      10.3.122.207
$ awk '{print $3}' instance.txt| xargs -IXXX -P 10 sh -c 'ssh -o StrictHostKeyChecking=no XXX "pwd"'
/home/developer
/home/developer
/home/developer

これを利用して
実行したいコマンドファイルをあらかじめ作成し、ssh先で実行させたい場合も使えます。

この時、コマンドファイルを

  • 引数として渡すなら、コマンドファイルがssh先にある必要がある
  • 標準入力として渡すなら、コマンドファイルがローカルにある必要がある

などが変わるため、注意が必要です。

# 引数としてcommand.txtを読み込む。command.txtはssh先にある必要がある
awk '{print $3}' instance.txt| xargs -IXXX -P 10 sh -c 'ssh -o StrictHostKeyChecking=no XXX "sh command.txt" > output.log'

# 標準入力としてcommand.txtを読み込む。command.txtはローカルにある必要がある
awk '{print $3}' instance.txt| xargs -IXXX -P 10 sh -c 'ssh -o StrictHostKeyChecking=no XXX < command.txt > output.log'

ここまでできるようになると
一気にできることの幅が広がる気がしますね。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4