ゆるふわシェル問題 その4
全部で8問
そんなに難しくないはず
だいたい1~3コマンドで解ける
使う技術
問題
Q1 環境変数PATHにセットされているディレクトリの一覧を縦に並べて出力してください
a1.shecho $PATH | tr : \\n #シェル芸
問題の意図
環境変数 PATH
を縦に並べて確認したくなることはちょいちょいあるので
解説
linuxの環境変数PATHにはディレクトリが
:
区切りで設定されている
なので :
を改行文字 \n
に置換してあげればよい
Q2 以下のテキストファイルaの数値に3桁きざみの , (カンマ)をいれて出力してください
q2.shcat << EOS | tee a
1000
10000000
EOS
a2.shrev a | sed -E 's/.{3}/&,/g' | rev
または
問題の意図
numfmt
コマンドを知ってるかなぁ、と思い
解説
sed
でやってる方
rev
で一度逆順にする
前方から「任意の文字が3文字連続する」
正規表現でマッチ
マッチした正規表現をそのまま使いつつ、後ろにカンマを付与して置換
rev
で元に戻す
numfmt
のほう
オプション渡すだけ
Q3 以下のファイル a b をカンマ区切りで横に並べて出力してください
q3.shseq 1 3 > a
seq 3 5 > b
echo "期待値は以下"
a3.sh seq 1 3 > a
seq 3 5 > b
paste a b -d ,
問題の意図
paste
コマンドの確認
解説
paste
は引数にファイルを受け取って横に並べて出力する
横に並べる時の区切り文字を指定するときは -d
オプションを使う
Q4 以下のfor文の引数のうち、「山田太郎」さん(表記のゆらぎをふくむ)にマッチするように変数を定義して、変数を呼び出すようにしてください
a4.shregexp="ここで定義"
for word in 山田太郎 山本太郎 山田一郎 山田たろう やまだ太郎; do
if [[ "\$word" =~ {ここで使用} ]]; then
echo \$word
fi
done
a4.shregexp="(山田|やまだ)(太郎|たろう)"
for word in 山田太郎 山本太郎 山田一郎 山田たろう やまだ太郎; do
if [[ "$word" =~ $regexp ]]; then
echo $word
fi
done
問題の意図
[[ ]]
における正規表現の使い方を問う問題
結構これ間違えてしまう
解説
[[ 左 =~ 右 ]]
と書く時、「右」に正規表現文字列を書くと正規表現と一致するか判定してくれる
この時、「左」はダブルクオートでくくっても良いが、「右」はダブルクオートでくくってはいけない
ダブルクオートも正規表現の判定に使われてしまうため
これは変数を展開する時も同じで、「左」はダブルクオートでくくっているが、「右」は変数をダブルクオートでくくってはいけない
例えば、以下は A
しか出力されない
sh[[ 山田 =~ ^山田$ ]] && echo A
[[ 山田 =~ "^山田$" ]] && echo B
Q5 1111年11月11日はいまから何日前?
a5.shseq -f "date -d '%g years ago' +%%Y" 1000 | sh | nl | grep 111[0-9] | awk '{for (i=0; i<365; i++) { print ($1-1) * 365 + i }}' | sed -E "s/.*/date -d '& days ago' +'& %Y-%m-%d'/g" | sh | grep -F "1111-11-11"
問題の意図
date
の使い方の確認
これ今まで出した問題の中で一番難しい気がする
てかゆるふわな問題では無かったかもしれない
解説
seq -f "date -d '%g years ago' +%%Y" 1000
では date
コマンドの文字列を作っている
こういう文字列が返ってくる
>date -d '1 years ago' +%Y
sh
にパイプで渡してコマンドを実行
実行結果に nl
で連番を付与
1111年前後の年だけほしいので grep
で絞り込み
awk
で年から日数を生成
生成した日数の数値から sed
で date
のコマンド文字列を生成
sh
で実行
実行結果のうち、 1111-11-11
を grep
することで、その日付が今から何日前かがわかる
他
ps1(Get-Date)-(Get-Date "1111/11/11") | Select-Object Days
Q6 bashのビルトイン関数ではないechoコマンドで寿司と出力してください
問題の意図
なんとなく
普段使ってる echo
はビルトイン(組み込み)関数なんだよということだけ
解説
単純に echo
と入力するとビルトイン関数が呼ばれてしまうので、フルパスで echo
を指定してあげればよい
Q7 以下のように引数を渡したときに、期待値の通り標準出力に出力できるように関数を定義してください
q7.shfunc1() {
: TODO
}
func1 "基本設計書 (1).xlsx" "" "world"
期待値
txta = 基本設計書 (1).xlsx
b =
c = world
a7.shfunc1() {
echo "a = $1"
echo "b = $2"
echo "c = $3"
}
func1 "基本設計書 (1).xlsx" "" "world"
問題の意図
関数の位置引数(positional parameter)の使い方とか
本当はダブルクオートでくくらなかったら位置がずれる、って問題にしたかったけれど問題の内容を間違えてしまった
解説
第一引数は $1
に設定される
第二、第三も同様
Q8 何らかの方法で以下の順序でechoコマンドを定義するけれど、出力順序は B A となるようにしてください。echoの前後になにかしても良いです
a8.sh(sleep 2; echo A) &
n=$!
(sleep 1; echo B) &
m=$!
wait $n
wait $m #シェル芸
問題の意図
コマンドのバックグラウンド実行方法を問う
問題の説明の書き方が良くなかったなぁと反省
解説
コマンドの末尾に &
を設定すると、そのコマンド呼び出しをバックグラウンドジョブとして実行してくれる
バックグラウンド実行されてるジョブを確認したいときは jobs
と実行すると確認できる
複数のコマンドをまとめてバックグラウンドジョブに登録したいときは ()
でくくってサブシェルにする
サブシェルの最初に sleep
を挟むことで echo
が実行されるタイミングをずらすことで実行順序を入れ替えた
ただしこのままだとechoが出力される前に親のシェルプロセスが終了してしまうので wait
を挟む
wait
では2つのバックグラウンドジョブを待つ必要があるので、バックグラウンドジョブ登録直後にプロセスIDを変数に設定する
$!
では直前のバックグラウンドジョブのプロセスIDが格納される
「直前の」なので複数バックグラウンドジョブを登録する場合は、1つバックグラウンドジョブを登録するたびに何らかの別変数に $!
の値を格納しないと上書きされる
最後に wait
で2つのジョブの完了を待機すれば完了
以上
余談
個人的にはQ5が一番難しいかな、と