# WEBスキルチェック(第二回)
WEBスキルチェックの第二回です。[初回](https://hackmd.io/@tamura2004/HkuiKEFpu)で説明した内容も使用しますので、思い出しながら進めて下さい。
前回のスキルチェックで作成した自分のユーザーでログインしたところから始めて下さい。
課題は前回と同じく、ユーザーのホームディレクトリ直下のsubmitフォルダ内に作成して下さい。
例:001/sample.txt であれば
/home/[ユーザー名]/submit/001/sample.txtとして保存
ディレクトリは適宜作成する
## 1. システム管理
## pingの使い方
サーバとIPアドレスについては研修で学びましたね。
IPアドレスに応答できるサーバの有無は、`ping`コマンドで確認することができます。
```
$ ping -c 3 -w 3 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
--- 10.0.0.1 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2032ms
```
宛先サーバが存在しない、または、応答しない場合はこのように表示されます。`-c`は試行回数、`-w`は待ち時間を秒数で指定します。
試行回数を指定しない場合、`ctrl+C`で中断するまで無制限に応答確認を行います。
```
$ ping -c 3 -w 3 142.251.35.206
PING 142.251.35.206 (142.251.35.206) 56(84) bytes of data.
64 bytes from 142.251.35.206: icmp_seq=1 ttl=116 time=8.18 ms
64 bytes from 142.251.35.206: icmp_seq=2 ttl=116 time=8.25 ms
64 bytes from 142.251.35.206: icmp_seq=3 ttl=116 time=8.26 ms
```
宛先サーバが存在する場合、このような応答になります。`time=`の後ろに確認電文の往復にかかった時間が表示されます。約8ミリ秒前後になります。
## 課題1
IPアドレスの範囲、10.0.0.1〜10.0.0.10の中から、pingに応答するものが5つあります。その5つのIPアドレスを提出ファイル内に1行づつ縦に並べて記述して下さい。
末尾の数字が小さいものを先頭行にして小さい順から並べて記述します。また、最終行の末尾で改行して下さい。各行の先頭や末尾に余分な空白を入れてはいけません。
課題1:101/servers.txt(10点)
ファイルの作成は、前回説明した方法でもできますが、この後説明する`vim`を使用しても構いません。
## vimの使い方
ターミナル上のテキストエディタとして、業務で`vi`または`vim`を使用することがあります。簡単な使い方を覚えましょう。
まずは適当な場所に新しいファイルを作成します。`animal.txt`を作成しましょう。
```
$ vim animal.txt
```
すると全画面で以下のように表示されます。
```
_
~
~
"animal.txt" [New File]
```
この状態で、英小文字の`i`を押してみましょう。
```
_
~
~
-- INSERT --
```
iを押すと入力モードになります。ここから、`esc`キーを押すと、最初の画面に戻ります。最初の画面のことを編集モードと呼びます。複数のモードを切り替えて操作することが、`vi`の特徴の一つ目です。
編集モードでは、アルファベットの1文字づつに、カーソル移動やその他のコマンドが割り当てられています。これが`vi`のもう一つの特徴です。
入力モードに変更して、キーボードから、dog[enter]cat[enter]elefant[enter]と入力しましょう。そのまま入力されます。
```
dog
cat
elefant
_
```
さて、よく見るとelefantは、elephantの打ち間違いですね。修正してみましょう。上矢印を1回、右矢印を3回押します。カーソルがfの上に来たら、phと押しましょう。
そうしたら、fの後ろのaまでカーソルを移動させて、`bs`キーを押してfを削除します。
入力モードでは、普通のエディタと同じように編集できることが分かりましたか。
それでは保存しましょう。
編集モードに戻して、`:wq`と入力して`enter`キーを押しましょう。
`:`はコマンドラインウィンドウを表示させます。`w`はファイル書き込み、`q`はエディタの終了のコマンドです。
さて、正しく書き込まれたか、確認しましょう。
```
$ cat animal.txt
dog
cat
elephant
```
## 課題2
上で作成したファイルのelephantをratに変更して、ファイル名はそのままで提出フォルダに保存してください
。
課題2:101/animal.txt(10点)
## ftpの使い方
`ftp`コマンドを利用すると、他のサーバからファイルを取得したり、逆にファイルを送ったりすることが出来ます。
ただし、接続先サーバがftpサービスを立ち上げていることが必要です。サービスにはポート番号が割り当てられており、ftpは21番です。ちなみに、httpの番号は80番、sshの番号は22番です。
このように、異なるサービスに別々のポート番号を割り当てることにより、1台のサーバで複数のサービスを動作させることが可能になっています。
## ncでポート確認
それでは、先程確認した3台のサーバから、21番ポートに応答する、すなわちftpサービスが動作しているサーバを探しましょう。
色々な方法がありますが、`nc`コマンドで確認することもできます。試してみましょう。
```
$ nc -v 10.0.0.x 21
shellinabox.internal.cloudapp.net [10.0.0.x] 21 (ftp) : open
```
ctrl+cで中断します。
サーバでftpサーバが立ち上がっていない場合はConnection refusedと表示されます。
```
$ nc -v 10.0.0.x 21
shellinabox.internal.cloudapp.net [10.0.0.x] 21 (ftp) : Connection refused
```
## ftpによる接続
それでは、ftpサービスが立ち上がっているサーバに接続しましょう。
```
$ ftp 10.0.0.4
Connected to 10.0.0.4.
220 (vsFTPd 3.0.3)
Name (10.0.0.4:user): _
```
ユーザー名が求められるので入力して下さい。ここで使用するユーザー名は、ブラウザ接続で使用した初期IDです。自分用に登録したIDではないので注意。
記載したメールを消してしまったり忘れてしまったら、講師か同期に聞いて下さい。
```
331 Please specify the password.
Password:_
```
ユーザーIDを入力すると続けてパスワードの入力が求められますので、同じく初期パスワードを入力して下さい。
```
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
```
ログインに成功すると、`ftp>`というコマンドプロンプトが表示されます。ネットワークの制約を回避するため`passive`コマンドを入力して、モードを切り替えます。
```
ftp> passive
Passive mode on.
```
`ls`コマンドで、接続先サーバのファイルとディレクトリ一覧を取得出来ます。2つのファイルと、一つのディレクトリが確認できます。
```
ftp> ls
227 Entering Passive Mode (10,0,0,4,29,59).
150 Here comes the directory listing.
-rw-r--r-- 1 1001 1001 21 Aug 19 06:38 birds.txt
-rw-r--r-- 1 1001 1001 0 Aug 19 02:20 empty.txt
drwxrwxr-x 4 1001 1001 4096 Aug 19 06:35 log
```
## 接続が切れてしまったら
もし操作の途中で以下のようなメッセージが出たら、接続が切れてしまっています。
```
ftp> cd log
Not connected
```
`open`コマンドで再接続しましょう。
```
ftp> open 10.0.0.4
connected to 10.0.0.4.
220 (vsFTPd 3.0.3)
Name (10.0.0.4:user): _
```
最初の接続と同じように、ユーザーIDとパスワードを入力します。
## ディレクトリの移動
ディレクトリ`log`の中を確認してみましょう。
```
ftp> cd log
250 Directory successfully changed.
ftp> ls
227 Entering Passive Mode (10,0,0,4,29,59).
150 Here comes the directory listing.
-rw-r--r-- 1 1001 1001 21 Aug 19 06:38 http.log
drwxrwxr-x 4 1001 1001 4096 Aug 19 06:35 group_a
drwxrwxr-x 4 1001 1001 4096 Aug 19 06:35 group_a
ftp> pwd
257 "/home/eban/log" is the current directory
ftp> cd ..
250 Directory successfully changed.
ftp> pwd
257 "/home/eban" is the current directory
```
このように、ターミナルでのファイル操作と同じように`cd`,`ls`,`pwd`などのコマンドが利用できます。
## ファイルの取得
それでは接続先のサーバから、ファイルを取得しましょう。`get`コマンドを利用します。
```
ftp> get birds.txt
local: birds.txt remote: birds.txt
227 Entering Passive Mode (10,0,0,4,157,224)
150 Opening BINARY mode data connection for birds.txt (21 bytes).
226 Transfer complete.
21 bytes received in 0.00 secs (206.0868 kB/s)
```
これで接続元のサーバにファイルが取得されました。ftpを終了して確認してみましょう。
```
ftp> quit
221 Goodbye.
$ ll
-rw-r--r-- 1 1001 1001 21 Aug 19 06:38 birds.txt
$ cat birds.txt
sparrow
swallow
swan
```
## 課題3
接続先サーバのlogディレクトリ配下から、以下の条件に合致するファイルを1つ探して、提出先ディレクトリにファイル名を変更してコピーして下さい。条件に該当するファイルは1つのみ存在します。ディレクトリ内に更にディレクトリがある場合、その中も探す必要があるので注意して下さい。
条件:errorという文字を含む行が4行以上あること
課題3:102/dns.log(10点)
## awkの使い方
取得したログから以下の条件で項目を抽出する必要があるとします。
1.行の先頭が` + tx`で始まる
2.空白区切りの5列目を取り出す
1はgrepを使用すると実現できます。`awk`コマンドで2を実現してみましょう。
確認のため、以下の内容を持つ`abc.txt`を作成して下さい。
```
$ cat abc.txt
a b c d
e f g h
```
awkコマンドにより、2列目のみ、3列目のみなど取り出すことが出来ます。
```
$ cat abc.txt | awk '{print $2}'
b
f
$ cat abc.txt | awk '{print $3}'
c
g
```
## sort,uniqの使い方
urlの出現回数を集計する必要があるとします。
`sort`コマンドや、`uniq`コマンドを組合せて実現できます。
確認のため、以下の内容を持つ`color.txt`を作成して下さい。
```
$ cat color.txt
red
blue
yellow
blue
red
red
```
redが3行、blueが2行、yellowが1行あります。
```
$ cat color.txt | sort
blue
blue
red
red
red
yellow
```
前回でも少し説明したとおり、sortコマンドは並べ替えを行います。
```
$ cat color.txt | sort | uniq -c
2 blue
3 red
1 yellow
```
uniqコマンドは、同じ内容の行を1行に纏めます。`-c`オプションをつけると、元の行数を数字で付けてくれます。
```
$ cat color.txt | sort | uniq -c | sort -r
3 red
2 blue
1 yellow
```
更にsortをつなげることで、出現回数が多い順に並べることができます。`-r`オプションは、大きい順に並べることを示します。
## 課題4
前の課題で作成したdnsログから、以下の条件でurlを取り出し、集計して下さい。
・"+ tx"を含む行を対象とする
・行の5列目がurlなので、それを取り出す
・同じurlの出現回数を集計して、多い順に並べる
・件数の集計は`uniq -c`を使用する
課題4:102/ranking.txt(10点)
ヒント
```
$ wc dns.log
1460 8524 86460 dns.log
```
## 2. データベース
今回は、既に作成済のデータベースを使用して、テーブル結合の方法を学んでみましょう。
ftpサーバの、dbディレクトリ配下に、travel.dbというファイルがありますので、ftpで取得して適当なディレクトリに保存して下さい。[初回](https://hackmd.io/@tamura2004/HkuiKEFpu)の内容を思い出しながら、sqlite3で開いてみましょう。
`.headers on`コマンドは、列名を表示させます。
```
$ sqlite3 travel.db
> .headers on
> .table
attraction category city country customer
> .schema
...
```
国(country)や市(city)、観光地(attraction)などのテーブルがあるようです、実際に中身を見てみましょう。
```
> select * from attraction;
attraction_id|attraction_name|country_id|city_id|category_id
1|東京タワー|1|1|1
...
```
他のテーブル、country, city, categoryも同様に見てみましょう。
## キー
各テーブルとも、`テーブル名_id`の名前の列を持っています。市テーブルを除き、番号が重複していないことを確認して下さい。市テーブルについては、country_id, city_idの組であれば重複していないことを確認して下さい。
これらはキーと呼ばれ、テーブルから特定の行を取り出す際などに利用されます。
## テーブル間の関係
ひとつの国は複数の市を持ち、ひとつの市は複数の観光地を持ちます。また、ひとつのカテゴリもそれぞれ複数の観光地を持ちます。
このような、所属関係・親子関係を表現する際に、親側のキーを子側で持つような構成を取ります。また、これを利用して複数のテーブルを結合して情報を取り出すことが出来ます。
もし混乱したら、いったん読むのを止めて、紙に図を描いて整理してみましょう。このようなテーブルの関係を図示したものを、ER図と言います。
```graphviz
digraph hierarchy {
nodesep=1.0 // increases the separation between nodes
node [shape=box]
"市" -> "国" [label="country_id"]
"観光地" -> "市" [label="city_id,country_id"]
"観光地" -> "カテゴリ" [label="category_id"]
}
```
[参考:ER図とは](https://qiita.com/ramuneru/items/32fbf3032b625f71b69d)
さて、理解できたら、次のSQLを実行してみましょう。
```
> select country_name, city_name from country join city on country.country_id = city.country_id;
日本|東京
日本|大阪
...
```
複数のテーブルから、関係する情報を取り出すことができました。
## SQLファイルからの実行
結合を含むSQLは非常に長くなることがあります。`;`までは適宜改行することが出来ますので、一旦ファイルとして作成してから実行できると便利です。
sqlite3を一旦終了して、上のSQLをファイルとして作成してみましょう。
`SELECT`,`FROM`など予約語の手前で改行しています。また、予約語を大文字にしています。
```
$ cat country.sql
SELECT country_name, city_name
FROM country
JOIN city
ON country.country_id = city.country_id;
```
ファイルから実行するには以下のようにします。標準入力からSQLを渡していることになります。
```
$ cat country.sql | sqlite3 travel.db
日本|東京
日本|大阪
...
```
ターミナルで実行していますので、以下のようにすると結果をファイルに書き出すことができます。
```
$ cat country.sql | sqlite3 travel.db > result.txt
```
## 課題5
国名、観光地名、カテゴリ名からなるファイルを作成して下さい。
テーブルに含まれる全件が対象です。
並び順は、観光地のID(attraction_id)の昇順(小さい順)とします。
ファイルの1行目は以下のようになるはずです。
日本|東京タワー|モニュメント
課題5:103/attractions.txt(10点)
## 課題6
国名、市名、観光地名からなるファイルを作成して下さい
ただし、カテゴリがモニュメントであるもののみが対象です
並び順は、観光地のID(attraction_id)の昇順(小さい順)とします。
ファイルの1行目は以下のようになるはずです。
日本|東京|東京タワー
課題6:103/monuments.txt(10点)
ヒント:join句のあとのonで結合条件を指定しますが、複数のキーで接続している場合、andでつなげることが出来ます。
[joinで複数の条件を書くには](https://sql-oracle.com/sqlserver/?p=1180)
## 3. プログラミング
## ソート
並べ替え処理(ソート)は業務でも幅広く使用されています。プログラムでの様々なソート処理を確認してみましょう。
ターミナルからirbを起動します。
```
$ irb
irb(main):001:0> _
```
ランダムな数字の入った配列を準備してみます。
```
> rand(1..6)
=> 3
```
`rand`メソッドをこのように書くと、1から6までの数字をランダムに生成します。
```
> a = (1..10).map { rand(1..6) }
> a
=> [5, 6, 2, 3, 4, 1, 2, 4, 6, 3]
```
組み合わせて、1から6までのランダムな数字を持つ、長さ10の配列を生成しています。
小さい順に並べ替えてみましょう。
```
> a.sort
=> [1, 2, 2, 3, 3, 4, 4, 5, 6, 6]
> a
=> [5, 6, 2, 3, 4, 1, 2, 4, 6, 3]
```
並べ替えられましたが、配列aは変更されていません。
あとで使用するためには、別の変数に代入します。
```
> other = a.sort
> other
=> [1, 2, 2, 3, 3, 4, 4, 5, 6, 6]
```
別の変数に代入せずに、a自信を並べ替えるには`sort!`を利用します。
実行に伴って自分自身が変更されてしまう操作を、破壊的操作と呼び、`!`を付けています。
```
> a.sort!
=> [1, 2, 2, 3, 3, 4, 4, 5, 6, 6]
> a
=> [1, 2, 2, 3, 3, 4, 4, 5, 6, 6]
```
## ペアのソート
出席番号と身長など、数字の組でデータを持つ場合があります。出席番号順、身長順など、別々の項目で並べ替えができると便利です。
まずはテスト用の配列を準備しましょう。
```
> b = (1..6).map{|i| [i, rand(1..200)]}
> b
=> [[1, 97], [2, 194], [3, 160], [4, 71], [5, 56], [6, 198]]
```
これをバラバラにシャッフルします。その名の通り、`shuffle!`という破壊的メソッドがあります。
```
> b.shuffle!
=> [[3, 160], [1, 97], [2, 194], [4, 71], [5, 56], [6, 198]]
```
ばらばらになりました。それでは普通にソートしてみましょう。
```
> b.sort
=> [[1, 97], [2, 194], [3, 160], [4, 71], [5, 56], [6, 198]]
```
何も指定しないと、ペアの先頭順に並べ替えられるようです。ペアの末尾(身長)順にするには以下のようにします。
```
> b.first
=> [5,56]
> b.first.last
=> 56
> b.sort_by(&:last)
[[5, 56], [4, 71], [1, 97], [3, 160], [2, 194], [6, 198]]
```
`last`は配列の最後の要素を返すメソッドです。上記のように`sort_by`に渡すと、ペアの最後の要素順に並べ替えができます。
## 文字列のソート
文字列は辞書順にソートされます。テストデータを作ってみましょう。
```
> c = (1..10).map { ("a".."z").to_a.sample(6).join }
=> ["ewisgc", "mwtlnb", "aktyqc", "gracmy", "mliwrf", "brthie", "hqclkv", "toewrx", "hodbjn", "rlscgh"]
> c.sort
=> ["aktyqc", "brthie", "ewisgc", "gracmy", "hodbjn", "hqclkv", "mliwrf", "mwtlnb", "rlscgh", "toewrx"]
```
`sample(6)`は配列の要素からランダムに6つ選びます。`join`は配列の要素を文字列に変換して結合します。結果は文字列になります。
## 配列の統合(zip)
2つの配列からペアの配列を作るために、`zip`メソッドを使うことができます。
```
> c.zip(a)
=> [["ewisgc", 83], ["mwtlnb", 39], ["aktyqc", 3], ["gracmy", 29], ["mliwrf", 80], ["brthie", 48], ["hqclkv", 70], ["toewrx", 90], ["hodbjn", 75], ["rlscgh", 48]]
> c.zip(a).sort
=> [["aktyqc", 3], ["brthie", 48], ["ewisgc", 83], ["gracmy", 29], ["hodbjn", 75], ["hqclkv", 70], ["mliwrf", 80], ["mwtlnb", 39], ["rlscgh", 48], ["toewrx", 90]]
irb(main):020:0>
```
zipしてsortすると、先頭の要素で並べ替えられたペアが作成されました。前後を逆にしたa.zip(c)についても試してみましょう。
## ペア配列の分離
ペア配列を分離するには、`transpose`メソッドを使用できます。
```
> c.zip(a).sort.transpose
=> [["aktyqc", "brthie", "ewisgc", "gracmy", "hodbjn", "hqclkv", "mliwrf", "mwtlnb", "rlscgh", "toewrx"], [3, 48, 83, 29, 75, 70, 80, 39, 48, 90]]
```
aの番号が二番目に小さいcの要素は、"brthie"ですが、以下のように取り出すことが出来ます。
```
> c.zip(a).sort.transpose.first[1]
```
配列の添字は0から始まってるため、`[1]`で2番めの要素が取り出されます。`first`は配列の先頭です。
## バーチャルコンテスト
今回は8問のヴァーチャルコンテストに挑戦しましょう。上で説明したソートを使用する問題が含まれています。ぜひ、活用してみましょう。
[ヴァーチャルコンテスト・第二回](https://kenkoooo.com/atcoder/#/contest/show/489f3922-425e-4d1c-8f53-aee861dd5ce5)
課題:各5点×8=40点