イヌツムリのメモ

学習したことのメモである。しかし、他の人が読んでもわかるように書こう。

配列を引数にするより、イテレータを使うほうがbashらしい

はじめに

bashで配列を引数に取るように書こうとすると難しいし、読みにくい。
なのでより簡単でbashらしい書き方を調べたので記録する。

(2021/02/01)追記

結論

結論、下記のようにwhile readすることで、変数名に引数が代入されて処理できる。
疑問: 複数の引数を処理したい場合はどう記述するか? -> やってみた(2021/02/01)

function my_func(){
  while read -r 変数名
  do
    処理
  done
}

パイプでイテレータ処理する

bashらしい関数とは、パイプで渡される引数を逐次処理できる関数である。 配列でまとめて渡されるような関数はbashらしくない。 また、引数をmy_func( args ) のように渡す書き方もbashらしくない。 (そういうのがやりたいなら、pythonいいよ) 同じ処理を複数回実施する場合はパイプで渡して逐次処理するのがbashらしい書き方である。

#!/bin/bash

function one_to_ten(){
    echo 1
    echo 2
    echo 3
    echo 4
    echo 5
    echo 6
    echo 7
    echo 8
    echo 9
    echo 10
}

function add_one(){
    while read -r VAL #注意 変数宣言なのでVALの頭に$をつけないこと。
    do
        echo $(( VAL + 1 )) # expr $VAL + 1するより早いらしい
    done
}

one_to_ten | add_one

ご存知の通り、bashでは関数の戻り値はechoで渡すことができる。 (標準出力が戻り値になる。catでも、grepでもなんでもいい) 今回は例として、one_to_ten関数で1から10の数値をechoする。 (seq 1 10 と同じ。)

逐次処理するミソはパイプを使うことにある。 one_to_tenの出力値をパイプでadd_oneへ渡す。 (引数は標準入力で与えられる。パイプすることでパイプ左の標準出力をパイプ右の標準入力にできる。) add_oneは渡された値に1足してechoする。

パイプを使っていることにより、one_to_tenechoするたびに、 add_oneに値が渡されるため、逐次1加算する処理を実行できる。 (なんてbashらしい処理なんだ!) readを使っていることにより、入力待機が発生して実現できている?要勉強

イテレータともう1つ引数をひとつ取る場合

(2021/02/01)追記

疑問: 複数の引数を処理したい場合はどう記述するか?
ということでやってみた。
引数を増やす場合、参照する位置パラメータを増やせば良い。

疑問:複数のイテレータを扱うには?

#!/bin/bash

function one_to_ten(){
    echo 1
    echo 2
    echo 3
    echo 4
    echo 5
    echo 6
    echo 7
    echo 8
    echo 9
    echo 10
}

function add_x(){
    X=$1
    while read -r VAL 
    do
        echo $(( VAL + X ))
    done
}

one_to_ten | add_one 10

参考

  1. プログラマーの君! 騙されるな! シェルスクリプトはそう書いちゃ駄目だ!! という話 - Qiita
    素晴らしい記事だった。

  2. シェルで変数のインクリメントに expr を使うと100倍遅い件 - Qiita