繰り返しと関数
目標
繰り返しと関数を、実際に使える形で身につけます。特に、「一致するファイルがないとき」、「引数に空白があるとき」、「引数がまったくないとき」のような境界の状況で、壊れないように作ります。
なぜ重要なのか
シェル関数は値を返しません。計算した値は標準出力に出し、成功と失敗は終了コードで知らせます。この規約を守ってこそ、if is_number "$x"のような自然な使い方ができます。そして、ファイルの一覧はlsをパースせず、シェルのグロブに任せます。$(ls *.log)は空白を含むファイル名で壊れ、一致がないときにエラーメッセージをループの入力として流し込みます。代わりにグロブを使うと、一致がないときにパターン文字列がそのまま残るという別の落とし穴が生まれるので、[ -f "$f" ] || continueという防御線が必要です。
ステップ
/root/bin/loop1.shを作り、1から5まで1行に1つずつ出力させてください。/root/loops/logsディレクトリを作り、その中に.logファイルを4つ、.txtファイルを2つ置いてください。そして/root/bin/count.sh <디렉터리>を作り、そのディレクトリの直下の.logファイルの個数だけを出力させてください(プレースホルダーはディレクトリです)。空のディレクトリを渡すと0が出る必要があります。/root/loops/users.csvに이름,역할の形式で3行以上入れ、/root/bin/readcsv.shを作って、そのファイルを読んで이름(역할)の形式で1行ずつ出力させてください(引数なしで実行します。2つの形式の韓国語は、それぞれ「名前」と「役割」を意味する語です)。/root/bin/lib.shにslugify関数を定義してください。単語を小文字に変えて、ハイフンでつなぎます。/root/bin/use.shは、そのファイルをsourceで読み込んで使う必要があります。use.sh Hello Worldの出力はhello-worldです。/root/bin/isnum.sh <문자열>を作ってください。数字だけでできていれば終了コード0、そうでなければ0以外のコードです。何も出力してはいけません。空文字列、符号、小数点は、すべて数字ではありません(プレースホルダーは文字列です)。/root/bin/each.shを作り、受け取った引数ごとに[인자]の形式で1行ずつ出力させてください。each.sh "a b" cは2行になる必要があります。引数がなければ何も出力しません(プレースホルダーは引数です)。/root/bin/sum.shを作り、受け取った数値の引数の合計を出力させてください。引数がなければ0です。/root/bin/summary.sh <디렉터리>を作ってください。そのディレクトリの.logファイルの個数と全体の行数を、次の2行で出力します(プレースホルダーは、ディレクトリ、個数、合計です)。files=<개수>total_lines=<합계>
参考
. /root/bin/lib.shとsource /root/bin/lib.shは同じコマンドです。新しいプロセスを作らないので、関数定義が残ります。wc -l < 파일は、ファイル名を出力に付けないので、計算に使いやすいです(プレースホルダーはファイル名です)。- よくある間違い1: ステップ2とステップ8で空のディレクトリを入れると、グロブが展開されずにパターン文字列がそのまま残り、個数が1になります。
- よくある間違い2: ステップ6で
for x in $*を使うと、"a b"が2つに分割されます。
1から5までを出力する
/root/bin/loop1.shを作り、1から5まで1行に1つずつ出力させてください。
for x in 1 2 3 4 5、またはブレース展開{1..5}を使えます。
グロブでファイルを数える
/root/loops/logsディレクトリを作り、その中に.logファイルを4つ、.txtファイルを2つ置いてください。そして/root/bin/count.sh <디렉터리>を作り、そのディレクトリの直下の.logファイルの個数だけを出力させてください(プレースホルダーはディレクトリです)。空のディレクトリを渡すと0が出る必要があります。
一致するファイルがないと、パターン文字列がそのまま残ります。[ -f "$f" ] || continueで除外してください。
CSVを1行ずつ読む
/root/loops/users.csvに이름,역할の形式で3行以上入れ、/root/bin/readcsv.shを作って、そのファイルを読んで이름(역할)の形式で1行ずつ出力させてください(引数なしで実行します。2つの形式の韓国語は、それぞれ「名前」と「役割」を意味する語です)。
while IFS=, read -r a bの形を使い、入力はdoneの後ろでリダイレクトします。
関数をライブラリとして分離する
/root/bin/lib.shにslugify関数を定義してください。単語を小文字に変えて、ハイフンでつなぎます。/root/bin/use.shは、そのファイルをsourceで読み込んで使う必要があります。use.sh Hello Worldの出力はhello-worldです。
関数をlib.shに定義し、use.shから.またはsourceで読み込んでください。コピペしてはいけません。
終了コードで判定する
/root/bin/isnum.sh <문자열>を作ってください。数字だけでできていれば終了コード0、そうでなければ0以外のコードです。何も出力してはいけません。空文字列、符号、小数点は、すべて数字ではありません(プレースホルダーは文字列です)。
判定結果を出力すると、if文で使えません。return 0 / return 1でのみ知らせてください。
空白を含む引数を安全に巡回する
/root/bin/each.shを作り、受け取った引数ごとに[인자]の形式で1行ずつ出力させてください。each.sh "a b" cは2行になる必要があります。引数がなければ何も出力しません(プレースホルダーは引数です)。
for x in "$@"とfor x in $*の違いを確認してください。引数がないときは、何もしない必要があります。
可変長引数の合計
/root/bin/sum.shを作り、受け取った数値の引数の合計を出力させてください。引数がなければ0です。
累積変数を0で初期化するのを忘れないでください。引数がなければ0が出る必要があります。
ディレクトリの要約レポート
/root/bin/summary.sh <디렉터리>を作ってください。そのディレクトリの.logファイルの個数と全体の行数を、次の2行で出力します(プレースホルダーは、ディレクトリ、個数、合計です)。
files=<개수>total_lines=<합계>
ファイルの個数と全体の行数を一緒に数えます。空のディレクトリでも0/0が出る必要があります。