OmniAICreator/Qiita-1.07M huggingface dataset:
Docker プライベートレジストリのWebUI調査 (2)

[Docker プライベートレジストリのWebUI調査 (1)](https://qiita.com/rururu_kenken/items/4ab319877dcdd750b2ab)
[Docker プライベートレジストリのWebUI調査 (3)](https://qiita.com/rururu_kenken/items/996076daaea11e657995)
[Docker プライベートレジストリのWebUI調査 (4)](https://qiita.com/rururu_kenken/items/fb7e6758a3fa60a83314)
[Docker プライベートレジストリのWebUI調査 (5)](https://qiita.com/rururu_kenken/items/b5b6b754774d76b08c53)
[Docker プライベートレジストリのWebUI調査 (6)](https://qiita.com/rururu_kenken/items/530ef38fe0342d6e2bfa)

#klausmeyer/docker-registry-browser

[Dockerイメージ](https://hub.docker.com/r/klausmeyer/docker-registry-browser)

**OS**
　Amazon Linux release 2 (Karoo)
**Docker**
　Docker version 18.06.1-ce, build e68fc7a215d7133c34aa18e3b72b4a21fd0c6136

**■前提条件**

docker network作成、プライベートレジストリのコンテナ作成、イメージ登録済

```
docker network create registry-ui-net

docker run -d \
  -p 5000:5000 \
  --net registry-ui-net \
  --name registry-srv \
  registry:latest

docker pull ubuntu:16.04
docker tag ubuntu:16.04 localhost:5000/ubuntu:16.04
docker push localhost:5000/ubuntu:16.04
```

コンテナ起動

```
docker run -d \
  -p 8082:8080 \
  --net registry-ui-net \
  --name registry-browser \
  -e DOCKER_REGISTRY_URL=http://registry-srv:5000 \
  -e ENABLE_DELETE_IMAGES=true \
  klausmeyer/docker-registry-browser
```
![image1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208807/a62aec03-17aa-e9d6-640b-e8e43c506971.png)
![image2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208807/4439f635-39b9-08d9-8993-8a6935c4dfcb.png)
Ubuntu Server 16.04 から 18.04 にアップグレード後，ifupdown ではなく netplan を使うようにする

# 背景
Ubuntu 16.04 ではネットワークインタフェースの設定に `ifupdown` を利用しますね．
`/etc/network/interfaces` を書き換える方法に慣れてしまった方も多いのではないでしょうか．

しかし，Ubuntu 18.04 では標準で `netplan` を利用します．
Ubuntu 16.04 からアップグレードした場合には従来の設定ファイルをそのまま使えますが，
`netplan` は構文のチェックもしてくれるので何かと便利です．

そこで，Ubuntu Server 16.04 から 18.04 にアップグレードした後，
`ifupdown` ではなく `netplan` を使うようにするための方法を記載します．

# おさらい
Ubuntu 16.04 から 18.04 へのアップグレード方法の詳細は省略させていただきます．
概ね以下のようなコマンドを入力し，インストールウィザードに従っていけば大丈夫なはずです．

```
$ sudo apt update
$ sudo apt upgrade
$ sudo apt autoremove
$ sudo do-release-upgrade
```

# 手順
- `netplan` のインストール

apt でインストールできます．

```
$ sudo apt install netplan.io
```

- 設定ファイルの作成

例を参考にしながら `/etc/netplan/` 以下に .yaml ファイルを作成します．
[Netplan configuration examples](https://netplan.io/examples)

```/etc/netplan/config.yaml
network:
  version: 2
  renderer: networkd
  ethernets:
    enp3s0:
      addresses:
        - 10.10.10.2/24
      gateway4: 10.10.10.1
      nameservers:
          search: [mydomain, otherdomain]
          addresses: [10.10.10.1, 1.1.1.1]
```

上記の例では 1 つのインタフェースに IPv4 のアドレスを静的に割り当て，DNS の設定もしています．
各自の `/etc/network/interfaces` を見ながら作成してください．

- 設定の適用

下記のコマンドで設定が反映されます．
`netplan try` によって apply 前に構文チェックも可能です．

```
$ sudo netplan apply
```

- 不要なパッケージの削除

使用しなくなった `ifupdown` は削除してしまいましょう．

```
$ sudo apt purge ifupdown
```

- 再起動

設定がうまく行っていれば，再起動で完了となります．

```
$ sudo reboot
```

# おわりに
まだ `/etc/network/interfaces` を使っている方も `netplan` へ移行してしまいましょう．
設定ファイルのミスを修正するのが楽になると思います．

# 参考
- [Install Netplan on Ubuntu - Linux Hint](https://linuxhint.com/install_netplan_ubuntu/)
- [How to enable netplan on ubuntu server upgraded from 16.04 to 18.04 - Ask Ubuntu](https://askubuntu.com/questions/1034711/how-to-enable-netplan-on-ubuntu-server-upgraded-from-16-04-to-18-04)
Spring Security

急に現場で使うことになったので
いくつかわからないことを調べる。

### 1. HTMLの「ユーザ名」と「パスワード」をjava側でどのように識別するか。

```java:WebSecurityConfigSample
  protected void configure(HttpSecurity http) throws Exception {
    // ログイン処理の設定
    .formLogin()
    // ログイン処理のURL
    .loginPage("/login")
    .usernameParameter("username") // ここでHTMLのname属性?を設定する。
    .passwordParameter("password") // ここでHTMLのname属性?を設定する。
    .permitAll()
```


　　

### 2. UserDetailsServiceってユーザ情報しか渡さないけど、パスワードも一致していることをどのように確認するのか。

・UserDetailServiceでUserDetailに設定したパスワードと passwordParameter()で設定したパスワードを比較するらしい？
ので、実際に比較処理をこちらがで実装しなくていいらしい。
・比較処理に失敗するとBadCredencialExceptionが発生する。


### 3. SuccessHandlerとFailedHandlerへは何をもって遷移が切り替わるのか。
https://terasolunaorg.github.io/guideline/public_review/Security/SpringSecurity.html
6.3.2.3. 認証処理の設定
認証に成功した場合、認証ユーザー情報を返却する
認証に失敗した場合、例外をスローする

### 4.DefaultのJdbcDaoImplの内部的な処理はUserDetailServiceと同一？
まず、DBにアクセスして認証するためのインターフェース「UserDetailsService」がSpringで用意されている。
また、それを実装したデフォルトクラスに「JdbcDaoImpl」がある。

 ⇒ 定義を見てみると確かにUserDetailsServiceの実装である。

```java:JdbcDaoImpl.java
　　public class JdbcDaoImpl extends JdbcDaoSupport implements UserDetailsService { ~~~ }
```

### 5.現場で下記２つの認証処理の登録をやって要るっぽいのだけれど片方いらないのでは？

```java:WebSecurityConfigSample.java
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.jdbcAuthentication().dataSource(datasource).usersByUsernameQuery("SELECT * ~~~"); 
    auth.userDetailsService(service);
  }	  
```

内部メソッドをのぞいてみると「auth.jdbcAuthentication()」も「userDetailsService」もListに追加されていた。
つまるところ、認証処理は複数設定できるってこと？(いずれ動作確認したい。)

```java:AbstractConfiguredSecurityBuilder.java(一部抜粋)
   private <C extends SecurityConfigurer<O, B>> void add(C configurer) throws Exception {
   List<SecurityConfigurer<O, B>> configs = ~~~
   configs.add(configurer);
```


### 6. EnableGlobalMethodSecurity(prePostEnabled  true)の役割とは
　メソッド呼び出しに対する「認可」処理が有効になるらしい。
　アクセスポリシーを設定できるメソッドに付けられるアノテーションが有効になるらしい。

```java:sample.java
  @PreAutorize("has('ADMIN') OR (#username == principal.username)")
  public xxx() {}
```

また、jspでもアクセスポリシーが設定できて管理者のみ表示するような条件分岐を行うことができる。

```xml:sample.jsp
  <sec:authorize access="hasRole(`ADMIN`)">
  
  </sec:authrize>
```

### 7.認可エラー時に実装すべき2つのインターフェース
AccessDeniedHandler : 承認済みのユーザの認可エラー応答処理
AuthenticationEntryPoint : 未承認ユーザの認可エラー応答処理

### 8. 認証結果(UserDetails)が保持する権限情報(GrantedAuthority)の中身はどんな？
https://docs.spring.io/spring-security/site/docs/5.0.x/api/org/springframework/security/core/GrantedAuthority.html

・SimpleGrantedAuthority : String型で保持
・LdapAuthority

```java:sampleAdd.java
  authorities.add(new SimpleGrantedAuthority(authority.toString())); // 権限をList<String>へ追加する。
```
URLRequestのcachePolicy

# はじめに
HTTPサーバからiPhoneにHTTP Status = 304と戻しているのに、iPhoneでは、HTTP Status = 200に見えました。これは、iPhone内でキャッシュを持っているようで、キャッシュポリシーによって、アプリ側の見え方が異なるようです。サーバからの応答をそのまま受け取りたかったので、実現方法を調べてみました。

# 実装方法

SwiftのURLRequestのcachePolicyを変更することにより、アプリが取得できるデータが変わる。何も指定しないと".useProtocolCachePolicy"。RFC2616に定義されているキャッシュポリシーを利用。
ローカルキャッシュを使わずに、サーバからデータを取得したい場合には、
”.reloadIgnoringLocalCacheData”を指定する。

  キャッシュポリシー | 説明
------------------|----------
　 .useProtocolCachePolicy | デフォルト。RFC2616
　 .reloadIgnoringLocalCacheData | ローカルキャッシュを無視して、サーバから取得する
　 .returnCacheDataElseLoad | 有効期限が切れていてもキャッシュが存在していれば、キャッシュを参照。キャッシュがない場合に、サーバから取得する。
　 .returnCacheDataDontLoad | 有効期限が切れていてもキャッシュが存在していれば、キャッシュを参照。キャッシュがない場合に、取得に失敗する。
　 .reloadIgnoringLocalAndRemoteCacheData | 未実装。利用してはいけない
　 .reloadRevalidatingCacheData | 未実装。利用してはいけない


# 環境
- Xcode 10.2
- Swift 5

# 参考文献
- Apple公式マニュアル [URLRequest.CachePolicy](https://developer.apple.com/documentation/foundation/urlrequest/cachepolicy)
- Apple公式マニュアル [デフォルトのキャッシュポリシー] (https://developer.apple.com/documentation/foundation/nsurlrequest/cachepolicy/useprotocolcachepolicy)
【html】Twitterへの投稿リンク（Twitter Share Button）の設置

直近のWebアプリ制作で、Twitterへの投稿リンク（Twitter Share Button）の設置を行う機会がありましたので、
今回設置で実装した範囲を、備忘と共有を兼ねて記載します。

#Twitterへの投稿リンクって？
Twitterが公式で提供しているTwitterの投稿を行うためのリンクです。
メディア系サービスで、記事をTwitterで共有してもらうために設置することが多い印象です。

:link: [例えばこんな感じ](https://twitter.com/intent/tweet?text=テキスト)
こちらのリンクをクリックすると、以下のようなツイート画面が表示されます。
![無題.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/381875/11176d06-9a81-7c26-50d8-3328e44005bb.jpeg)

#実装方法
基本的には、Twitterの公式ドキュメントに記載された内容の通り実装すれば問題ありません。
:link: [Guides — Twitter Developers — Tweet Button](https://developer.twitter.com/en/docs/twitter-for-websites/tweet-button/overview)

#基本のき
"[https://twitter.com/intent/tweet](https://twitter.com/intent/tweet)"を遷移先とするリンクを設置すれば良いだけです。
かんたん！

```html:html
<a href="https://twitter.com/intent/tweet">
    Twitterへ投稿
</a>
```

#応用
"[https://twitter.com/intent/tweet](https://twitter.com/intent/tweet)"の後ろに、クエリストリングの形式でパラメータを付加することができます。
ここでは以下をご紹介します。

- text(ツイート内容を設定する)
- url(URLを設定する)
- hashtags(ハッシュタグを設定する)
- via(関連するTwitterアカウントを設定する)

```html:html
<a href="https://twitter.com/intent/tweet?text=Hello%20world&url=https://qiita.com/&hashtags=qiita&via=hogeUser">
    Twitterへ投稿
</a>
```
:link: [こんなかんじ](https://twitter.com/intent/tweet?text=Hello%20world&url=https://qiita.com/&hashtags=qiita&via=hogeUser)

:point_up_tone1: **ポイント**
パラメータの値はURLエンコードする必要があります。
URLエンコードの方法は割愛させていただきますが、概ね以下のような対応を行う必要があるかと思います

- 事前にURLエンコードを行っておく
- リンククリック

## text(ツイート内容を設定する)
ツイート画面表示時に、ツイート内容をデフォルトで設定したい場合は、textを設定します。

```html:html
<a href="https://twitter.com/intent/tweet?text=Hello%20world%0d改行も可能">
    Twitterへ投稿
</a>
```
:link: [こんな感じ](https://twitter.com/intent/tweet?text=Hello%20world%0d改行も可能)

## url(URLを設定する)
ツイート画面表示時に、URLをデフォルトで設定したい場合は、urlを設定します。
URLは投稿時にTwitter側で短縮リンクに変換されます

```html:html
<a href="https://twitter.com/intent/tweet?url=https://qiita.com/">
    Twitterへ投稿
</a>
```
:link: [こんな感じ](https://twitter.com/intent/tweet?url=https://qiita.com/)

## hashtags(ハッシュタグを設定する)
ツイート画面表示時に、ハッシュタグをデフォルトで設定したい場合は、hashtagsを設定します。
複数指定可能(カンマ区切り)、先頭の#は除いて設定します

```html:html
<a href="https://twitter.com/intent/tweet?hashtags=qiita,twitter,html">
    Twitterへ投稿
</a>
```
:link: [こんな感じ](https://twitter.com/intent/tweet?hashtags=qiita,twitter,html)

## via(関連するTwitterアカウントを設定する)
ツイート画面表示時に、ツイートに関連するTwitterアカウントを設定したい場合は、viaを設定します。
@は除いて設定します

```html:html
<a href="https://twitter.com/intent/tweet?via=hogeUser">
    Twitterへ投稿
</a>
```
:link: [こんな感じ](https://twitter.com/intent/tweet?via=hogeUser)


#最後に
今回はTwitterへの投稿リンクの基本的な部分だけでしたが、
Twitter公式ドキュメントを読む限り、他の機能やオプションなどまだまだ色々ありそうです。
また知見がたまったら記載していこうと思います

#参考
:link: [twitterのツイートボタンを作るためのリンクshareとintent/tweet](https://qiita.com/coconeko/items/dcd3a004578e02f63da5)
:link: [Guides — Twitter Developers — Tweet Button](https://developer.twitter.com/en/docs/twitter-for-websites/tweet-button/overview)
CVXOPTのimportの経緯

#こちらを実行したい
授業で講談社の「**機械学習のための連続最適化**」を輪講していて、14章のSVMの理解を深めたいと考え検索していた。
何件か理論とその実装の記事が見つかりコードも記載してあった。今回は[線形SVMの理論と実装](https://satopirka.com/2018/12/theory-and-implementation-of-linear-support-vector-machine/)にあるコードを実行する。

#出たエラー
最適化問題を解くのに便利なcvxoptライブラリについてimportがまだだった。
```Q = cvxopt.matrix(_Q)
p = cvxopt.matrix(-np.ones(N))
G = cvxopt.matrix(-np.eye(N))
h = cvxopt.matrix(np.zeros(N))
A = cvxopt.matrix(y[np.newaxis], (1, N), 'd')
b = cvxopt.matrix(0.0)
solution = cvxopt.solvers.qp(Q, p, G, h, A, b)
```

jupyterを起動し貼っつけてみると以下のように出る。

>ModuleNotFoundError                       Traceback (most recent call last)
<ipython-input-8-fa601ce7f3ca> in <module>()
      1 import numpy as np
      2 import sys
----> 3 import cvxopt
      4 import matplotlib.pyplot as plt
      5 import sklearn.datasets
ModuleNotFoundError: No module named 'cvxopt'

#対処
対処はこちらを参考にさせていただいた。凄くシンプル。検索は[No module named]で。
[cvxoptのインストール](http://hidehikomurao.blogspot.com/2018/11/python-cvxopt.html)
ちなみにModuleNotFoundErrorで検索すると[ModuleNotFoundError解決方法](https://qiita.com/homines22/items/c43e564811466bf088e7)といった記事が紹介されたが今回は関係なさそう。

#結果
AnacondaでもJupyterでも動作確認でき、記事と同じ実行結果となった。一安心。
     pcost       dcost       gap    pres   dres
 0: -8.2406e+00 -1.4586e+01  3e+02  1e+01  2e+00
 1: -7.6844e+00 -2.4040e+00  3e+01  2e+00  2e-01
 2: -4.7478e-01 -5.7972e-01  1e+00  5e-02  6e-03
 3: -3.5402e-01 -4.3247e-01  9e-02  7e-04  8e-05
 4: -4.1264e-01 -4.3173e-01  2e-02  8e-05  1e-05
 5: -4.2931e-01 -4.2951e-01  2e-04  9e-07  1e-07
 6: -4.2949e-01 -4.2949e-01  2e-06  9e-09  1e-09
 7: -4.2949e-01 -4.2949e-01  2e-08  9e-11  1e-11
Optimal solution found.
![svm02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206597/d545e6d4-a88e-690a-98b4-878516d150b6.png)

#感想
二次計画問題について先に学習していたため、今まで学んでいたことにつながっていくのが面白いと思う。以下などを参照しながら最適化アルゴリズムの実装につなげたい。
機械学習の数理 Advent Calendar 2018
[Pythonの数理最適化用ライブラリCVXOPTの使用例](https://qiita.com/MoyashiMisosoup/items/4cbde18ca5b324b2366e)
データベースの歴史：超初心者向け

###データベースとは

情報を整理して簡単に取り出せることを目的としたもの

###データベースの始まり

第二次世界大戦中に米軍が敵国の情報を整理する為に、データ専用の基地「データベース」を作ったのがきっかけで、「データベース」という言葉ができたらしい。

###データベースの4つの進化

####1.ネットワーク型

シンプルに言うとタグ付け。
SNSのタグをイメージするとわかりやすい。

**問題点**
分類分けはできたが、整理するのがとても大変。

↓

####2.階層型

名前の通り階層型にデータを整理したもの。
会社組織図などのイメージ。

**問題点**
綺麗にまとまっているが、まだ使いづらい。

↓

####3.リレーショナル型

シンプルにいうとエクセルのようなもの。
既存のデータを変更せずに新しい情報を追加して整理できる。
今現在も主流となっている。

**問題点**
オンラインゲームのような同時多発的なアクセスに適していない

↓

####4.NoSQL

データ構造が(Key-Value Store)の形でとてもシンプル。
個々の情報を同時多発的に高速検索する必要がある場合に適している。
AWS ALBのターゲットグループの登録解除の遅延は本当に遅延しているのか

グッドモーニング。いがりです。

今日はタイトル通り、AWS Application Load Balancer（以降、ALB）のターゲットグループの登録解除の遅延の挙動を調べてみました。

もともと、調べようと思ったきっかけは、現在ECS(Fargate)を使っていて、デプロイツールに ecs-deploy を使用していて、このデプロイ時間を短くしようと思ったことからです。

ecs-deployは[こちら](https://github.com/silinternational/ecs-deploy)
[![Image from Gyazo](https://i.gyazo.com/77a43f6598e9dd7576d728fb39bd55d1.png)](https://gyazo.com/77a43f6598e9dd7576d728fb39bd55d1)

まず、デプロイ（ローリングアップデート）時のALBは何をしているのか、まとめました。

## ECS(Fargate) の ローリングアップデート でのALBの挙動
ざっくりいうと、新しいインスタンスを立てて、ALBを用いてインスタンスが立ったら新しいインスタンスにパケットを流そう！ということです。。。

簡単な図にしてみました。

### 1.デプロイ前
[![Image from Gyazo](https://i.gyazo.com/8d5f8ccf5320dda9683f73efbacb6bb6.png)](https://gyazo.com/8d5f8ccf5320dda9683f73efbacb6bb6)

ロードバランサーがFargateインスタンスに対してパケットを流しています

### 2.デプロイ中
[![Image from Gyazo](https://i.gyazo.com/78b3ca03313973605c381714cd333cda.png)](https://gyazo.com/78b3ca03313973605c381714cd333cda)

デプロイ中。新しいFargateインスタンスが立ちました。

### 3.デプロイ後①
[![Image from Gyazo](https://i.gyazo.com/81cc908a71037f19c98392039c729559.png)](https://gyazo.com/81cc908a71037f19c98392039c729559)

ALBが新しいインスタンスが立ったことをチェックして、新しいインスタンスにパケットを流し始めます。

### 4.デプロイ②
[![Image from Gyazo](https://i.gyazo.com/bcb76bc5f1628133416e823e232162aa.png)](https://gyazo.com/bcb76bc5f1628133416e823e232162aa)

使わなくなった古いFargateインスタンスが消えます。

このようにしてローリングアップデートが行われます。ここで登録解除の遅延という概念が登場します。

## 登録解除の遅延とは

AWSにはこのような記述が。
> ターゲットの登録解除中に未処理のリクエストが完了するまで待つ時間。この時間中、ターゲットの状態はストリーミングになります。

ここでいう、ターゲット（ターゲットグループ）とは、Fargateインスタンスのことです。
なので、ターゲットの登録とはどのインスタンスにパケットを流すかを決定することですね。

[![Image from Gyazo](https://i.gyazo.com/8d5f8ccf5320dda9683f73efbacb6bb6.png)](https://gyazo.com/8d5f8ccf5320dda9683f73efbacb6bb6)
登録されているターゲットに、矢印のようにアクセスが流れます。

この記述から推測するに、未処理のリクエストがすべて完了したら、遅延の時間を待たずに登録を解除するのでしょうか。🤔
それとも、遅延時間が最後まで経過するまで待ってから登録を解除するのでしょうか。🤔

また、[公式Docs](https://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/load-balancer-target-groups.html#deregistration-delay)にはこのようにあります。

> Elastic Load Balancing は、登録解除するターゲットへのリクエストの送信を停止します。デフォルトでは、Elastic Load Balancing 登録解除プロセスを完了する前に 300 秒待って、ターゲットへ処理中のリクエストが完了するのを助けることができます。Elastic Load Balancing が待機する時間を変更するには、登録解除の遅延値を更新します。

お。後者なのかな。

もし後者であれば、遅延の時間を短くすればデプロイ時間は短縮するはず。

今回は、小さな実験をして、遅延時間とecs-deployの時間の関係性を調べてみました。

## 実験： 遅延時間とecs-deployの時間の関係性

### 動作環境
```
・ECS(Fargate)にRuby on Railsアプリをのせている
・ecs-deployからローリングアップデートをさせる
・アプリに30秒ごとのヘルスチェック以外のアクセスはしない
```

### やること
遅延時間を30秒間隔で0秒から120秒まで増やしていく実験を行い、遅延時間とecs-deployでかかる時間を調べる。

### 結果
表にしました。

| 遅延時間(sec)     |       ecs-deployの処理時間(sec) |
|:-----------------:|:------------------:|
| 0             |               162|
| 30           |            191 | 
| 60             |              203 |
| 90               |                234 | 
| 120             |             275 |  

### 考察
およそ、
`(ecs-deployの処理時間) = (遅延時間) + 160 [sec]`

と見ることができるでしょうか。👀
線形に増加しているようです。なるほど。

デプロイ時間は遅延時間に比例して伸びるようです。

## まとめ
**デプロイ時間は登録解除の遅延時間に線形に伸びる**
なので、ローリングアップデートをしているシステムで、デプロイ時間を短くしたい場合は、この遅延時間を短くする必要がありそうです。
Homebrew のPHP7.2 新 formula の php@7.2 を Mac(+ fish shell) にインストールする手順

久々に homebrew の PHP7.2 を Mac OS ( fish 環境 ) にインストールしようとしたら homebrew/php が deprecated になっていたので、新フォーミュラ `php@7.2` のインストール手順を残す。

```
## 新 Formula
brew install php@7.2

## $PATH を通す
echo 'set -g fish_user_paths "/usr/local/opt/php@7.2/bin:$PATH" $fish_user_paths' >> ~/.config/fish/config.fish
echo 'set -g fish_user_paths "/usr/local/opt/php@7.2/sbin:$PATH" $fish_user_paths' >> ~/.config/fish/config.fish
source ~/.config/fish/config.fish

## バージョン確認
php -v
=> PHP 7.2.18 (cli) (built: May  2 2019 13:03:01) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
```
Visual Studio Codeの新規拡張機能のRemote Developmentを試した

#記事の目的
Remote Developmentを導入にあたって、何点か障壁があったので、共有しようと思います。先に言うと、ホスト側のログインシェルがcsh(tcshも？)では使えません。結局、私は導入できませんでした。

Remote Developmentについて、ここでは説明しません。下記URLで説明されているので参考にしてください。
https://forest.watch.impress.co.jp/docs/news/1183384.html

#Remote Development導入の動機
現状の開発では、Windowsでコーディングして、ソースコードをscpでLinuxに送ってbuildしています。さらに、ソースコード管理にgitを使っているので、branchを切り替えたりすると、WindowsとLinuxでたまに同期がうまくとれない場合があり、少々不便でした。できれば、ソースコードは1ヶ所に置きたいと思っており、今回Visual Studio Codeの拡張機能のRemote Developmentを試してみようと思いました。

#導入手順
##Visual Studio Code Insidersのインストール
ここからダウンロードして、インストールします。
https://code.visualstudio.com/insiders/
##Remote Developmentのインストール
Visual Studio MarketplaceからRemote Developmentを検索して、インストールします。このとき、Remote SSH等も一緒にインストールされます。

##接続先の設定
Remote DevelopmentをインストールするとVisual Studio Codeの左側にあるアクティビティーバーにサーバに接続しそうなアイコンが追加されるのでクリックします。すると、アクティビティーバーのすぐ右画面にサイドバーが出ます。そこにCONNECTIONタブがあり、その右のほうに設定のアイコンがあるのでクリックすると、設定ファイルを選択できるようになります。ここでは"C:¥User¥username¥.ssh¥config"をクリックします。
エディタで下記のようにHostname等を入力しましょう。これで保存するとサイドバーにあるCONNECTIONにhogeが追加されます。

```:config
Host hoge
    Hostname hostname
    user username
```

#SSHクライアントのインストール
hogeをクリックするとSSHしようとするのですが、その前にSSHクライアントを下記の方法でインストールする必要があります。
https://code.visualstudio.com/docs/remote/troubleshooting#_installing-a-supported-ssh-client
補足すると、まずWindows10とWindows7で方法が違います。Windows10ではOSの機能としてON/OFFできて、おそらくデフォルトでONになっています。Windows7ではGit for windowsをインストールして、PATHをはればOKです。

#SSH接続
私の環境ではSSH接続しようとすると、SSHクライアントが途中で落ちる？ような状態となってしまい、このままでは実行できませんでした。これは一度、ターミナルでSSHして、RSA key fingerprintを作成しておけば解消されます。
これでCONNECTIONのhogeをクリック(hogeの右側にでるアイコンでないとダメかも)するとSSH接続がはじまって、Visual Studio Codeの右下に"ⓘ Confirming that hoge is reachable ..."とポップアップがでます。
私はここでつまづいたのですが、これを待っているだけでは接続できません。Visual Studio Codeの統合ターミナル(bash, powershellになっている？)をunameに変更して、"username@hostname's password:"と出力されているので、パスワードを入力します。これで接続！...できるはずでしたが、現実は甘くはありませんでした。

#ログインシェルの制約
私の場合は接続できず、下記のようなWarningが出力されました。このWarningはcshとtcshで出力されるらしく、ログインシェルの変更が必須になるようです。私の環境ではログインシェルの変更ができず、泣く泣くRemote Developmentの導入は諦めました。残念です。

```
Warning: no access to tty (Bad file descriptor).
Thus no job control in this shell.
```

#もう一つ問題
私の環境ではもう一つ上手くいかないことがありました。
SSH接続の際、"C:¥Users¥username¥AppData¥Local¥Temp"にあるファイルを参照するのですが、このPathが間違って読み込まれているようでした。

エラーを追っていくと、"C:¥Users¥username¥.vscode-insiders¥extensions¥ms_vscode-remote.remote-ssh-x.xx.x¥out¥extension.js"のprocess.env.TEMPがうまく読み込めていませんでした。

システム変数をcallしているのですが、システム変数TEMPには"C:¥Users¥username¥AppData¥Local¥Temp"が与えられ、間違っていませんでした。設定の変更では回避できずに、process.env.TEMPの箇所に直接、"C:¥Users¥$username¥AppData¥Local¥Temp"を記載して(extension.js修正後、マシンの再起動要)、なんとか動かせました。ログインシェルの問題で、結局動かせなかったのですが...
MATLABからPythonを実行しようとしたら未定義と言われたとき or MATLABで使うPythonを指定したいとき

検証したOSはUbuntuのみですが多分どのOSでも大丈夫です。備忘録です。

デフォルトでは`python`コマンドが使えないといけないみたいです。`python3`コマンドではダメです。まずはそこを確認しておきましょう。

次に`python3`コマンドとかしかないよといった場合。MATLABの`pyversion`関数を使用して直接Pythonを指定してやるのが楽みたいです。

```MATLAB:MATLAB
>> pyversion [実行可能パス]
```

例

```MATLAB:MATLAB
>> pyversion /usr/bin/python3
```

下手に`alias python='python3'`とかやっても認識されなかったしこれが一番楽そう
仮想環境とかもこれなら柔軟に対応できそうですね

参考になれば幸いです。

参考URL:

[変数 "py" または関数 "py.command" は未定義です - MATLAB & Simulink - MathWorks 日本](https://jp.mathworks.com/help/matlab/matlab_external/undefined-variable-py-or-function-py-command.html)
PhpStorm(IntelliJ) でコピー&ペースト時の自動整形をなしにする

## 参考

下記のページを参考にした 
[Paste without auto-formatting](https://intellij-support.jetbrains.com/hc/en-us/community/posts/207280009-Paste-without-auto-formatting)  

## 実行
Preferences > Editor > General > Smart Keys > Reformat on paste: None
[raspberry pi] PHP7.1-fpmをapt-getでインストールする

参考
http://www.neko.ne.jp/~freewing/raspberry_pi/raspberry_pi_php_7_2/

# パッケージソースに下記の行を追加する
```.bash
$sudo nano /etc/apt/sources.list
```
以下の行を末尾に追加

```.bash
deb http://mirrordirector.raspbian.org/raspbian/ buster main contrib non-free rpi
```

変更を有効にする

```
$sudo apt update
$sudo apt upgrade
```

PHP関連モジュールを一括でインストール

```
$sudo apt install php7.1-fpm php7.1-mcrypt php7.1-curl php7.1-cli php7.1-mysql php7.1-gd php7.1-iconv php7.1-xsl php7.1-json php7.1-intl php-pear php-imagick php7.1-dev php7.1-common php7.1-mbstring php7.1-zip php7.1-soap -y
```

以上
[EC-CUBE4]インストール後にbin/console server:runが実行できなくなる

# 環境

- Ubuntu 18.04
- PHP 7.2
- MySQL 5.7
- EC-CUBE 4.0.2 ( 基盤としているSymfonyは3.4.22 )

# 背景

モダンなフレームワークは大半がサポートしていますがEC-CUBE4（が利用しているsymfonyフレームワーク）には開発用途のビルトインウェブサーバがあります。これを使えば、本格的なWebサーバを設置しないでもクイックに開発がすすめられます。

EC-CUBEのパッケージを公式サイトからダウンロードして、ルートディレクトリ直下で`bin/console server:run`で実行が開始されて、`http://127.0.0.1:8000`でアクセスできます。（Ctrl+Cで終了）

# 問題

インストールが完了後、一度、終了するとこのコマンドが利用できなくなります。エラー内容は以下です。

```
There are no commands defined in the "server" namespaces
```

参考として画面も。
![](https://i.gyazo.com/ca31c82e6065a2f7e192c69dea6abf8e.png)

# 対処

インストール後にルートディレクトリ直下に作成される`.env`内の`APP_ENV`の値を`prod`から`dev`に変更することで実行できるようになります。

APP_ENVはsymfonyが管理する定義のようですが、こういった挙動は常識なんですかね？？？
[How to Master and Create new Environments (Symfony Docs)](https://symfony.com/doc/current/configuration/environments.html)


# 所感

EC-CUBE4、2日間かかって、いまだに開発環境が構築できません。主な罠は以下。
心が折れそうです。。。(T_T)

- インストーラの（PHP拡張モジュールの有無に関する）誤った警告 ← 次のエラーと関係あるんじゃないか？と思って調査に時間を要した
- プラグインインストール時の一貫性のないエラー（しかもときどき成功する）
- システム要件にPHP7.1以上と書かれているがPHP7.3ではcomposerエラー発生
- インストール後にビルトインウェブサーバが利用できなくなる（本件）
- インストール方法の違い（Composer利用とパッケージ利用）で、インストール後のディレクトリ構成が異なる。←気持ち悪い

その他、調査に時間を要したものとして以下など。

- SESでメール送信するための設定方法の調査
- 拡張ライブラリをインストールするためのコマンド
初心者メモ　Debian一般ユーザー作成とsudo権限付与まで

#### 一般ユーザー作成
```
# useradd [ユーザー名]
```
#### パスワード設定
```
# passwd [ユーザー名]
Enter new UNIX password: 
Retype new UNIX password:
```
#### sudoインストール
```
# apt install sudo
```
#### 権限付与
```
# usermod -G sudo [ユーザー名]
```
#### ユーザー切り替え
```
# su [ユーザー名]
```
Excel 3つのセルに令和の年月日が分割で入っている場合日付で変換する　さらに5年先の年度末を令和をカッコ書きつきで表示する

#次のように令和の日付が年、月、日で入っています。

| |A |B |C |D |E |F |G |H |
|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
1 |令和| 元| 年|5 |月| 1| 日|
2 |令和| 2| 年|1|月| 1| 日|


これを日付にするのは
=DATE(SUBSTITUTE(ASC(B1),"元","01")+2018,D1,F1)
となります

##年度末について考えます
令和元年は西暦では
2019年度
です
平成31年度でもあります。
5年のカウントは次の年から考えるなどあります。この場合2019年度が終了してから5年後、2015年、つまり
+6
を加算した
2025年度末
とします。
すると2025年度末は
2026年3月31日
です。
つまり
令和08年03月31日
です。
2019年からみると
2026-2019=7
となります。
しかし令和元年度の
令和02年1月1日だと
2026-2020=6
となります。


```
=IF(MONTH(DATE((SUBSTITUTE(ASC(B1),"元","01"))+2018,D1,F1))<=3,YEAR(DATE(SUBSTITUTE(ASC(B1),"元","01")+2018+7,D1,F1))&"(令和"&SUBSTITUTE(ASC(B1),"元","01")+7-1&")"&"年度末",YEAR(DATE(SUBSTITUTE(ASC(B1),"元","01")+2018+7,D1,F1))&"年度末"
```
AOJ ITP1_11_A: Dice I サイコロ問題

# ITP1_11_A
Dice I | Aizu Online Judge
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ITP1_11_A

問題は処理の部分
サイコロを一方向に回すとき
4面が動き
2面は固定
Nに1回動くときはSに3回動く
Wに1回動くときはEに3回動く

サイコロの面は配列として考え0-5までに当てはめる。
このプログラムは配列の添字をベースとして考え
動く前と動いたあとの数値を連鎖をさせながら考えている。

0, 1, 5, 4などのマジックワードが出てくるが、
これはサイコロの面の動きを原始的に考えているだけ。

Nに1回動かすと考え(X軸方向)
添字は
0 1 2 3 4 5 (初期値)から
1 5 2 3 0 4 と変化する
ここは紙の出番。
紙上で書きながら考える。
（自分には頭の中だけで考えるのは無理。）

2面と3面は固定しているのがわかる。（変化なし）
動いているのは4面
初期値の動く数字を見ていく
上面の0はtemporary（＝一時退避）
0には1を入れる。
1の場所には5を入れる。
5の場所には4を入れる
4の場所には0 （＝temporary）を入れる。

トップを一旦退避して、そこに数字を入れ、さらにそこに入れた数字を追いかけていきながら考える。
添字で考えているので6面サイコロならどんな数字でも対応可能。

これをプログラムとして書くと

``` dice.cpp
void roll_x() { roll(0, 1, 5, 4); }  // to N
void roll(int i, int j, int k, int l) {
    int t = f[i];
    f[i]  = f[j];
    f[j]  = f[k];
    f[k]  = f[l];
    f[l]  = t;
}
```

この考えを元にy軸方向とz軸方向でも考える。

Z軸
012345(初期値)
024135(0と5は固定)

Y軸
012345(初期値)
215043(1と4は固定)

```dice.cpp
void roll_z() { roll(1, 2, 4, 3); }
void roll_y() { roll(0, 2, 5, 3); }  // to W
```
と答えが出る。

# 参考
オンラインジャッジではじめるC/C++プログラミング入門 p238

```dice.cpp
# include <bits/stdc++.h>
using namespace std;

class Cube {
   public:
    int f[6];  //サイコロ
    Cube() {}  //コンストラクタ
    void roll_z() { roll(1, 2, 4, 3); }
    void roll_y() { roll(0, 2, 5, 3); }  // to W
    void roll_x() { roll(0, 1, 5, 4); }  // to N
    void roll(int i, int j, int k, int l) {
        int t = f[i];
        f[i]  = f[j];
        f[j]  = f[k];
        f[k]  = f[l];
        f[l]  = t;
    }
    void move(char ch) {
        if (ch == 'E')
            for (int i = 0; i < 3; i++) roll_y();  // Wを３回
        if (ch == 'W') roll_y();
        if (ch == 'N') roll_x();
        if (ch == 'S')
            for (int i = 0; i < 3; i++) roll_x();  // Nを３回
    }
};

int main() {
    Cube   c;    // サイコロ
    string com;  // 命令

    // 入力
    for (int i = 0; i < 6; i++) cin >> c.f[i];  // サイコロの面
    cin >> com;                                 // 命令

    // 処理
    for (int i = 0; i < com.size(); i++) {
        c.move(com[i]);
    }

    // 出力
    cout << c.f[0] << "\n";

    return 0;
}

```

### 感想
クラスを使うということでプログラミングへの心理的障壁は高かったが、中身を改めて見渡すと中学生でも簡単だと思えるレベルのものだった。
しかし、紙上でサイコロの面を追いかけながらどの数字がどこに入るかを追いかけていくのはかなりの時間がかかってしまった。
VSCode InsiderのRemote Development extensions(Python)を試す

## 概要
VSCode InsiderをつかってPythonのRemote Debugを試してみます。
docker環境で開発を行い、ローカルの環境がグチャグチャになるのを防ぐということができるはずです。
[Getting Started](https://code.visualstudio.com/docs/remote/containers#_getting-started)に書いてあるとおりです。

> (Docker Toolbox is not currently supported.)

とあるのを気にせずdocker toolboxで進めてみたら、案の定エラーになりました。
ということで以下はDocker for Windows環境で試したものです。


## 手順
### VSCode Insider設置
VSCodeには未導入のためVSCode Insiderを設置します  
[Download Visual Studio Code Insiders](https://code.visualstudio.com/insiders/)から取得して設置するだけです

### Remote Development 拡張設置
[Remote Development extension pack](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack)を設置します

>Remote - SSH
>Remote - Containers
>Remote - WSL
>Remote - SSH: Explorer
>Remote - SSH: Editing COnfiguration

も同時にインストールされるようです。

### サンプルプロジェクトを取得
Pythonで試したかったので以下を取得

~~~
git clone https://github.com/Microsoft/vscode-remote-try-python
~~~
[vscode-remote-try-python](https://github.com/microsoft/vscode-remote-try-python)そのままです。

### Open Folder in Container
左下の緑のボタンをクリックして、「Remote-Containers: Open Folder in Container」を押します
F1コマンドで「Remote-」と押して候補選択してもよいです。
<img width="769" alt="remote.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278271/033561b4-cce9-daed-2e1e-a28a9ce59339.png">

<img width="449" alt="remote2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278271/ae8b3a33-b222-981f-14b1-b15f15920e55.png">

docker buildが始まって、VSCodeの編集画面が開きます。

試した環境ではプロキシありの環境だったので

~~~
docker build --build-arg http_proxy=xxx ...
~~~
を指定したかったのですが、--build-argの指定方法がわからず
DockerFile内にENV http_proxy=xxxを足すことで対応

VSCode InsiderのExtensionはimageが変わるごとに追加する必要があるようですが
Managing extensionsを参考にsettings.jsonに

~~~
"remote.containers.defaultExtensions": [
    "ms-python.python"
]
~~~
を追加しておくことで自動設置してくれるようでした。

あとは、ブレークポイントの設定をしてデバッグ実行することで、ローカルのフォルダを開くのとほとんど同じ感覚で使えます。
dockerコマンドを打たずにシームレスに統合されている感がすごい。

一度コンテナ止めてから、再度「Open Folder in Container」してもつながるので、どこかで覚えていてくれているのでしょう。

### Remote-Containers: Attach to Running Container

好きなようにコンテナを立ち上げた状態から
おもむろにVSCodeを立ち上げ、
F1コマンドから「Remote-Containers: Attach to Running Container」を実行、
コンテナ選択することでアタッチできます。

既存のdocker-composeを利用して、ひとまずアタッチする場合にはこちらのほうが便利かも。
.devcontainerにdocker-composeファイルを指定する方法もあるようなので
Open Folder in Containerからの起動の方がステップがすくなるのかもしれません。


### Remote-Containers: Forward Port from Container

ガイドにこっそりありますが、
起動させた状態から一時的にポートフォワードの設定を追加することができます。

空いているポートでないと候補にあがってこないようなので
デバッグ実行などしてポートを開いた状態で
F1コマンドから「Remote-Containers: Forward Port from Container」を実行します。

一度とめなくてよいので便利です。

## まとめ
リリースが楽しみです。
HerokuでActive Adminの管理者ユーザーの権限を与える


###はじめに 
開発環境で管理者ユーザー権限を与えるには $rails console でできますが、Herokuにデプロイして、本番環境に移行した後ってどうやって設定するの？？
と思ったのでその方法を共有しておこうと思います！

###方法
まずは 
```$cd app.name ```
でアプリのディレクトリに移動して


```$ heroku login
```
をします。この時、なぜか

```heroku: Press any key to open up the browser to login or q to exit:```

というエラーが出てしまって何か押しても

```heroku: Waiting for login... ⣷```

と出てしまって先に進まないときは、

```$heroku login --interactive```

とするとできると思います。

パスワードなどを入れてHerokuにログインできたら、
```$ heroku console```

コマンドを実行してみると、
```Running console on ⬢ app.name... up, run.3425 (Free)
Loading production environment (Rails 5.1.7)
irb(main):001:0>```

となり、アプリに入れます。

```irb(main):001:0>  user = User.find(1)
irb(main):002:0>  user.admin = true          
irb(main):003:0>  user.save   ```

（↑ここは開発環境の時に　$rails c で管理者ユーザー権限を与えた方法と同じにしてください  ）

これで、本番環境のアプリにも、指定したユーザーに管理者ユーザーの権限を与えることができます。

少しいたずらされてもちょちょいと消し去ることができるようになりました！！


###参考文献
- Railsで管理者権限（admin）を設定するカンタンな方法は？
 - https://nyoken.com/rails-admin
- herokuでactiveadminの管理者ユーザー権限を与えるには
 - https://kossy-web-engineer.hatenablog.com/entry/2018/09/06/060801
- heroku loginコマンドを実行できません
 - https://teratail.com/questions/158511
PHPのsessionでセットされるレスポンスヘッダー

PHPのプロジェクトの挙動を追ってたら予想外の挙動に遭遇したのでメモがてら。

# TL;DR;

`session_start`を呼ぶとhttp response headerの中身に書き換わるものがある

# 詳しく

途中でセットしてあるはずのヘッダーが実際にはサーバーから送信されず、どこからも指定していないヘッダーが送信される、という挙動に遭遇した。

具体的には 

```php
header('Cache-Control: pre-check=0');
```

のようにカスタム？値をセットしているはずがレスポンスには現れず、代わりに以下の3行が追加されていた。ちなみにpre-checkというのは仕様には無く、IE5の独自ヘッダーらしい？

> Expires: Thu, 19 Nov 1981 08:52:00 GMT
> Cache-Control: no-store, no-cache, must-revalidate
> Pragma: no-cache

この値をセットしている記述はどこにもないが、ローカル環境でもプロダクション環境でも同様の値がセットされているため、何かしらの処理が入っているはず。全く当たりがつかないので、ステップ実行しながら怪しい箇所で `headers_list`を実行して、何に起因しているかを追ってみた。すると、どうやら`session_start`をコールした前後で書き換わっているようだった。

なぜこんなことが起きるか分からないのでsession拡張のコードを見てみると
`session_start` 関数から`php_session_cache_limiter` が呼ばれていた。
気になったらこの辺から追ってみると良いでしょう。
https://github.com/php/php-src/blob/f009d6e7c3ce124d1a5a602e7532f93ed55ebae0/ext/session/session.c#L2431

その中ではiniの設定値の `session.cache_limiter` の中身に従ってヘッダーを設定する処理が記述されている。
https://github.com/php/php-src/blob/f009d6e7c3ce124d1a5a602e7532f93ed55ebae0/ext/session/session.c#L1180-L1208

関連していそうな`session_cache_limiter`関数のドキュメントはこちら
https://www.php.net/manual/ja/function.session-cache-limiter.php

私の手元では`session.cache_limtier`に `nocache`が設定されていたため、

```c
CACHE_LIMITER_FUNC(nocache) /* {{{ */
{
    ADD_HEADER("Expires: Thu, 19 Nov 1981 08:52:00 GMT");

    /* For HTTP/1.1 conforming clients */
    ADD_HEADER("Cache-Control: no-store, no-cache, must-revalidate");

    /* For HTTP/1.0 conforming clients */
    ADD_HEADER("Pragma: no-cache");
}
```

この処理が実行されて上で記述したヘッダーがセットされているようだった。
このExpiresの日付はなんなんだと思ったけど、この変更が入ったのはどうやら16年も前らしい・・・

試しにExpiresやContent-Typeがどうなるかを見るために

```php:test.php

<?php
header('Content-Type: application/json');
header('Expires: Wed, 18 Nov 1981 00:00:00 GMT');
session_start();

echo json_encode([
    'foo' => 'bar',
]);
```

という適当なPHPコードを書いてBuilt inサーバーで実行したところ

> Content-Type: application/json
> Expires: Thu, 19 Nov 1981 08:52:00 GMT
> Cache-Control: no-store, no-cache, must-revalidate
> Pragma: no-cache

というヘッダーが返ってきた。Expiresは上書きされてConetnt-Typeはそのままという想定した挙動になった。

session_startをコールする前にheaderをセットするのはお行儀の悪さが感じられてあまり見ることは無いんじゃないかと思うが、久しぶりにPHPの想定しない挙動に巡り会った。

`header`関数が呼ばれたらすぐに出力に乗るのかと思っていたけど、`headers_sent`を実行してもfalseが返ってくるしsession_startも警告を出さないのでどうやら違うらしい。
これも調べてみた方が良さそう。
ExcelVBAからの脱却。そして初めてのhtmlとCSS、JavaScript

# はじめに
いままで自分はプログラミングといえばもっぱらExcelVBAを触ってきた。
それは業務の中で、環境導入の必要のない最も身近だったプログラミング環境だからだ。
仕事もよくありがちなExcelベースが多かった為、必然とも思える。

しかし、その名の通りExcelVBAは"Excel"が入っていないPCでは動作しない。
この度、動作を想定している端末では、

- Excelが他の業務で使用中の場合、作業の邪魔をしたくない。
- Excelそのものが入っていない端末でも同じように動作させたい。

という欲求があった。
そこで、どの端末にも入っているブラウザで動作するアプリケーションを作ろうと思った。

記事を書いていくのは

- プログラミングを少しかじったことがある。
- 改善活動をしている。

そういった人の後押しになれればと思ったからだ。
あとは備忘録~~と承認欲求~~。

# やりたいこと
業務で使用しているデータはもっぱらcsvのテキストデータである。
csvのログデータ、数値データを元に業務を円滑に行えるよう、
ガイダンスとしてのアプリケーションの作成を目標とする。
以上を踏まえた上で、当面の学習していく機能は以下。

- ローカルにあるcsvデータの読み込みと表示、確認、消去
- 読み込んだcsvデータからのガイダンス検索、表示機能
- データの自動更新
- グラフが描ける。


#環境諸々
- ~~GoogleChromeで動作すること。~~ ローカルファイルを読み込むためElectronで行うことになった。
- 実行環境として追加のソフトウェアのインストール(自身含め)が不要であること。
- html + CSS + JavaScript
- コーディングはVSCodeを使用。

次回から具体的な内容を書いていく。
React基礎③「Stateとライフサイクル」

#はじめに
[Reactホームページ](https://reactjs.org/)の内容と付け加えて学んだことを備忘録として書き残す。
##Stateについて
stateとは：コンポーネントの完全に独立した値。<br>
値が変更されるとrenderが行われる。

##ライフサイクルを使用するにあたって
``ライフサイクルはクラス定義によって作られたコンポーネント
のみ使用できる。``

###クラスの書き方

```jsx

//コード１
class Clock extends React.Component{
    render(){
        return(
            <div>
                <h1>It is {this.props.date.toLocaleTimeString()}.</h1>
            </div>
        );
    }
}

```

1. React.Componentを継承するクラスを定義（ここではクラス名をClockとしている）
2. render(){}と呼ばれる空のメソッドを追加する。
3. render(){}の`{}`の中に`return();`を追加する。
4. return();の`()`の中に表示内容を書いていく。

※注意点：render()の中で使われる`props`などは`this.props`とする。

ここでコンポーネントをClassで作ることで同一DOM内で
あるクラス（設計図）をもとに作ったインスタンス（実物）が
1つだけになるので、stateやライフサイクルが使える。

###stateの追加

```jsx

//コード2
class Clock extends React.Component{
    constructor(props){
        super(props);
        this.state = {date: new Date()};
    }
    render(){
        return(
            <div>
                <h1>It is {this.state.date.toLocaleTimeString()}.</h1>
            </div>
       );
    }
}

ReactDOM.render(
    <Clock/>,
    document.getElementById('root')
);
```

1. コード１の`this.props`を`this.state`に変更する。
2. `this.state`の初期状態をコンストラクタで設定する

```jsx

//コード3
constructor(props){
    super(props);
    this.state = {date: new Date()};
}
```
※注意点：コンストラクタでは`super()`によって親要素のコンストラクタ
を呼び出すのが必須である。

##クラスにライフサイクルメソッドを追加

ライフサイクルはリソースの開放をしリソースを無駄なく使うための
メソッド。コンポーネントのデータの更新をし画面上に表示させるまでの
一連の流れを示します。<br>
＜実際の流れ＞
Mounting→Updating→Unmounting

* Mounting<br>
クラスがDOMとして描画するための準備。
* Updating<br>
描画されており、ユーザーが編集できる期間。
* Unmounting<br>
コンポーネントが破棄される際にコンポーネントが占有していた
リソースの開放を行う。

```jsx

class Clock extends React.Component{
    constructor(props){
        super(props);
        this.state = {date: new Date()};
    }
    
    componentDidMount(){
        this.timerID = setInterval(
            ()=>this.tick(),1000
        );
    }
    
    componentWillUnmount(){
        clearInterval(this.timerID);
    }
    tick(){
        this.setState({
        date: new Date()
        });
    }
    
    render(){
        return(
          <div>
            <h1>It is{this.state.date.toLocaleTimeString()}.</h1>
          </div>  
        );
    }
}

ReactDOM.render(
    <Clock/>,
    document.getElementById('root')
);
```
1. `</Clock>`が`ReactDOM.render()`に渡されると
まず、ReactはClockコンポーネントのコンストラクタの呼び出しをする。
コンストラクタではthis.stateの初期化を行っている。
2. render()は画面に出力する項目を指定している。
Clockコンポーネントの`render()`を呼び出すことで、Reactは画面に表示すべき項目を
知ることができる。DOMをClockのrender出力と一致するように更新する。
3. Clock の出力が DOM に挿入されると、React は componentDidMount() ライフサイクルメソッドを呼び出します。
その中で、Clock コンポーネントは毎秒ごとにコンポーネントの
 tick() メソッドを呼び出すためにタイマーを設定するようブラウザに要求します。
4. ブラウザは、毎秒ごとに`tick()`メソッドを呼び出す。
その中でClockコンポーネントは現在時刻を渡した`setState()`メソッドを
呼び出すことで、UIの更新を予約する。Reactはstateが変化したことを確認し、
render()メソッドで再度何を画面上に表示すべきなのかを判断する。
その変更に応じてReactはDOMを更新する。
5. ClockコンポーネントがDOMから削除された場合は、Reactは`componentWillUnmount()`ライフスタイルメソッド
を呼び出しリソースの開放を行う。
【Chapter 1】 文系大学生が読み解くDeep Learning Book

# 【Chapter 1】 文系大学生が読み解くDeep Learning Book

## 概要

数学的素養がほとんど高校生止まりの
とある文系大学生が著名な[Deep Learning Book](http://www.deeplearningbook.org/)
を読み解いていきたいと思います。（＝翻訳・まとめ・解釈）

## きっかけ

今僕は丁度20歳なのですが、
とある偉大な先輩から
「僕が今2019年の20歳だったら間違いなくこの本を徹底的に読み込むね」
とおっしゃっていたので、
いざ奮起して頑張ります。

それでは早速いきたいと思います。

## Chapter 1

### 1.0 Introduction

- Deep Learningの大きな転換点の一つは、1997年に、IBM の Deep Blue が当時の世界チャンピオンを倒したこと。

- Machine Learning の例として、Logistic Regression や Naive Bayesなどが存在する。

- Machine Learning のデータの表現(representation)を特徴(feature) という。

- AutoEncoder(自己符号化器)によりデータの次元削減を行い、新たな特徴を獲得できる。

- MLP(Multilayer Perceptron・多層パーセプトロン)は結局はただのインプットからアウトプットを導く関数にしか過ぎない。

### 1.1 Who Should Read This Book? / 誰がこの本を読むべきか？

- 読者として想定しているのは、これからMachine Learning を専攻しようとしている大学生・大学院生、もしくは Deep Learning の技術を取れいれたいソフトウェア・エンジニア

- 本書のPart 1 は、基礎的な数学や Machine Learning のコンセプト中心

- 本書のPart 2 は、Deep Learning の近年の進歩について

- 本書のPart 3 は、これからの Deep Learning に不可欠な概念などについて

### 1.2 Historical Trends in Deep Learning / Deep Learning の過去のトレンドについて

- Deep Learning は、トレーニング の データの分量の拡大に伴い加速的に成長してきた。

- Deep Learning のモデル自体も、コンピュータの処理能力の飛躍に伴い、加速的に複雑になってきた。

- Deep Learning により、高度な複雑な問題においても精度高い予測をはじき出すことができるように。

### 1.2.1 The Many Names and Changing Fortunes of Neural Networks / Neural Networks の変遷について

- Deep Learning の歴史は 3つの区分(Cybernetics, Connectionism, Artificial Neural Networks)に分けることができる。

- 1950年代に開発されたADALINEは、初期のStochastic Gradient Descent(確率的勾配降下法)を用いていた。

- Linear Model(線形モデル)は、XOR関数を学習することができない。

- Neuroscience をベースに Deep Learning が発展してきた歴史がある。

- 現在の多くのNeural Networkでは、Neuron として、非線形関数のReLUを用いるように。

- University of Toronto の Geoffrey Hinton, University of Montreal の Yoshua Bengio, New York University の Yann LeCun より カナダの CIFAR による NCAPイニシアチブの遂行

### 1.2.2 Increasing Dataset Sizes / 肥大化傾向にあるデータセット容量

- デジタル化社会がデータセットの肥大化に大きく寄与

- Big Data 時代により大きなデータセットを用いて汎化性能が飛躍的に工場

- 小さいデータセットや教師なし学習への適用も研究分野としては重要になりつつある

### 1.2.3 Increasing Model Sizes / 肥大化傾向にあるモデルのサイズ

- 隠れ層が発明されてから、Neural Networkのモデルのサイズはおよそ2年半おきに倍増

- 1980・1990年代 の MNIST データセット、そして2000年代のCIFAR-10 データセット

- コンピューティング能力の加速的進化は、現代のDeep Learningの進化における大きな原動力

### 1.2.4 Increasing Accuracy, Complexity and Real-World Impact / 高まる精度・複雑度・現実社会へのインパクト

- 2012年度の ILSVRC に置いて、史上初めてエラーの度合いが26.1%から15.3%へと低下し、CNN(Convolutional Neural Networks / 畳み込み ニューラル ネットワーク)の台頭のきっかけに。

- Reinforcement Learning(強化学習)の発展

- 総じて、Deep Learning とは Machine Learning のアプローチの一つであり、高度な演算処理能力の発展により、加速度的に進化している。

## 最後に

間違いやご指摘などが御座いましたらご教示願います！

## 参考文献

https://deepage.net/deep_learning/2016/10/09/deeplearning_autoencoder.html
https://qiita.com/JunyaKaneko/items/b3ec906be4fb8cb713ad
http://blog.brainpad.co.jp/entry/2017/02/24/121500
TemporaryDirectoryとwith文の仕組み

# わかる

```py:f.py
path = __file__
f = open(path)
print(type(f))       # <class '_io.TextIOWrapper'>
f.close()
with open(__file__) as fw:
    print(type(fw))  # <class '_io.TextIOWrapper'>
```

with文はスコープを抜けるとデストラクタが呼ばれるため上記の場合`f.close()`を省略できるというスグレモノである。よくある例なので特にどうということはない。

# わからない

```py:d.py
import tempfile
t = tempfile.TemporaryDirectory()
print(type(t))       # <class 'tempfile.TemporaryDirectory'>
with tempfile.TemporaryDirectory() as tt:
    print(type(tt))  # <class 'str'> ←なんで文字列？
    print(tt)        # /tmp/tmp6nng15e8　←中身はディレクトリパス
```

with文を使うとasで渡した変数に文字列、この場合は一時ディレクトリのパスが入りました。

# どうしてこうなった？

ドキュメントを読むのが正規ルート。
[3\. データモデル — Python 3\.7\.3 ドキュメント](https://docs.python.org/ja/3/reference/datamodel.html#context-managers)
>object.\_\_enter__(self)
コンテキストマネージャのの入り口で実行される処理です。 with 文は、文の as 節で規定された値を返すこのメソッドを呼び出します。

https://github.com/python/cpython/blob/master/Lib/tempfile.py

```py:tempfile.py
class TemporaryDirectory(object):
...
    def __enter__(self):
        return self.name

```

`name`を返すようになっています。with文によって`cleanup()`メソッド呼ばれるし、他に必要なメソッドがない、必要になるとしたらパスだけだから`__enter__`で名前だけ返してやればいいよねって言うことか。
【個人メモ】carrierwaveでcacheを取り回す

# 概要
* [ここ](https://github.com/carrierwaveuploader/carrierwave#making-uploads-work-across-form-redisplays)を読んで、どうやって実装するかわからず、ネットの情報もぼんやりとしていたので、書いてみました。。
* carrierwaveを使った画像アップロードとフォーム→確認画面→送信の画面遷移を想定したときに、一度アップロードした画像のキャッシュを活用すればOK

# 環境
* local の開発環境
* DBはsqlite
* cacheは `public/tmp/` に配置
* carrierwaveは1.3.1
* railsは5.2.2

# その前に
* ざっくりとしか試していません。
* 関連するところしか記述していないです。適宜おぎなってください。

# 方法
## ポイントとなるメソッド
* [cache_name](https://www.rubydoc.info/github/jnicklas/carrierwave/CarrierWave%2FUploader%2FCache:cache_name)
* [retrieve_from_cache!](https://www.rubydoc.info/github/jnicklas/carrierwave/CarrierWave%2FUploader%2FCache:retrieve_from_cache!)
* xxx_cache （この名前は何でも良いと思う）

# コード
主要な箇所のみソースコード書きます。

```uploader/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
 # 省略
end
```

```models/user.rb
class User
  attr_accessor :image_cache
  mount_uploader :image, ImageUploader
end
```

```controllers/users_controller.rb
class UsersController
  def new
    @user = User.new
  end
  def confirm
    @user = current_user.users.new(user_params)
    # フォームからimageが渡されなかったとき（=変更がなかったとき）にcahceから復元するところがポイント
    if !@user.image.present?
      @user.image.retrieve_from_cache! @user.image_cache
    end
    @user.image_cache = @user.image.cache_name
    # 省略
  end
  def create
    @user = current_user.users.new(user_params)
    @user.image.retrieve_from_cache! @user.image_cache # cache nameを渡して、imageを復元するところ
    # 省略
  end

  def user_params
    params.require(:user).permit(:image, :image_cache)
  end
end
```


```new.html.erb
// 適当にconfirmアクションににアクセスできるようにｍ、 routes.rbを定義してください。
<%= form_with @user, url: user_confirm_path, local: true do |f| %>
  <%= f.file_field %>
  <%= f.hidden_field :image_cache %>
<%= f.submit %>
```

```confirm.html.erb
<%= form_with @user, url: userｓ_path, local: true do |f| %>
  <%= f.hidden_field :image_cache %>
<%= f.submit %>
```

# ポイント
cache nameを渡して、imageを復元するところと、フォームからimageが渡されなかったとき（=変更がなかったとき）にcahceから復元するところがポイント
* [cache_name](https://www.rubydoc.info/github/jnicklas/carrierwave/CarrierWave%2FUploader%2FCache:cache_name) これで、cache fileのidを取得
* [retrieve_from_cache!](https://www.rubydoc.info/github/jnicklas/carrierwave/CarrierWave%2FUploader%2FCache:retrieve_from_cache!) 上記cache fileを imageに復元する

# その他
* cacheは
Nuxt.js v2.7.0 がリリースされた

日本時間 5/14 に Nuxt.js v2.7.0 がリリースされました！

**[Nuxt.js v2.7.0 Release Note](https://github.com/nuxt/nuxt.js/releases/tag/v2.7.0)**

以下に出てくるキャプチャはすべて上記の Release Note のものを使わせていただいています。

# DX の改善

## SSR ログのブラウザ出力

SSR 時に出力されたログがブラウザのコンソールにも出力されるようになりました！

![](https://user-images.githubusercontent.com/904724/57533442-12acec80-733e-11e9-92e9-950718b198ac.gif)

SSR のログは `nuxt dev` を実行したターミナル上で確認する必要がありましたが、その必要がなくなります。

## `store/` 以下の監視の改善

`store/` ディレクトリの作成が監視されるようになりました！
作成すると自動で再読み込みされます。

![](https://user-images.githubusercontent.com/904724/57455462-70243900-726b-11e9-8b1b-675e31f17c44.gif)

これまでは、`store/` が存在しないまま `nuxt dev` を実行しているときに `store/` を作成しても、自動的に再読み込みが行われませんでした。

### 【要注意】`serverMiddleware` の監視の改善

PS として `serverMiddleware` の監視も改善されたと書かれています。

v2.6 までは、`serverMiddleware` で指定したファイルを変更した際に確かに再読み込みが行われるのですが、実際に反映はされないという不具合がありました。

これが改善されたのだと思って実際に手元で試してみたのですが、どうやら改善はされていない様子でした...

# 最後に

この記事よりも詳しい記事がもう出ていました...

[Nuxt.js v2.7.0 の変更点と注目のポイントまとめ - Qiita](https://qiita.com/potato4d/items/43b4f17c73aebc7a2154)
基本に忠実に

# サンプルコードで動かしてみる
- 前日の問題から何がいけないのか？を調べる為改めて本を読み返してみた結果、[サンプルコード](https://www.shoeisha.co.jp/book/download/9784798142418)があったのを思い出した
- さっそくDLして動かしてみると本の解説の通り動く。なので自分で書いた内容がいけない事が判明した。個人的によくやる事だけど、妙に自分のオリジナリティーを出そうとして構文なり設定なりを設定、書き間違える事が過去にも多々あったような気がした。ただのコピペになってしまうとはいえ基本に忠実である事が大切であると改めて痛感

# でも動かない（涙
- サンプルコードを部分修正しコードを書いてみるもやはり同じエラー
- いや〜、参りました。そしてセンス無し...
- また明日頑張ろう。お休みなさい

# 補足：そろそろSCMを使わないと
- 前職で使っていたGitをそろそろ使わないと思い始めた
- 直編はやはり効率が悪いので今週末にクラウドに対するssh接続よく調べ直そうと思う。昔やった事でも全然思い出せず（汗汗汗
Java tips - StaticUtilityクラスの修飾子

# StaticUtilityクラスへの修飾子
皆さんはStaticUtilityクラスにどんな修飾子をつけていますか？
私はなんの疑問を持たずにfinalをつけていました。
最近のソースコードはfinalをつけずにabstractをつけていたので、その詳細と解説を備忘録として残します。

## ちょっと昔の一般的なStaticUtilityの書き方
前のプロジェクトでは、StaticUtilityクラスを作成する際は、final修飾子をつけて
インスタンスを生成されないようにprivateコンストラクタを作成していました。
（毎回、めんどくさいなあと思いながら）

```java
// クラスの修飾子にfinalをつけて継承防ぐ
public final class A {
  // インスタンスを生成されないようにprivateコンストラクを作成
  private A(){
  }

  public static String getHello() {
    return "Hello";
  }
}
```

## 最近の書き方
Spring Frameworkのクラスを読んでいるときに発見したのですが、最近の強い人たちは
abstract　をつけてStaticUtilityを作成しているようでした。
[Springソース](https://github.com/spring-projects/spring-framework/blob/master/spring-context-support/src/main/java/org/springframework/ui/freemarker/FreeMarkerTemplateUtils.java)

```java
// abstractにしてコンストラクタを書いてない
public abstract class FreeMarkerTemplateUtils {
  public static String processTemplateIntoString(Template template, Object model) 
    throws IOException, TemplateException {
      StringWriter result = new StringWriter();
      template.process(model, result);
      return result.toString();
  }
}
```

## 理由
抽象クラスの場合、インスタンスの生成ができません。そのため、privateコンストラクタを定義しなくても
インスタンスを生成される心配がないので、コーディング量が減るのだと推察します。

## 結論
abstaractだと継承されちゃうのでは？と考えたのですが、継承されたところでオーバーライドできないし、
何よりもprivateコンストラクタの手間を考えると圧倒的にabstarctの方がメリットが大きいなと思いました。
（そもそもprivateeコンストラクタ書くの忘れる時があるから、その防止にもなるし！）


どんどん強い人のソースコードを真似していきましょう！
Rubyは2つの変数の値入れ替えに別途変数が不要

## 環境

読んでる本：たのしいRuby 4版
Ruby : ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin18]

## 変数の入れ替え
プログラミングの入門書にありがちな変数の入れ替え。
Javaで書くとこんな感じのやつです。
「そのまま代入しちゃうと値が消えちゃうからtmpが必要だよね」っていうやつです。

```Java:changeval.java
public static void main(String[] args) {
    int a = 10;
    int b = 20;
    System.out.println(a); // => 10
    System.out.println(b); // => 20

    int tmp = b;
    b = a;
    a = tmp;
    System.out.println(a); // => 20
    System.out.println(b); // => 10
}
```

## Rubyだとtmp不要
Rubyだとtmpが必要ないって知って驚いたのが今回の話。
Rubyで多重代入を使って書くとこうなる。

```Ruby:changeval.rb

a = 10
b = 20
puts a  # =>10
puts b  # =>20

a, b = b, a
puts a  # =>20
puts b  # =>10

```

一瞬しか使わないとはいえ無駄な変数を作るのは面倒なので、Rubyはすげーなーって思いました。
(そもそも変数を入れ替える機会がどれだけあるのかという話は別の話)
golangのnet.Dialでアドレスにlocalhostを指定すると接続に時間がかかる場合がある

golangがクライアント、C#がサーバのプログラムを書いたときに接続に時間がかかることがあった。

```go:client(golang)
for i := 0; i < 10; i++ {
	s := time.Now()
	conn, _ := net.Dial("tcp", "localhost:8080")
	conn.Close()
	e := time.Now()
	fmt.Printf("%fsec\n", (e.Sub(s)).Seconds())
}
```

```csharp:server(C#)
var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(IPAddress.Loopback, 8080));
socket.Listen(10);
while (true)
{
    socket.Accept().Dispose();
}
```

```plaintext:実行結果
1.005335sec
1.003056sec
1.002442sec
1.002592sec
1.001638sec
1.001835sec
1.003848sec
1.003685sec
1.001684sec
1.000838sec
```

なぜこうなるか調べてみるとアドレスに`localhost`を指定すると**IPv6**と**IPv4**の両方に解決され、
**IPv6** -> **IPv4** の順で接続を試しているからだった。

なので、クライアント側を`net.Dial("tcp", "127.0.0.1:8080")`に変更するかサーバ側のソケット作成部分で`AddressFamily.InterNetworkV6`を指定、バインド時に`IPAddress.IPV6Loopback`を指定することで早くなる。

`http.Get`などを使用するときも同様のことが起こる。
マニュアルテストのエビデンス取得効率化話【vol.1】

株式会社オズビジョンのユッコ (@terra_yucco) です。

今日はプライベートな用事があってあんまりしっかりした記事がかけないので、自分がマニュアルで PHP スクリプトのテストをするとき、エビデンス取得用に使っている bash スクリプトを晒してみます。
画面が無いバッチ処理のテスト実施は、このスクリプトを作って随分簡易化されました。
※画面要件には適しません。

# エビデンス取得用スクリプト
## 要件

- そのテストがいつ行われたものか知りたい
- そのテストで実行されたコマンドが知りたい
- 事前事後で比較する必要があるものは自動的に取得しておきたい
    - DB 内容
    - ls 結果 など
- 引数違いのテストなどは同じスクリプトで対応したい


## 実コード

DB 接続先や実際の起動スクリプトはさすがに書けないのですが、こんな感じのテンプレートを用意し、テスト対象に応じて書き換えて使っています。

```bash
#!/bin/bash -u

condition="${1}"
timestamp=$( date +'%Y%m%d%H%M%S' )
base_date=$( date +'%Y-%m-%d 00:00:00' )

c_main_db="mysql --default-character-set=utf8 --verbose -h {{main_endpoint}} -u {{main_user}} -p{{main_password}} -D {{main_database}}"
c_sub_db=" mysql --default-character-set=utf8 --verbose -h {{sub_endpoint}}  -u {{sub_user}}  -p{{sub_password}}  -D {{sub_database}}"

get_evidence() {
  type="${1}"

  ${c_main_db} -e"
  status;
  set session time_zone = 'asia/tokyo'; set names 'utf8';
  select now();
  {{対象データを select する SQL, 条件に ${condition} や ${base_date} を使ったり}}
  select now();
  " > ./${timestamp}_DB_${type}_main.txt 2>&1

  ${c_sub_db} -e"
  status;
  set session time_zone = 'asia/tokyo'; set names 'utf8';
  select now();
  {{対象データを select する SQL, 条件に ${condition} や ${base_date} を使ったり}}
  select now();
  " > ./${timestamp}_DB_${type}_sub.txt 2>&1
}

get_evidence "01"

set "-x"
date
echo "file timestamp=[${timestamp}]"
/usr/bin/php {{テスト対象スクリプト}} ${condition}
echo $?
date

set "+x"

get_evidence "02"

exit 0
```

## 実行すると何ができるのか

今回は php をインストールしていない windows 上で実施したのでエラーも出ていますが、結果に示すように、タイムスタンプと事前(01)事後(02)でネーミングされたログが取得されます。
bash の内容は `set "-x"` で表示させているので、呼び出したスクリプトとその戻り値も確認可能です。
なので、これにより、

- いつ実施したテストなのか: タイムスタンプ及びスクリプト実行前後の date で確認可能
- 何をテストしたのか: 実行スクリプトで確認可能
- 事前事後でどのようにデータなどが変わったのか
- 引数で渡す `${condition}` により例えば `dollar` `yen` `euro` などのパターンをテスト可能

という要件を満たすことができます。

```terminal
owner@DESKTOP-H70OCAI MINGW64 ~/Desktop/qiita
$ pwd
/c/Users/owner/Desktop/qiita

owner@DESKTOP-H70OCAI MINGW64 ~/Desktop/qiita
$ ls -l
total 4
-rwxr-xr-x 1 owner 197609 1091 5月  14 22:52 get_evidence.sh*

owner@DESKTOP-H70OCAI MINGW64 ~/Desktop/qiita
$ bash get_evidence.sh dollar
+ date
2019年 5月 14日 火曜日 22:54:33
+ echo 'file timestamp=[20190514225433]'
file timestamp=[20190514225433]
+ /usr/bin/php '{{テスト対象スクリプト}}' dollar
get_evidence.sh: line 35: /usr/bin/php: No such file or directory
+ echo 127
127
+ date
2019年 5月 14日 火曜日 22:54:34
+ set +x

owner@DESKTOP-H70OCAI MINGW64 ~/Desktop/qiita
$ ls -l
total 8
-rw-r--r-- 1 owner 197609   51 5月  14 22:54 20190514225433_DB_01_main.txt
-rw-r--r-- 1 owner 197609   51 5月  14 22:54 20190514225433_DB_01_sub.txt
-rw-r--r-- 1 owner 197609   51 5月  14 22:54 20190514225433_DB_02_main.txt
-rw-r--r-- 1 owner 197609   51 5月  14 22:54 20190514225433_DB_02_sub.txt
-rwxr-xr-x 1 owner 197609 1091 5月  14 22:52 get_evidence.sh*

owner@DESKTOP-H70OCAI MINGW64 ~/Desktop/qiita
$
```

## 普段どう使っているか

Windows 使いな私は、スクリプトの出力があるならそれを見るのと、取得したログを WinMerge で差分確認してテスト結果を確認していることが多いです。

## Next Action

diff を取得するところまでスクリプトに組み込み、スクリプト全体の出力結果も別ファイルに書き出せば、それだけでエビデンス取得をほぼ自動化できそうだなあ…なんて考えていたりします。
※繰り返しますが、画面要件には適しません。