OmniAICreator/Qiita-1.07M huggingface dataset:
PythonのOpenCVでwebカメラが使えないときはプライバシー設定が問題かも

# はじめに
最近Windowsのデスクトップパソコンにusb接続したカメラを接続して、PythonのOpenCVで遊ぼうと思っていました。
しかしなぜかカメラを認識せず、動かないという問題がありました。
試行錯誤の結果、Windowsのプライバシーのカメラの設定がすべてオンにすることで解決しました。





# プログラム

今回使用していたプログラムは、以下のサイトのプログラムです。

https://ensekitt.hatenablog.com/entry/2017/12/19/200000

```python
# OpenCV のインポート
import cv2

# VideoCaptureのインスタンスを作成する。
# 引数でカメラを選べれる。
cap = cv2.VideoCapture(0)

while True:
    # VideoCaptureから1フレーム読み込む
    ret, frame = cap.read()

    # スクリーンショットを撮りたい関係で1/4サイズに縮小
    frame = cv2.resize(frame, (int(frame.shape[1]/4), int(frame.shape[0]/4)))
    # 加工なし画像を表示する
    cv2.imshow('Raw Frame', frame)

    # 何か処理（ここでは文字列「hogehoge」を表示する）
    edframe = frame
    cv2.putText(edframe, 'hogehoge', (0,50), cv2.FONT_HERSHEY_PLAIN, 3, (0, 255,0), 3, cv2.LINE_AA)

    # 加工済の画像を表示する
    cv2.imshow('Edited Frame', edframe)

    # キー入力を1ms待って、k が27（ESC）だったらBreakする
    k = cv2.waitKey(1)
    if k == 27:
        break

# キャプチャをリリースして、ウィンドウをすべて閉じる
cap.release()
cv2.destroyAllWindows()
```

これを実行すると、`AttributeError: 'NoneType' object has no attribute 'shape'`というエラーが出るうえ、`res`という変数が`False`となっていました。つまり画像を取得できていませんでした。

# 試したこと




## 1.PCがカメラを認識しているかの確認

winodwsの検索からデバイスマネージャーを開き、カメラの下にWebcameraがあるかを確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/993303/9409a7a5-ce97-0d7f-7cef-c31ba2b1335b.png)


## 2. カメラアプリでの確認

カメラアプリでちゃんと写っているか確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/993303/d65d9573-1e74-3e05-58c2-c77dd13ca48e.png)



## 3.プライバシー設定

設定からプライバシーの設定をします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/993303/45612847-68be-7591-850a-e9142601ea22.png)


まず、`このデバイスのカメラへのアクセスを許可する`を`オン`にします。
次に、`アプリがカメラにアクセスすできるようにする`も`オン`にします。
この時、Microsoft Storeアプリは個別にオンにする必要はありません。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/993303/a1690628-e4d6-55cb-9d5d-de0ca3d7d772.png)
1.


最後に、まず、`デスクトップアプリがカメラにアクセスできるようにする`を`オン`にします。
この状態でPythonでカメラを使用していると、画像のように`Python`が現れます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/993303/218ea7aa-b507-7aaf-8c15-079f3a083b56.png)


# まとめ

今回、カメラが使用できなかったのは、プライバシー設定でカメラのアクセスがオフになっていたからでした。
プライバシーに気を使っていると同じような問題が起きるかもしれません。
GitHub Pagesにフロントエンド自宅鯖にバックエンドを置いてみた話

# あらすじ

うーむ。何か変わったことをやりたいな・・・

ということで思いついたのが

- GitHub Pagesにフロントエンド(React/TypeScript)
- 自宅鯖にバックエンド (warp/Rust)

という構成のサービス。

基本的なアイデア:
バックエンドサービスは基本的にはGET系のAPIのみを提供し、
POST系の処理はプライベートネットワークからのみアクセス可能とする。
GETだけならトークンもいらんだろうしGitHub Pages(無料版)でもええか？

ということで、さっそく作ってみたのですが・・・

# 落とし穴1: Mixed Contentの禁止

Google Chormeでは[Mixed Content](https://www.howtogeek.com/443032/what-is-mixed-content-and-why-is-chrome-blocking-it/)が禁止されています。
これは、HTTPSで提供されているサービス中にHTTPで提供されるコンテンツが混じっているような状況のことなのですが、ここでは、次のような状況になっていました。

- GitHub PagesはTLSを使用(httpsでアクセス)
- 用意したバックエンドは80番ポート(http)で公開

当然、API呼び出しがブロックされることに。簡単な回避方法も見つからなかったのでひとまずLet's EncryptでTLSを導入することに。

[Let's EncryptでのTLSの導入について以前書いた記事](https://qiita.com/little_wolf513/items/e0dacf6835c453c043f7)

また、この時のドメインについては、いつもの無料DDNSサービス
[DDNS Now](https://ddns.kuku.lu/)さんを使わせていただきました。

# 落とし穴2: ERR_CERT_COMMON_NAME_INVALID

443ポートの解放し忘れなど初歩的なミスを犯しながらなんとか当初の目的であった
GitHub Pages上での動作確認に成功したのですが、ここで一つ問題が。

フロントエンドに変更を入れた後、デプロイ前にローカルで動作を確認したかったのですが、確認のためにブラウザを開いているマシンとバックエンドサービスをホストしているマシンのグローバルIPアドレスが同じなために、ルータのポートフォワーディングが正しく機能しないことが判明。

具体的には、ブラウザ側からURLに向けてリクエストが投げられると、
1. URLからグローバルIPアドレスにDDNSが変換
1. それを使ってルータに到達したパケットは送り先がルータ内のネットワーク
1. ポートフォワーディングの代わりにルータの設定画面を返す

という流れに入ってしまうんですね。
これは同じことはGitHub Pages上の本番フロントエンドを開いた時にも言えるのですが、こちらはひとまず置いておくとして、少なくとも開発時には通常のおうちインターネットで結果の確認ができないと困ってしまうわけです。

とはいえ、同一ネットワークにはいれるのであれば、ポートフォワーディングが機能しなくとも単にプライベートアドレスでAPIにアクセスするように開発時のコードを修正してあげれば・・・

**ERR_CERT_COMMON_NAME_INVALID**

・・・はい、ダメなんですね。証明書にはしっかりとホスト名が書いてあるので、IPアドレスでアクセスしようとすると、そこが一致しないために不正な証明書であるとしてはじかれてしまいます。

いろいろ対処法はあると思うのですが、ひとまず今回は、バックエンド側にルーティングの処理を追加して、httpでのアクセスもできるようにしました。そのうえでフロントエンド側では、開発時のみhttpで直接プライベートアドレスにAPIリクエストを投げるようにしてあげたわけですね。

# まとめ

- Mixed ContentではじかれるのでAPI側は面倒でもTLS対応しよう
    - 今はLet's Encrypt君がいるのでそんなに面倒くさくないよ
- 何らかの事情で証明書エラーをバイパスしたいときにはいっそのこと開発時のみと割り切ってhttpアクセスも許しちゃうのも手かも？

# あとがき
- ネットワーク・インフラ何もわからない日曜エンジニアなので、「セキュリティ的にお前かなり危ないことやってるよ」とかあったらこっそり教えてください

# 成果物
https://jackalltrades-mon.github.io/MyRandomScraps/
異なる種別のデータベース(Oracle / SQL Server)間でのデータ比較を行う

# 本記事の目的

2つのDBテーブル間でのデータ比較を行いたいが、比較対象のテーブルはそれぞれ異なるデータベース上にある。手間をかけずにささっと簡単な比較をするには、どうしたらよいか。

■ 使いたいデータソース
1. Oracle Server (オンプレ)
1. SQL Database (Azure)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/76f2d040-8b58-a22d-3908-c7167b4cf553.png)

いつもは「Object Browser for Oracle」と「SQL Server Management Studio」を使い分けてそれぞれのDBにアクセスしていることもあり、DBを横断して比較するとなったときに、ふと手が止まった。

何か楽にできる方法がないかと考えたら、Power BIで簡単にできることに気が付いた。
Power BIを「データコラボレーションツール」と捉えられている人にとっては当たり前だが、「データ可視化ツール」と認識していると案外こういう発想が出てこなかったりする。

# アプローチ
＜手順＞
Power BIを利用したことがある人であれば、特に迷うことはない。
1. Oracle Databaseコネクタを使ってOracleのデータを取得
2. SQL Serverデータベース コネクタを使ってSQL Databaseのデータを取得
3. PowerQueryなりDAXなりで自由に分析


### 1. Oracle Databaseコネクタを使ってOracleのデータを取得
Power BIでは様々なデータソースに対するコネクタが用意されているため、それを使えば画面操作で簡単にデータをかき集めてくることができる。ちなみに対象へのコネクタをMicrosoftが提供していない場合でも、サードパーティのコネクタを使えば接続できることもある。

Oracleの場合も、接続先の情報を入れて認証情報を入れるだけでつながる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/e35fc6f4-c43c-2c75-2d3f-65ce7c1c21f2.png)

### 2. SQL Serverデータベース コネクタを使ってSQL Databaseのデータを取得
Oracleの場合とほとんど同じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/3e1f3079-040f-6bf8-134c-70b61b868faf.png)

### 3. PowerQueryなりDAXなりで自由に分析
両方のデータを取り込んだら、あとはPower BI側の機能で分析すればよい。JOINやUNIONなど多くのことはPowerQueryでもDAXでも実現できる。PowerQueryなら画面上のマウスクリック操作だけですぐ実現できるので、データ取り込み時にPowerQuery上でやってしまえば良い。

以下はPowerQueryでのJOINとUNIONの操作。データの取り込み時に「データの変換」を選ぶとPowerQueryの画面になる。

#### JOIN
「クエリのマージ」という機能を使えば良い。どのテーブルでリレーションを貼るかを選択して、結合の種類を選択する。

ちなみに<a href='https://qiita.com/ryokita/items/31747824db1bbf534f16'>過去の記事</a>で書いたが、Power BIでは複合キーによるリレーションは使えないので、必要ならマージした列を作るなどしておく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/81482061-5bf9-4679-354b-d52db6b583c5.png)

#### UNION
「クエリの追加」という機能を使う。どのテーブルをUNOINしたいかを選択するだけ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596092/d089c42b-65ed-26e8-545f-031e73144d55.png)

DAX関数を使えば、他にも細かいことが色々とできる。例えば<a href='https://docs.microsoft.com/ja-jp/dax/except-function-dax'>EXCEPT</a>を使えば、あるテーブルの中でもう1つのテーブルに出現しない行だけ持つ差分テーブルが作れるので便利。

# まとめ
「Power BI = データの可視化ツール」という考えに囚われると頭が固くなる。Power BIはデータをかき集めてきてあれこれする「データコラボレーションツール」と考えたほうが良さそう。豊富に用意されているコネクタ機能を使えば、比較的簡単にいろいろ実現できて面白い。
Coursera機械学習入門のOctave実装で詰まってる人を救いたい。

## 主旨
現在、仕事の時間でCourseraを受講させてもらっています。
コースの中では、Octaveというプログラムを用いて機械学習のアルゴリズムを実装します。
初めてのプログラミング課題は、丸１日かかりましたが、5週目のニューラルネットワークの実装では、ノーヒントで1時間くらいで実装ができました。
独学する中で、自分なりに、得た知見をメモしておきます。
実装課題で詰まっている人のヒントなれば幸いです。

## 結論
行列の形状をめちゃくちゃ意識して下さい。
極論ですが、行列の形状だけ意識すれば、アルゴリズムをあまり理解していなくても解けることがあります。

### 演習課題PDFの公式は脳死でトレースしない。
PDFの公式が間違っているという意味ではありません。
ただ、プログラム内に存在する変数の行列の形状と、公式で扱っている行列の形状が異なることはよくあります。
そのため、脳死で公式をトレースすると、高い確率で、行列の積の演算ができなくなります。

### OctaveのGUIでプログラム変数の行列の形状を観察する。
GUIでゴリゴリにデバッグをしてください。
特に、使用変数の行列の形状はめちゃくちゃ見ます。
特に大事なのは、プログラミング課題の対象となる、関数のインプットとアウトプットの行列の形状になります。

私の場合、とりあえず動かしてみて、ほぼすべての変数の行列の形状をノートに写し、
アウトプットの行列の形状にするには、インプットをどのように計算していけばいいかを考えます。

### 行列の積と、要素ごとの積（アダマール積）を区別する。
タイトルそのままです。
自身が、どちらの実装を行いたいのか、意識してください。

### シグマを行列で行う考えかたを身に着ける。
公式でシグマがよく出てきますが、行列との直感的な関係性がわかると便利です。
```math
\Theta = \text{m×nの行列}\\
\sum_{i=1}^m\sum_{j=1}^n\Theta_{i,j} = \text{sum(sum($\Theta$)$^T$)}
```
sum()は、列方向での総和をとります(1×nになる)。
それを転置してもう一度sum()を行うことで、スカラー値にすることができます。

### 場合わけも行列で行う考え方を身に着ける。
場合分けも行列でできると気持ちいいです。
コースの中では、よくj≠0であれば、特定項を足すけれども、j=0では足さない。というような、場合分けされた公式が出てきます。
これを行列で行う考え方としては、行列の中で、j=0に該当する要素をすべて0に置き換えてしまえばよいです。
そうすれば、要素ごとの計算を行ったときに、j=0については何も足されず、それ以外は特定項が足される場合分けになります。

### ループ処理から行列へのパラダイムシフト
そもそもなんですが、Courseraでは、ループでできることを行列(ベクトル)で補うことが多いです。
ここが、普通のプログラムを書いてる人にとっては、思考を切り替えなくてはならない部分になってます。
例として、とある仮説関数に、5,000個のデータを流したい場合に、行列(ベクトル)で一気に計算します。

構造化プログラミングからオブジェクト指向プログラミング（さらには関数型プログラミング）へ切り替わるように、
ループを行列で置き換えることも、慣れが必要です。
完全に置き換える必要はないですが、身に着けられると、ちょっとだけ行列が好きになります。

## まとめ
私も初めてOctaveで実装したときは、若干キレながら実装していました。
また、GitHubにある参考プログラムでは、複雑な行列計算を行っていて、どうやったらこんな行列計算が思いつくのかと、途方に暮れていました。
しかし、「行列の形状を意識する」ことに気づいてからは、インプットからアウトプットの行列の形状に変えるため、行列の計算をパズルを解くように行っていました。
私も、まだ5週目で約半分なので、引き続き頑張ります。
AWS CLIでThe IAM Role arn:aws:iam::xxxxxxxx:role/dms-vpc-role is not configured properly

## 事象
- CLIで`AWS　DMS`を作成しようとしたら以下のエラーが発生しました。
```bash
$aws dms create-replication-subnet-group \
  --replication-subnet-group-identifier "replication-subnet-group" \
  --replication-subnet-group-description "for something" \
  --subnet-ids subnet-xxxxxxx subnet-xxxxxxxx

An error occurred (AccessDeniedFault) when calling the CreateReplicationSubnetGroup operation: The IAM Role arn:aws:iam::xxxxxxx:role/dms-vpc-role is not configured properly.
```

## 原因
- `arn:aws:iam::xxxxxxx:role/dms-vpc-role`がいないことです。


## 対応策1
- AWS Consoleからレプリケーションインスタンスを作る。
完了すると、 `arn:aws:iam::xxxxxxx:role/dms-vpc-role`が出来るので、その後はAWS CLIで作成できます。

## 対応策2
- 自前で作る

|項目|値|
|:---|:---|
|Role名|　dms-vpc-role|
|AssumeRolePolicy(信頼ポリシー)|{<br>　"Version": "2012-10-17",<br>　"Statement": [<br>　　{<br>　　　"Sid": "",<br>　　　"Effect": "Allow",<br>　　　"Principal": {<br>　　　　"Service": "dms.amazonaws.com"<br>　　　},<br>　　　"Action": "sts:AssumeRole"<br>　　}<br>　]<br>}|
|RolePolicyAttachment(許可ポリシー)|`AmazonDMSVPCManagementRole` |

## 参考
- https://stackoverflow.com/questions/58542334/aws-dms-database-migration-service-system-error-messagethe-iam-role-arnawsi
【メモ】はじめてgit initする前にやっておくべきこと

# 環境
* macOS Monterey 12.1 (M1チップ)
* git version 2.35.1


# 現象
gitコマンドをはじめて利用し、試しに何も考えず
git initしてみたところ、下記のエラーが表示された。



```
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint: 
hint: 	git config --global init.defaultBranch <name>
hint: 
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint: 
hint: 	git branch -m <name>

```


# 対応方法
下記コードを実行する。（上記エラーコード5行目の\<name>部分に mainを代入する形）
```
% git config --global init.defaultBranch main
```
Railsでxml, html などMIMEタイプごとに返すViewを指定する


mime typeによって返すviewを変更する方法です。

例えばxml, htmlへのレスポンスを別々に作成する場合

```ruby:UsersController.rb
class UsersController < ApplicationController
	def show
    @user = users.find(params[:id])
	respond_to do |format|
      # HTMLのレスポンス
      format.html
      # XMLを使う場合のレスポンス
      format.xml
	　　　　# 指定がない場合のレスポンス
	　　　　format.any
  end
```

html, xml それぞれに対応したテンプレートは以下のパスに作成します。

`app/views/users/show.html.erb`

`app/views/users/show.xml.erb`


### MIME TYPE種類

https://developer.mozilla.org/ja/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
整数値を偶数にする関数

# なんで記事に？
思い付いたのですが、調べても案外出ませんでした。
出てくるのは奇遇判定ばかり…。

折角なので書こうと思います。**絶対使うことないけど。**

*※以下のコードはすべて動作確認をしていないので、使用する際はご注意ください。**まあ使うことないと思うけど。***


# いきなりコードかい！

``` C++:C++ / 記事タイ通りです
// 整数値を偶数にする。
int toEven(
        int  a_num,
        bool a_toZero = false
) {
    // 絶対値を取得。
    int absNum = abs( a_num );
    // 奇数なら偶数にし、さらに入力値がマイナスならマイナスにした値を返す。
    return ( absNum
            + (a_toZero * -2 + 1 * (absNum % 2)) )
            * ((a_num != absNum) * -2 + 1);
}
```

**`初心者向け` タグを付けたのに競技プログラミングみたいじゃないかい！** と思いましたよね？
**失礼いたしました…。** 一応、速度が出るように、分岐を無くしたからです。速いかどうかは知らん(多分コンパイラに任せるべき)。

## てなわけで砕いた奴
``` C++:可読性はこっちの方が圧倒的に高いですよね。
int toEven(
        int  a_num,
        bool a_toZero = false
) {
    // 絶対値を取得。
    int absNum = abs( a_num ); // 入力値の絶対値
    int res    = absNum; // 戻り値

    // 奇数なら偶数にする。
    if (1 == absNum % 2) {
        // ゼロ側に丸めるなら、1を引いて偶数に。
        if (a_toZero) {
            res -= 1;
        }
        // 1を足して偶数に。
        else {
            res += 1;
        }
    } // if (1 == absNum % 2)

    // 入力値がマイナスだったのなら、
    // 出力値もマイナスにする。
    if ( a_num < 0 ) {
        res *= -1;
    }

    // 値を返す。
    return res;
}
```

やっていることは簡単で、
1. `absNum % 2` で奇遇判定をする
1. 奇数なら、 `1` 足すか引いて偶数にする
だけです。
冒頭に書いた「調べて出てくる」ものは、このリストの1番目です。
そこに、日常で数合わせ等の為に行っている、偶数にする操作を加えただけです。

# 一応関数の説明
XAML でも JavaDoc でもない形式なのでご注意を。
```
// summary: 整数値を偶数にする。
// param a_num   : もとの整数。
// param a_toZero: 0 に近い数にするか。
//     true なら 0 に近いほうの偶数にし、
//     false なら 0 から遠いほうの偶数にする。
// return: 整数の偶数値を返す。
```

# 最後に
もし何かに使う機会があったり、同じ処理をするコードを使ったことがある人は、良ければコメントに書いていってください。
この筆者は、どうやら**絶対に使わない**と思っているようなので、教えてあげてください。
<sub>ま、一応画像サイズは偶数が好ましいことが多いので、そこで使ったりしますけどね。</sub>
list内包表記とmapとgenerator式でmethodを実行した場合の速度測定結果

表記を共有します

:::note alert
@kts_h さんにご指摘を頂き、generator式を追記しました
generator式が一番高速である結果となっています
@kts_h さん、ありがとうございました
:::

## 結論
~~わずかながらmapの方が速いようです~~
generator式が1番速い結果となりました(4/13追記)
これぐらいの速度差だと、どちらでも良いかも知れませんが

:::note warn
すみません、回数は-1回してください、range(回数)で測定していました:sweat:
:::

今回測定した処理は下の通りです
make_outputではプロパティの文字列を出力用に編集してreturnするだけのシンプルなものです
変数名やメソッド名は変更しています

なお、皆様の実行する内容や実行回数により、同じ結果になるかは分かりませんので、ご承知おきください

```python:dummy.py
output = "\n".join(map(lambda value_object: value_object.make_output(), value_objects))
output = "\n".join([value_object.make_output() for value_object in value_objects])
```

## 測定結果

| 回数 | list内包表記 | map |generator|
|-----------:|:------------:|:------------:|:------------:|
|10|9.499999999995623e-06|9.042000000000772e-06|7.5419999999992715e-06|
|20|1.533299999999932e-05|1.5167000000003428e-05|1.3499999999999623e-05|
|30|2.129099999999967e-05|2.116699999999555e-05|2.012500000000278e-05|
|40|3.158400000000117e-05|2.7208000000000787e-05|2.5790999999997233e-05|
|50|3.775000000000306e-05|3.34580000000001e-05|3.16250000000004e-05|
|100|6.808299999999656e-05|6.445800000000335e-05|6.254100000000623e-05|
|200|0.00012937500000000102|0.00011662500000000214|0.00011325000000000224|
|500|0.00034287499999999943|0.0003077920000000012|0.0003053329999999979|
|1000|0.0006297919999999971|0.000612208000000003|0.0006056670000000042|

どなたかのお役に立てると嬉しく思います
JavaScriptで入力値を100万単位に変換したい時



## やりたいこと
input入力欄に3と入力されたら300万にしてAPIに送りたい。
桁数が大きいデータを扱うときにフロントでは桁数を少なく表示したい。一方でAPIに送る情報には元の数値を入れて送る


## 実装メモ
```ts 
  <input type='text' name='name'/>
```

```ts 
const getInputValue = ():string => {
 const target = document.querySelector("input[name=name]");
 return target.value;
}
```

inputで入力された値を取ってくるgetInputValue関数を作ります。
getInputValue関数の返り値はstringで返ってくる想定です。

### inputで入力された数値を100万単位にする関数
```ts
const changeToMillion = ():number => {
  const normalValue = getInputValue();
  const millionValue = parseInt(normalValue,10)*1e6;
  return millionValue;
}
````
次にgetInputValue関数で取ってきた値を、changeToMillion関数で100万単位に変換します。
`1e6`は10の6乗を意味します。
今回は100万単位にしますが、乗数を変えることで億単位等に変更することも可能です。
1e7:1000万,
1e8:1億...等
eの後の数字を変更すればさまざまな桁に対応できます。ここでのe は 10 のべき乗を表します。
ifupdownパッケージを消したバカ

消しました。さくらのVPSでした。ネットワークから切断されました。

どうしましょうか……

VPSのWeb管理画面で悩みます。NFS、マウントできないでしょう。物理デバイス、USB差しようがねえっす。

幸い、ログインは出来ます。Web画面上からシリアルコンソールでコピペも出来ます。

ifupdownのdebパッケージに依存関係はなく、 `ifupdown_0.8.35ubuntu1_amd64.deb` なるもの、これさえネットワークから切り離されたLinuxホストに送り届けられれば、きっと復活するはずです。

```
base64 ifupdown_0.8.35ubuntu1_amd64.deb | wc -c
   80701
```

はーん？コピペできて、80000文字程度とな？

よろしい、 **シリアルコンソールを経由してbase64化したdebパッケージ80701字をコピペして中で復元だ**

```python
#!/usr/bin/env python3

import sys

filename = sys.argv[1]

with open(filename) as f:
    while True:
        s = f.read(2000)
        if not s:
            break
        print(s)
        input()
```

心を無にしてコピペ。目標をセンターに入れてスイッチ……

Linux環境内ではvimで地道に文字列をふくげーん

```
$ md5sum base64.txt
a97e002f07fb56f9d7156c8923df5e95  base64.txt
```

よーし base64 でdebパッケージをバイナリに戻して `dpkg -i` して、再起動。ねんじろ！

Linuxはげんきになりました

おしまい
[php] filter_input って便利

# filter_input 使ってみた

PHP5系で書かれたコードってPHP8にすると結構、変数の未定義や値が空って事でエラー出ちゃいますよね。

こんな時、`isset` の様なチェックをしてくれて、更にバリデーションも行ってくれる `filter_input` は超便利。

今回、PHP5.6で動いてたツールのコードを PHP８で動く様にして欲しいと頼まれました。

まぁ何年も前に書かれた様で、こんな感じ↓

```php
$flg = $_GET["flg"];
$status = (!isset($flg) && $flg == '') ? 0 : $flg;

if ($status !== 0) {
    何かの処理;
}
```

当然、PHP8になると `$_GET["flg"]` が空だと、そんなキーはない！と怒られますよね。

```
Warning: Undefined array key "flg" in Your Path
```

エラー回避する為には、`$_GET["flg"]` に値が入ってるか `isset` したり、値のチェックしたりと少々面倒。


あと、

- `$_GET["flg"]` にどんな値が入るか明確でない。
- バリデーションもしていない。
- `$status` が 0 以外に処理とか曖昧すぎ。

など、この5行だけでも改善点がありあり。

## filter_input 使いましょ


確認したところ、`$_GET["flg"]` は 0 か 1 しか送られてこない事が分かったので、

```php
$range  = ['min_range' => 0, 'max_range' => 1, 'default' => 0];
$status = filter_input(INPUT_GET, 'flg', FILTER_VALIDATE_INT, ['options' => $range]);

if ($status === 1) {
    何かの処理;
}
```

これで解決！

[説明]
- filter_input :

　　HTTP通信のGETやPOSTで送信されたデータを検証したり、処理するための関数です。

[filter_input について](https://www.php.net/manual/ja/function.filter-input.php)

- FILTER_VALIDATE_INT :

　　検証フィルタ。値が整数であるかどうか検証します。

[その他の検証フィルタ](https://www.php.net/manual/ja/filter.filters.validate.php)

- オプション `min_range`, `max_range`, `default` :

　　オプションを設定する事で、取得する値の範囲と初期値が設定出来ます。

***

今回の場合、GETデータで送られて来た `flg` は　0 か 1 が送られてくる、というのが分かります。
それ以外の数値は初期値の 0 が返されます。

また、それ以外に英字や日本語など数値以外のものが送られてきても初期値の 0 を返します。

わざわざ、`is_int` とか `is_numeric` とか `0 or 1` みたいなチェックしなくてもいいですね。


# まとめ

他に用意されている [検証フィルタ](https://www.php.net/manual/ja/filter.filters.validate.php) では、ドメインかどうか、Emailかどうか、IPアドレスかどうか、などの検証も可能です。

`filter_input` 積極的に使っていきましょー。
Node.jsで使えるExcelライブラリ比較

Excelファイルを扱うライブラリが多すぎてどれを使ったらよいかわからないので・・・

# 結論

データの読み込みだけなら __xlsx__ 
Excelのテンプレートに書き込むなら __ExcelJS__ or __xlsx-populate__
ただし、ExcelJSはかなり多くのバグが放置されているので注意

|   | TypeScript | 読み込み | 書き込み | スタイル設定 | スタイル保持 | 画像挿入 | セルコメント |
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| xlsx | ○ | ○ | ○ |   |   |   | ○ |
| xlsx-js-style | ○ | ○ | ○ | ○ |   |   | ○ |
| node-xlsx | ○ | ○ | ○ |   |   |   |   |
| ExcelJS | ○ | ○ | ○ | ○ | ○ | ○ | ○ |
| excel4node |   |   | ○ | ○ |   | ○ | ○ |
| officegen |   |   | ○ |   |   |   |   |
| xlsx-populate |   | ○ | ○ | ○ | ○ |   |   |

ダウンロード数、スター数などは以下を参考に。

https://www.npmtrends.com/excel4node-vs-exceljs-vs-officegen-vs-xlsx-vs-xlsx-js-style-vs-xlsx-populate-vs-node-xlsx

---

# 調査メモ

## xlsx(SheetJS)・SheetJS Pro
https://www.npmjs.com/package/xlsx

SheetJSからxlsxに名前が変更されたライブラリ。無料版のxlsx(SheetJS)と有料版のSheetJS Proがある。
無料版では読み込んだExcelのスタイルが消える、画像挿入などができない、かなり機能が制限されている。有料のSheetJS Proであればできるらしいが、メールで問い合わせが必要らしく料金も公開されていない。無料版は実質読み込み専用と考えたほうが良いかも。

## xlsx-js-style
https://www.npmjs.com/package/xlsx-js-style

xlsxのフォーク。スタイルの設定ができるように機能追加されている。これを使っても読み込んだExcelのスタイルは消える。
スタイル機能を追加しているxlsxのフォークはほかにも[xlsx-style](https://www.npmjs.com/package/xlsx-style)、[sheetjs-style](https://www.npmjs.com/package/sheetjs-style)などがあるが、xlsx-js-styleは最新のxlsxをフォークしている。

## node-xlsx
https://www.npmjs.com/package/node-xlsx

xlsxのラッパー。データの入出力などの簡単な操作しかしないのであれば。

## ExcelJS
https://www.npmjs.com/package/exceljs

xlxsに次いでダウンロード数が多いが800近いIssueがあり、更新停止中。画像挿入に使いたかったがメソッド引数の型定義が間違っていて指定できず。多機能ではあるがバグが多そうな感じ。これなら読み込んだExcelのスタイルは消えなかった。

## excel4node
https://www.npmjs.com/package/excel4node

ファイルの生成に特化している。Excelファイルを読み込むことはできない。

## officegen
https://www.npmjs.com/package/officegen

Office Open XML形式でWord、Excel、PowerPointのファイルを生成できる。ファイルの読み込みはなし。

## xlsx-populate
https://www.npmjs.com/package/xlsx-populate

ワークブックの機能、スタイルをそのまま維持することに重点を置いているらしい。
本のISBNリストからレビューサイトのリンクをまとめて生成&開く

# 1.背景
手元にある英語学習者向け本リスト（Excel）に載ってる本のレビューを一通り読んでおきたい
使いたいサイトは「GoodReads」 https://www.goodreads.com/search 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628332/cb71424f-e7f3-d1c8-59df-4ce44dbd94b7.png)
こんな感じで100冊くらいあるのでISBNをコピペして検索窓に入れていくだけでも結構面倒だなと感じた

# 2. 作業
本のURLはhttps://www.goodreads.com/search?q=ここにISBNを入力&qid=
から飛べるということが分かったので、そうと分かれば
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2628332/62f8e3e8-2a08-e79a-a660-f3a22f8ed3fb.png)


こんな感じで機械的にリンクを製造していき、これをポコポコ押すだけで良くなったのでかなり楽になった

# 3. リンクをポコポコ押すのもめんどくさい問題
複数URLオープナーhttps://www.websiteplanet.com/ja/webtools/multiple-url/ というサイトを使って全部まとめて開くようにした

# 4. 次にやりたいこと
リンクを開いたりの作業もなくしたい（全てを1つのページに統一したい）
というわけでWebスクレイピングの勉強
SVN から GitHub への移行手順

# はじめに
今さらながら SVN で管理されているソースコードを GitHub へ移行することがあったので手順を備忘録的に記載しておきます。
細かいことは置いといて、とにかく移行したい人向けです。

# 目次
1. [作業環境](#作業環境)
1. [手順](#手順)

# 作業環境
- macOS Big Sur 11.6.3
- Homebrew 3.4.6

# 手順

1. git-svn のインストール

    ```sh
    % brew install git-svn
    ```
    ```sh
    % git-svn --version
    git-svn version 2.35.1 (svn 1.14.1)
    ```

    - [macOS Catalina 10.15](https://developer.apple.com/documentation/macos-release-notes/macos-catalina-10_15-release-notes) から SVN 関連のコマンドが非推奨になったので必要
    - すでにインストールされている場合は不要

1. GitHub に空のリポジトリを作成
    - README.md とか何か一つでもファイルが存在してると push するときエラーになるので `空` で作成すること

1. ローカルに作業用のディレクトリ作成
    ```sh
    % mkdir svn-github-test
    % cd svn-github-test
    ```

    - 移行が完了したら作業用ディレクトリは削除しても問題ない

1. SVN リポジトリを git-svn で clone

    ```sh
    % git-svn clone [SVNリポジトリのURL]
    ```

    - パスワード求められたら入力
    - もし途中で止まったら clone してきたリポジトリに `cd` して `git-svn fetch` を実行すると続きからやってくれる
    - `git-svn fetch` しても何も出なくなれば clone 完了

1. clone してきたリポジトリに cd

    ```sh
    % cd [リポジトリのディレクトリ]
    ```

1. clone してきたリポジトリと GitHub の空のリポジトリを紐付ける

    ```sh
    % git remote add origin git@github.com:[ユーザー名]/[リポジトリ名].git
    ```

1. 紐付けできたか確認

    ```sh
    % cat .git/config
    [core]
	    repositoryformatversion = 0
	    filemode = true
	    bare = false
	    logallrefupdates = true
    	ignorecase = true
    	precomposeunicode = true
    [svn-remote "svn"]
    	url = [SVNリポジトリのURL]
    	fetch = :refs/remotes/git-svn
    [remote "origin"]
    	url = git@github.com:[ユーザー名]/[リポジトリ名].git
    	fetch = +refs/heads/*:refs/remotes/origin/*
    ```

    - `[remote "origin"]` が追加されてば OK

1. GitHub へ push

    ```sh
    % git push -u origin master
    ```

1. GitHub のリポジトリにプッシュされたか確認

以上
git運用、developのオリジンをひっぱってくる

# gitのブランチの基本運用
develop → featureで作業して
feature → develop → main

developからfeatureブランチを切るときはリモートのorigin/developから切る。
mainからdevelopにcheckoutするときに最新のdevelopをひっぱってくるコマンド
`git checkout -f develop origin/develop`
左右に余白があって均等配置の横並びサムネリストを作る

左右のマージン10pxで、画像間のマージン5pxのサムネが並んだ枠を作成した。
演算がややこしくて忘れそうなのでメモ。

ulに左右10pxのマージン、liは5pxずつ隙間がある。
5個横並びを固定で、画像のサイズは画面幅に応じて可変とする。
imageの縦横比は3:4。
縦横比の異なる画像は、枠内に納める。

![image 66 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/693304/02332413-956c-9437-9a17-60f30478b420.png)


## HTML
```
<div>
  <ul>
    <li>
      <a href="#">
        <img src="https://placehold.jp/150x150.png" alt="正方形">
      </a>
    </li>
    <li>
      <a href="#">
        <img src="https://placehold.jp/300x500.png" alt="縦長">
      </a>
    </li>
    <li>
      <a href="#">
        <img src="https://placehold.jp/500x150.png" alt="横長">
      </a>
    </li>
    <li>
      <a href="#">
        <img src="https://placehold.jp/400x300.png" alt="4:3">
      </a>
    </li>
    <li>
      <a href="#">
        <img src="https://placehold.jp/300x500.png" alt="縦長">
      </a>
    </li>
    <li>
      <a href="#">
        <img src="https://placehold.jp/500x150.png" alt="横長">
      </a>
    </li>
  </ul>
</div>
```
## css
```
// ここはリセット
ul, li {
  list-style: none;
  margin:0;
  padding:0;
}
// ここまでリセット


div {
  background: #3c3c3e;
}

ul {
    display: flex;
    align-items: flex-start;
    flex-wrap: wrap;
    margin: 0 10px;
}

li{
      margin: 0 0 5px 5px;
    width: calc((100vw - 40px) / 5  );
    height: calc( ((100vw - 40px) / 5) * (3/4) );
    overflow: hidden;

    &:first-child,
    &:nth-child(6n)  {
        margin-left: 0;
    } 
}

a {
    position: relative;
    display: block;
    padding: 1px;
    overflow: hidden;
    background: rgba(255, 255, 255, 0.1);
    width: 100%;
    height: 100%;
}

img {
    display: block;
    width: calc((100vw - 40px) / 5 );
    height: calc( ((100vw - 40px) / 5) * (3/4) );
    object-fit: contain;
    aspect-ratio: 4/3;
}

```
Laravel環境にPHP_CodeSnifferを導入する

# 概要

- Laravel環境にてPHP_CodeSnifferを導入する方法をメモ的にまとめる。

# 詳細

- PHP_CodeSnifferにてPSR-12に準拠しているかどうかチェックする。
- 筆者はDocker環境でPHP_CodeSnifferを導入する。
    - `/var/www/html`直下に直接laravelの構成ファイル（.envやconfigディレクトリ、appディレクトリ等が存在しているものとする。）

# 方法

1. PHPのコンテナにログインしてドキュメントルートまで移動する。
1. 下記コマンドを実行して当該のライブラリをインストールする。（ローカル環境にのみ入れたいので `--dev`オプション付与）

    ```terminal
    $ composer require --dev squizlabs/php_codesniffer
    ```

1. 下記コマンドを実行して使用する事のできるコーディング規約を確認する。

    ```terminal
    $ /var/www/html/vendor/bin/phpcs -i
    ```

1. 下記コマンドを実行してCodeSniffer用の設定ファイルを作成して開く。

    ```terminal
    $ vi /var/www/html/phpcs.xml
    ```

1. 下記の内容を記載する。（設定用のxmlの記載内容は公式がこちらにまとめてくれている → [https://github.com/squizlabs/PHP_CodeSniffer/wiki/Annotated-Ruleset](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Annotated-Ruleset)）

    ```/var/www/html/phpcs.xml
    <?xml version="1.0"?>
    <ruleset name="PSR12/Laravel">
        <description>PSR12 compliant rules and settings for Laravel</description>
    
        <arg name="extensions" value="php" />
    
        <!-- コーディング規約指定 -->
        <rule ref="PSR12" />
    
        <arg name="colors" />
    
        <arg value="ps" />
    
        <!-- 除外ディレクトリ設定 -->
        <exclude-pattern>/bootstrap/</exclude-pattern>
        <exclude-pattern>/node_modules/</exclude-pattern>
        <exclude-pattern>/public/</exclude-pattern>
        <exclude-pattern>/resources/</exclude-pattern>
        <exclude-pattern>/storage/</exclude-pattern>
        <exclude-pattern>/vendor/</exclude-pattern>
        <exclude-pattern>/server.php</exclude-pattern>
        <exclude-pattern>/app/Console/Kernel.php</exclude-pattern>
        <exclude-pattern>/tests/CreatesApplication.php</exclude-pattern>
    </ruleset>
    ```

1. 下記コマンドを実行してCodeSnifferが実行できることを確認する。（コーディング規約に準拠していない部分を出力してくれるコマンド）

    ```terminal
    $ ./vendor/bin/phpcs --standard=phpcs.xml ./ 
    ```

1. コマンドが長いのでcomposer.jsonのscriptに記載してcomposerコマンドでショートカットできるようにする。（`./vendor/bin/phpcbf`の方はCodeSnifferが可能な限り自動修正してくれる。自動で修正できないもの（関数名等依存度が高いもの）もあるのでその場合、手動で直す必要がある。）

    ```/var/www/html/composer.json
    "scripts": {
        "sniffer": [
            "./vendor/bin/phpcs --standard=phpcs.xml ./"
        ],
        "sniffer-rewrite": [
            "./vendor/bin/phpcbf --standard=phpcs.xml ./"
        ]
    },
    ```

1. 下記コマンドで準拠していない部分のチェックができるようになった。

    ```terminal
    $ composer sniffer
    ```

1. 下記コマンドで準拠していない部分を可能な限り自動修正してくれるようになった。

    ```terminal
    $ composer sniffer-rewrite
    ```
Vue CLI export default と import を使用したデータの受け渡しについて



**motodata.vue**

```javascript

const getUrl = location.pathname;
const urlArr = getUrl.split('/'); //  ['', 'admin', 'update', 'genre', '36', ''] 配列の数が6個ある
const arrCount = urlArr.length;
export default {
  ac: arrCount,
  name: 'GenreForm',
  mixins: [cf],
  props: [
    'data',
  ],
  components: {
  },
  computed: {

    },
  },
  methods: {
     },
  },
};
```

上記motodata.vueで定義した定数```arrCount```を別ファイルで使用したい時は、上記の様にexport defaultを使用する。
そして以下のsyutoku.vue使用したいファイルでimportする

**syutoku.vue**
```javascript


<scirpt>
import このファイルで扱う名前（任意） from '指定ファイルまでのパス/motodata.vue';
例）
import ac from '@/views/components/motodata.vue';

</script>
```
上記により、**ac**という値にexportした値が格納される。

```javascript

export default {
  name: 'hoge',
  components: {
      },
  data() {
    return {
      alldata: updateData, // exportした値をオブジェクトで全て取得出来る。
      ac: updateData.ac, // エクスポートしたデータのacという部分のみ取得出来る
    };
  },
  created() {
  },
  computed: {
  },
  methods: {
      async submit() {
      console.log(this.alldata); //データを取得する際はthisを使う
　　　　　　　　　　　　console.log(this.alldata); //データを取得する際はthisを使う

    },
  },
};
```
MIDIキーボードでFF14

<iframe width="560" height="400" src="https://www.youtube.com/embed/ns0lo12Lavw" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

https://www.youtube.com/watch?v=ns0lo12Lavw
実行ファイルは概要欄にて

```js
var key = {

	//白鍵キー設定
	C3:'1',D3:'2',E3:'3',F3:'4',G3:'5',A3:'6',B3:'7',
	C4:'8',D4:'9',E4:'0',F4:'-',G4:'=',A4:'Backspace',B4:'Delete',C5:'Escape',
	//黒鍵キー設定
	'C#3':'Shift+1',
	'D#3':'Shift+2',
	'F#3':'Shift+3',
	'G#3':'Shift+4',
	'A#3':'Shift+5',
	'C#4':'Shift+6',
	'D#4':'Shift+7',
	'F#4':'Shift+8',
	'G#4':'Shift+9',
	'A#4':'Shift+0',

}

//Keys 列挙型
//https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.forms.keys
//http://liliumrubellum.blog10.fc2.com/blog-entry-278.html
var WindowsKeys = {
	'`':'Oemtilde','-':'OemMinus','=':'Oemplus',Backspace:'Back',
	1:'D1',2:'D2',3:'D3',4:'D4',5:'D5',6:'D6',7:'D7',8:'D8',9:'D9',0:'D0',
	'[':'OemOpenBrackets',']':'OemCloseBrackets','\\':'OemPipe',
	';':'OemSemicolon',"'":'OemQuotes',
	Shift:'ShiftKey',',':'Oemcomma','.':'OemPeriod','/':'OemQuestion',
	Control:'ControlKey',
	NUM0:'NumPad0',NUM1:'NumPad1',NUM2:'NumPad2',NUM3:'NumPad3',NUM4:'NumPad4',
	NUM5:'NumPad5',NUM6:'NumPad6',NUM7:'NumPad7',NUM8:'NumPad8',NUM9:'NumPad9',
	Esc:'Escape',Ctrl:'ControlKey',//FF14表記
}

addEventListener('WebBridgeLoaded',e=>{
	_exe = e.detail
	//_exe.openDeveloperTools()
	console.log(_exe)
	function input(type,a){
		var s = a.shift()
		_exe.dispatch({type:type,key:WindowsKeys[s]||s})
		if(a.length>0) var id = setInterval(e=>{
			if(a.length>0){
				var s = a.shift()
				_exe.dispatch({type:type,key:WindowsKeys[s]||s})
			}else{
				clearInterval(id)
			}
		},10)
	}
	var listener = {
		NoteOn	: a=>input('keydown',a),
		NoteOff	: a=>input('keyup',a.reverse()),
	}
	new MIDIKeyboard().message(e=>{
		var k = key[e.key]
		var s = e.key.replace('#','Sharp')
		if(k) listener[e.type](k.split('+'))
		if(e.type=='NoteOn'){
			$('#MIDI').append($('<li>').addClass(s).append(
				`${e.name} ${e.key} = ${k||'none'}`
			))
		}else{
			$('#MIDI li.'+s).remove()
		}
	}).status(e=>{
		$('#MIDI').append($('<li>').append(e.type+': '+e.name))
	})
	$('body').append($('<ul id="MIDI">'))
})

class MIDIKeyboard{
	constructor(){
		this.notes	= 'C,C#,D,D#,E,F,F#,G,G#,A,A#,B'.split(',')
		this.type	= {0x80:'NoteOff',0x90:'NoteOn'}
		this.status(e=>{}).message(e=>{})
		navigator.requestMIDIAccess().then(r=>{
			r.inputs.forEach(v=>{
				this.onstatus({type:v.state,name:`${v.name} / ${v.manufacturer}`})
				this.initInput(v)
			})
		},console.log)
	}
	status(f){	this.onstatus	= f;return this}
	message(f){	this.onmessage	= f;return this}
	initInput(v){
		v.onmidimessage = e=>{
			if(this.type[e.data[0]]){
				var [type,noteNumber,velocity] = e.data
				var n = this.notes[noteNumber%12]
				var o = Math.floor(noteNumber/12)-1
				this.onmessage({
					type	: this.type[type],
					key		: n+o,
					keyCode	: noteNumber,
					note	: n,
					octave	: o,
					name	: v.name,
				})
			}
		}
	}
}
```
初学者AWS CLF合格体験記

## 業務経験0の初学者がAWS CLFに合格するまで

### 開始時の状態
- プログラミング経験、エンジニア業界の就労経験0
- 他の技術系資格未所持
- PCの自作、設定ができる程度の知識あり（ITパスポートの内容と同程度は頭に入ってるぐらい）

### 学習ツール
- [参考書](https://www.amazon.co.jp/AWS%E8%AA%8D%E5%AE%9A%E8%B3%87%E6%A0%BC%E8%A9%A6%E9%A8%93%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88-AWS%E8%AA%8D%E5%AE%9A-%E3%82%AF%E3%83%A9%E3%82%A6%E3%83%89%E3%83%97%E3%83%A9%E3%82%AF%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%BC-%E5%B1%B1%E4%B8%8B-%E5%85%89%E6%B4%8B/dp/4797397403)
- [AWS Cloud Practitioner Essentials](https://aws.amazon.com/jp/training/learn-about/cloud-practitioner/?th=tile&tile=learnabout)
- [Udemy 問題集](https://www.udemy.com/course/aws-4260/)
- [Koiwa Club](https://aws.koiwaclub.com/)
- オンラインエンジニアスクール
- Google検索

### 学習ツールの感想
参考書は情報量が足りないため、いつでも見れる復習用と割り切ったほうが良いかも。
説明が分かりやすく、理解しやすいのはAWS Cloud Practitioner Essentialsの動画です。
書籍の用語についていけない人は動画からスタートすることをお勧めします。
また、自分はスクール講師に質問することでインプットをカバーしましたが、
検索力、咀嚼力に自信のある方はGoogleで代用可能です。（少なくともCLF時点では。）
Koiwa Club、Udemy問題集はとても優秀で、問題数も多いです。
Udemyにハンズオンも含まれた講座があり情報量も多そうなのでこちらもお勧めできます。

### 実際の学習フローと結果
参考書>Essentials動画>Koiwa club>AWSハンズオン学習(スクール内課題)>Udemy問題集
以降WEB検索と問題集繰り返し

学習期間：8週間
取得点数：820点

1週間の平均学習時間数は25時間くらいです。
自身では4~6週間を目指していましたが、やはり完全初学、仕事の都合などもあり2か月程でした。

### 学習を終えた上でのポイント
- 学習開始はEssentials動画かUdemy講座推奨。
- VPC作成～EC2でWEBサーバー構築のハンズオンをやっておくと良い。
- 問題集初回は調べながら不明な情報をつぶしていくのがオススメ。
- メモアプリやスクショで効率的に復習できると捗る。
- 問題集を2～3週し、模擬試験や模擬試験形式問題集で80%くらい正答が合格目安。

自分は慎重に挑んだため、もう2週ほどは縮められたかな、と考えています。

### まとめ
- 完全初学者はEssentials見るべき。
- 効率よくやれば1か月～1か月半で取得可能
Cloud9でRuby on Rails開発環境構築（github連携も）

**自分用メモ**

## 手順

### 1. Cloud9のセットアップ

1. AWSコンソールへログイン（アカウントを持っていない場合はアカウント作成）
2. AWSコンソールで「cloud9」で検索し、colud9のページへ移動
3. 「Create environment」を押下（オレンジ色のボタン）
4. サーバ作成
    ・name：任意
    ・description：任意・空欄で問題ない
    ・「next step」を押下（オレンジ色のボタン）
5. 各種設定
    ・デフォルトで無料枠になっているので、そのままでOK
6. 確認画面で「Create environment」を押下
7. しばらく待機すると、Cloud9の画面が開き準備完了

### 2. Railsのインストールおよびプロジェクト作成（ターミナルで下記を順番に実行）

1. 「gem install rails -v 5.2.3」 → rails5.2.3のインストール
2. 「rails -v」 → インストールができたことの確認
3. 「rails new プロジェクト名」 → プロジェクトの作成
4. 「cd プロジェクト名」 → プロジェクトのディレクトリへ移動

### 3. githubと連携

1. 「git init」 → リポジトリに登録
2. 「git add -A」 → すべてのファイルをステージング
3. 「git commit -m "コミットコメント"」 → 変更をコミット
4. 「git branch -M main」
5. リモートリポジトリ（github）の準備
  ・github上でリポジトリ作成（New Repository）
6. SSH認証を行う
   1. Cloud9のコンソールで「ssh-keygen -t rsa -C "[リモートレポジトリを登録した自分のメールアドレス(・・・@gmail.comなど)]"」 → 公開鍵の作成
   2. Cloud9のコンソールで「cat ~/.ssh/id_rsa.pub」 → 公開鍵の取得
   3. 表示された公開鍵をコピーしておく
   4. githubで「Settings」→「SSH and GPG keys」→「New SSH key」
   5. Titleはなんでもいい（Cloud9とか）
   6. Keyにコピーした公開鍵を貼り付ける
7. githubにpush
   1. 作成したリポジトリのURLをコピー（SSHの方）
   2. Cloud9のコンソールで「git push git@github.com:〜から始まるSSHProtcolURL main 」 → リモートリポジトリへ変更を反映
8. Cloud9上でSSHProtocolURLを登録
  ・「git remote add origin git@github.com:〜省略〜」

## 今後githubへpushする時の流れ

1. 「git add -A」
2. 「git commit -m "コメント"」
3. 「git push origin main」

## 参考

- <https://qiita.com/vonxar/items/995cbb0b576febad9994>
- <https://qiita.com/tttaamm12/items/0f30295301a5250822dd>
- <https://qiita.com/chiaki-kjwr/items/118a5b3237c78d720582>
- <https://deepblue-ts.co.jp/git/git-push-error/>
Vuetifyのコンポーネントのメソッドを使うときの$refsの型定義[TypeScript]

# 前提
ESlint使ってます
クラススタイルで書いてます
Vue2

# refの型

Vuetifyのv-calendarのprev()とnext()の関数を使おうと$refsを使用したら

`Unsafe call of an 'any' typed value.`

というエラーが出てしまった。

↓ エラーが出たコード（読み込みとか諸々省略）
```
<template>
  <v-calendar ref="calendar"></v-calendar>
</template>

<script>
@Ref()
public calendar!: any; 

public prev(): void {
  this.calendar.prev();
}
</script>
```

any型を使うとESlintに怒られてしまう。
そこで以下のように修正することでエラーを回避しました。

```
<template>
  <v-calendar ref="calendar"></v-calendar>
</template>

<script>
@Ref()
public calendar!: Vue & { prev: () => void; }; 

public prev(): void {
  this.calendar.prev();
}
</script>
```

v-calendarのprev()の戻り値の型がvoidになってるのでそれを指定しました。

# 参考
以下のリポジトリのやり方を参考にしました。

https://gist.github.com/Nicklas2751/da1fbe81878df7bb531fa4978c0ce7a8
「Rails」+「Heroku」+「Docker」+「CircleCI」を学ぶためのおすすめの教材

# ✅こんな人におすすめ
- ローカルで「Rails」を開発したことがあり、Dockerを用いて開発してみたい方
- 「Rails」で開発したアプリを「Heroku」にデプロイしたことがあり、「CircleCI」について興味がある方
- 「Docker」も「CircleCI」も聞いたことがあり、少し勉強してみたが、いまいち理解できなかった
- あまりお金をかけず、入りだけでも理解したい方

# ✅「Docker」で「Rails」アプリを開発
https://youtu.be/ltDdZAJli8c
# ✅「Docker」の「Rails」で開発したアプリをHerokuにデプロイ
https://youtu.be/uQf9968RWWo
# ✅「CircleCI」を使用して、「Docker」の「Rails」で開発したアプリを「Heroku」に自動デプロイ / 自動テスト
https://youtu.be/bmXoupfX_r0
STM32G031のI2CスレーブモードでMCP9701温度センサーの値をマスターで受信する。


x MCP9701-E/TO 販売コード 103199

目的
I2Cのテスト
金銭的には、なんのメリットもない。おとなしくI2C温度センサーを買った方が得
I2C間通信の応用



![o_con316.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/9a62809b-247a-c3a7-6954-d37361606efc.jpeg)


![o_con320.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/a2e659a0-2a06-86c5-052b-1c8a44be1ebf.jpeg)




スレーブのプログラム


```c++


//i2c_slave_9701_031_1


#include <Arduino.h>
#include <Wire.h>


//10の割り算 0から1028までは、正しい。主に0から999
#define DIV10(n) ((n*205)>>11)


byte b=0;


void setup() {

  //I2Cの初期化
  Wire.begin(PA12, PA11); //stm32g031
  
  Wire.begin(8);// Slave ID #8
  Wire.onRequest(requestEvent);

}//setup


void loop() {
}//loop


void requestEvent() {

  Wire.write(b);

  analogReadResolution(12); //adc 12bit mode
  //int s = analogRead(A3); // PA11 PIN5 031
  int s = analogRead(A9); // PB7 PIN1

  //電圧を温度に変換 ex 20.0 -> 200 温度の十倍を出力
  s = ((s * 1692) >> 12) - 205;
  //s = 123; //debug

  //小数点以上と小数点以下を分ける
  b = DIV10(s);  // 小数点以上

}//requestEvent





```


マスターのプログラム


```c++



//I2C_Master_ino_031_1

#include <Arduino.h>
#include <Wire.h>

//10の割り算 0から1028までは、正しい。主に0から999
#define DIV10(n) ((n*205)>>11)

#define in7      PA0  // 4pin

#define DW   digitalWrite

#define UART_DELAY 102   //  9600bps ok 031


//仮想シリアルへの一文字出力 9600bps
int pc_putc(char ch) {

  DW(in7, HIGH);

  DW(in7, LOW);//START
  delayMicroseconds(UART_DELAY);

  for (int ii = 0; ii < 8; ii++) {
    DW(in7, (ch >> ii) & 1  );
    delayMicroseconds(UART_DELAY);
  }//for

  DW(in7, HIGH);//Stop
  delayMicroseconds(UART_DELAY);

  return (0);

}//pc_putc


//文字列の表示
int pc_printf(char *str1) {

  //文字の中身がゼロか
  while (*str1) {

    //一文字出力
    pc_putc(*str1 ++);

  } //while

  //戻り値
  return (0);

}//pc_printf


//初期化
void setup()
{

  //ポートをhiにする 初期化
  pinMode(in7, OUTPUT);
  DW(in7, HIGH);

  //I2Cの初期化
  Wire.begin(PA12, PA11); //stm32g031

} //setup


//メインループ
void loop()
{

  //読み込み
  Wire.requestFrom(8, 1);

  char data_read[8]; //バッファー

  while(Wire.available())  {    // 要求より短いデータが来る可能性あり
    unsigned char b = Wire.read();       // 1バイトを受信

    //表示
    data_read[5] = 0;
    data_read[4] = '\n';
    data_read[3] = '\r';
    data_read[2] = '0' + (  b - (DIV10(b) * 10)  );  // '0'+(b%10)
    b = DIV10(b);
    data_read[1] = '0' + (  b - (DIV10(b) * 10)  );  // '0'+(b%10)
    data_read[0] = '0' +  DIV10(b);                  // '0'+(s/10)
    pc_printf(data_read); //031

  }//while

  //1秒の待ち
  delay(1000);

} //loop






```
ディレクトリ名の大文字/小文字を変更してもGitに反映されない件

## ディレクトリ名の大文字/小文字を変更してもGitに反映されない件
すでにgitに上げたディレクトリ名を変更したい場合、ローカルのディレクトリの文字を変更してもgitの差分に反映しません。
(※Gitはデフォルトでファイル名の大文字・小文字の変更は検知しない設定とのこと)

なので、、
```terminal
git config core.ignorecase false
```
これでGitの設定を変えて[case sensitive]にしておく、これによって変更差分がgit addされるが、そのままpushして、gitのリポジトリをみてみると


> 修正前のディレクトリ：atoms/Input/index.tsx

が消えずに残ったまま、、、

> 修正後のディレクトリ：atoms/input/index.tsx
>
が追加されており、修正前、修正後の分がそれぞれできた状態になっています。
キャッシュか？
と疑い、、

```terminal
git rm -r --cached .
```

これでキャッシュをクリアして、（膨大な量のキャッシュクリア分がgit addされるので）

今回削除したいファイルだけをコミットしてpushしたら古いファイルがgitから消えました。
【PHP】数値じゃない要素にintvalをかけるとエラーが出ると思っていた

こういう処理を書いた時、数値じゃない文字は整数に出来ないからエラーを吐くんじゃないかと思ってたんですが、

```php:
$hoge = ['あ','い','う'];
$huga = null;
try {
  $huga = array_map('intval', $hoge);
} catch (\Throwable $exception) {
  dd($exception);
}
dd($huga);
```

出力結果、エラーになりませんでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/282328/50e09e3f-d197-8997-90ca-ce40a24716e1.png)



[PHP公式ドキュメント:intval](https://www.php.net/manual/ja/function.intval.php)
> 成功時は value の整数値、失敗時は 0。 空の配列の場合は 0、空でない配列の場合は 1 を返します。

#
数値以外の文字列が入っていないか検知したければis_numericとかを使ってチェックしましょうという事ですね

　
[PHP公式ドキュメント:is_numeric](https://www.php.net/manual/ja/function.is-numeric.php)
> value が数値または 数値形式の文字列 である場合に true、それ以外の場合に false を返します。

#
```php:
$hoge = (int)'hoge';
```
のようにキャストした場合も``0``で出力されたので少しびっくり、言語仕様あまり知らないまま使っていたのでちょっと怖いです
javascriptでの正規表現を用いて文字列を抽出する方法

業務でjsから文字列を抽出する作業を何度か行った為、
忘備録を残しておく。

例：
```javascript:ex.js
var a = "抽出元の文字列";
var b = a.match(/正規表現/);
console.log(b);//正規表現にマッチする文が出力
```

グルーピングを用いることも可能
阿部寛のホームページから「abehiroshi」のみを抽出する場合
```javascript:abe.js
var url = "http://abehiroshi.la.coocan.jp/";
var i = url.match(/(abe.*?)\./);
console.log(i[1]);//abehiroshi
```
【🔴エラー対応】CircleCiでの自動テスト時に、「Rails」+「Docker」環境でエラーになる【自動デプロイ】

# ✅当方の環境
- 開発環境 : Docker
- 本番環境 : heroku
- Gemfileの内容
  - ruby 2.7.5
  - rails 6.1.5
  - webpacker 5.0

# ✅関連キーワード
- 「Docker」+「Rails」+「CircleCi」の環境
- CircleCiにおける自動テスト時にエラー（ビルド時はエラーが出ていない）
- 「bundle exec rails db:migrate」でエラーが発生している模様
- Run `bundle install` to install missing gems.
- bundle installされない

# ✅エラー内容
- CircleCiのログの内容※一部抜粋
```
#!/bin/bash -eo pipefail
bundle exec rails db:migrate
Could not find rake-13.0.6 in any of the sources
Run `bundle install` to install missing gems.

Exited with code exit status 1
CircleCI received exit code 1
```

# ✅エラー箇所
- 「.circleci\config.yml」に記載している「test」のrunコマンドの実行箇所
```言語:.circleci\config.yml
＜中略＞
      - run:
          name: Database setup
          command: bundle exec rails db:migrate
＜中略＞
```


# ✅エラー原因
- どうやら、CircleCI側で用意しているDockerのRUBYのイメージが、2021/12/31以降から新しいものに変更されたたため。
- 「.circleci\config.yml」に記載しているDockerのイメージは、古いのもらしい（Rubyのバージョンとは無関係）

# ✅エラー対応
- 「.circleci\config.yml」に記載しているDockerのイメージを、2021/12/31以降の新しいイメージに修正する
```diff_ruby
＜中略＞
  test:
    docker:
-      - image: circleci/ruby:2.7 #変更前
+      - image: cimg/ruby:2.7     #変更後
       - image: circleci/mysql:5.5
＜中略＞
```
:::note alert
- そのほかのDockerイメージのもの(Rubyのみ)もすべて変更する
- Mysqlなどの別の種類は修正してはいけない（修正したらbuildでエラーが出ます）
:::



# ✅エラー参考資料
- [公式ドキュメント](https://circleci.com/docs/ja/2.0/circleci-images)
- [【CircleCI】bundle installされない場合の対処方法](https://jpdebug.com/p/2817794)
Adobe Acrobat Reader DCで手のひらツールをデフォルトにするのに格闘した話

# 目的、背景など
タイトル通りです。
「Adobe Acrobat reader dc 手のひらツール デフォルト」でググったら分かりますが、Adobe Acrobat reader dcをインストール直後だと「選択ツール」がデフォルトの設定になっているのですが、これを「手のひら」を起動時に設定するのに、中々どうしてクリティカルな解決記事がありませんでした。
需要があるかは分かりませんが、記事として残しておこうと思います。

# 作業環境
・OS：Windows10 64bit
・Adobe Acrobat Reader DCバージョン：2022.001.20085

# やりたいこと
これが起動時のデフォルトになっているのですが、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394613/26ebd517-4289-2191-56f9-5fc3d9b34bdd.png)

この状態をデフォルトにしたいわけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394613/cc4d2e3d-cc5f-fb83-730e-577727d68b81.png)

# レジストリ編集
まさかレジストリを編集することになるとは思いませんでした。
インストール直後だと以下のキーの値が**Select**になっているかと思います。
コンピューター\HKEY_CURRENT_USER\SOFTWARE\Adobe\Acrobat Reader\DC\Selection\aDefaultSelect
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394613/bf74c2d8-e756-516a-a1dd-998fc0be3f66.png)
これを**Hand**に変えてあげます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/394613/c07ef74b-60b9-3f37-a9ff-d1396cb75b5b.png)

自分のマシンの場合は再起動不要で即時適用されていました。

# 末筆
以上になります。
上記以外にも「終了時の設定を復元する」みたいな詳細設定もあるんですが、中々思うような動きをしてくれませんでした。すべてのPC・バージョンで同じ挙動になるかは定かではありませんが、解決の糸口の一つになればと思います。
もっとも、ここまでこだわる方がどれだけいるかって話でもあるのですがね(^^;)


# 追記　2024/06/06
ふと過去に投稿した当記事を眺めていた時のことです。
qiita内でいいねは付かないのですが、思いのほか閲覧数の伸びはすごくいいんですよね。やっぱり気にしている人いるんだなと思って、改めて「Adobe Acrobat reader dc 手のひらツール デフォルト」でググったところ、Adobe公式のコミュニティで以下の投稿がされていました。

https://community.adobe.com/t5/acrobat%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A9%E3%83%A0-discussions/acrobat-%E3%81%BE%E3%81%9F%E3%81%AF-reader-%E3%81%AB%E3%81%A6%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88%E3%81%A7%E6%89%8B%E3%81%AE%E3%81%B2%E3%82%89%E3%83%84%E3%83%BC%E3%83%AB%E3%82%92%E5%88%A9%E7%94%A8%E3%81%97%E3%81%9F%E3%81%84%E5%A0%B4%E5%90%88%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6/m-p/13181268?profile.language=ja

この記事を投稿してから半年後に投稿された公式のガイダンスです。
多分こっちのほうが参考になります。
AlmaLinux 8 WSLをインストールしてみた

# はじめに

Windows 10 に、AlmaLinux 8 WSLがインストールできるようになりました。
備忘録です。

# 環境
- Windows 10 Pro (64bit)

# インストール

Microsoft Storeからインストールします。
`almalinux` で検索すると簡単に見つかります。
「IARC 3+」なので3歳児でも大丈夫です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145578/bf0b8b84-324a-3531-2aef-0d68b1302fc0.png)

[入手]ボタンをクリックすると、インストールされます。数分で終わります。
インストールが終わると、[開く]ボタンに変わるので、クリック。
インストールの続きが実行されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145578/3d24adb9-70df-7793-7080-b5a0b975dc72.png)

インストール完了後、ユーザを作成しろ、と。
Windowsのアカウントとは別のユーザを作る必要があるようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145578/0cfe476c-8401-b37b-cbe5-7e8e355d2f51.png)

インストールはこれで終わり。

# rootユーザのパスワード変更

インストール時に作成したユーザで、`sudo passwd root` を実行することで、パスワード変更可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145578/4aa05b22-ba90-1f28-f834-a5f5e32a1639.png)

`su -`でスイッチ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145578/86395399-fbc1-e320-a8f3-0a41a314c78d.png)

# バージョン情報
AlmaLinux 8.5でした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145578/e9452c7c-0ef0-b1df-f684-b3b57edbbfd7.png)

kernelはMicrosoftの手が入っているようですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/145578/2a2bef94-2591-e954-2b40-97beccf8124c.png)
正規表現サンプルリスト

# overview
- 正規表現のトレーニングのために実際のサンプルとコードをここに記す
- 主にURIのパターンマッチで使うのでサンプルは全てURI


# get started!

## 特定の文字列に完全一致
- `^https://www.google.com$`
   - `^` 開始の特殊文字
   - `$` 終了の特殊文字

```diff
+ https://www.google.com
- https://www.google.coma
- https://www.goole.coma
```

## googleを含む文字列のみマッチ
- .*google.*
   - `.*` 任意の文字が１つ以上続く

```diff
- https://www.google.com
- https://www.google.coma
+ https://www.goole.coma
- google.caa
- aaagoogle
+ goole.caa
- https://www.googlecom
```
本日のRuby基礎練習問題(22/4/12)

# 問題
以下の要件を満たすsum_numberメソッドを実装しましょう。
第一仮引数をnとし、第二仮引数をmとすること
nからmまでの数を足し合わせていき、その総和を出力すること
（例：n=1,m=5の場合、総和は1+2+3+4+5=11）

↓雛形
```ruby.rb
def sum_number(n, m)
  # 処理を記述
end

# 呼び出し例
sum_number(1,10)
```
出力例
sum_number(1,3) → 6 ＜計算内容：1+2+3＞
sum_number(1,10) → 55 ＜計算内容：1+2+3+4…+10＞
sum_number(5,15) → 110 ＜計算内容5+6+7+8…+15＞
# 私の回答
```ruby.rb
def sum_number(n, m)
  total = 0
  while n <= m
    total = total + n
    n = n + 1
  end
  puts total
end

# 呼び出し例
sum_number(5,15)
```
# 解説
まず2行目に変数totalに0を代入する
3行目でWhile文を使いn<=mの場合の処理を繰り返す
4行目5行目で変数totalに実引数の値を＋していく
最終的にtotal値が5~15の総和になる
# 模範回答
```ruby.rb
def sum_number(n, m)
  total = 0 # 総和を代入する変数を、初期値0で定義しておく
  while n <= m   # n<=mの条件を満たす間、処理を繰り返す
    total = total + n # 変数totalに変数nを足す
    n = n + 1
  end
  puts total
end

# 呼び出し例
sum_number(5,15)
```
# 模範解説
まず、1行目で、仮引数nとmを設定し、数値をそれぞれ受け取ります。

2行目で、総和を代入する変数totalを、初期値0で定義しておきます。

3行目から6行目でwhile文を使用し、繰り返し処理を記述しています。条件はn <= mとしています。これは、nがmより小さいまたは同じという条件を満たす時、ブロック内の処理を繰り返すと言う意味です。

繰り返しの処理内では、まず変数totalにnの数値を足します。そして、nに1を足して、次の繰り返し処理を行います。nがmより大きい数値になったら、処理が終了となります。
# 感じたこと
While文を使った基本的な処理内容だったので、調べながら回答することができた。
Rubyでの繰り返し処理はWhile文、for文、each文、times、loopと色々な記述があるので、
各々の特徴や制限を理解して使用できるようにしたい。

アドバイスやご指摘等ございましたらぜひお願いします！！

以上
multipassのメモ

## multipass

ubuntuのVMツール
https://multipass.run/
最近k8sのlocal環境を構築のためにmultipassを利用してみて、
軽くて、シンプルで、便利だったので、後でも使えそうなのでメモする

## 利用環境
mac osx (m1でも行ける)

## install
```
brew install --cask multipass
```
installの時に`Could not find PKG`みたいなエラーが発生したら[ここ](https://qiita.com/LeoK/items/e735d8470d92d1cbc193)

## command

下記から便利のため`alias mp=multipass`にして置く

helpは
`mp help`で調べると出てくる
`mp launch --help`

### よく使えそうなcommand
- launch&emsp;&emsp;&emsp;&emsp;&emsp;#ubuntu vmを生成および起動
- start&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;#ubuntu vm　instanceを起動
- stop
- restart
- mount, umount &emsp;#local directoryをinstanceにmount/unmountする
- list
- delete
- recovery&emsp;&emsp;&emsp;&emsp;#purgeされてなかったらdeleteされたinstanceのrecoverができる
- purge&emsp;&emsp;&emsp;&emsp;&emsp;#deleteされたinstanceを完全削除


### launch
option(よく使えそう
`-c --cpus default 1`
`-m --mem default 1G`
`-d --disk default 5G`
`-n --name`
```
mp launch --cpus 2 --mem 2G --disk --name kb-master1
```

### start, stop, restart
`-v --verbose`
```
mp start kb-master1
mp stop kb-master1 # --allですべてのinstanceをstopするのもok
mp restart
```

### mount, umount
`multipass mount [options] <source> <target> [<target> ...]`

```
$ mp shell kb-master1
$ ubuntu@kb-master1:~$ mkdir /home/ubuntu/mount
$ mp mount /path/to/host/directory kb-master1:/home/ubuntu/mount
```

### delete, recover, purge
`delete`しても`purge`しない限り`recover`できる
```
mp delete kb-master1 # -pをつけるとpurgeまでやってくれる
mp recover kb-master1
mp purge # すべてのdeleteされたinstanceをclearする
```



### 


## その他
### trouble shooting
#### launch後設定修正(memory, cpu core, disk sizeなど
jsonファイルPathはos versionによって違うらしいので、下記はMaｃOS Big Surのpath
`/System/Volumes/Data/private/var/root/Library/Application Support/multipassd/multipassd-vm-instances.json`

分からなかったら
```
$ sudo find / -iname "multipassd-vm-instances.json"
```

※ファイル修正前後multipassdをstop & startしておくこと

```
$ sudo launchctl unload /Library/LaunchDaemons/com.canonical.multipassd.plist
$ vi /path/to/multipasswd-vm-instances.json
$ sudo launchctl load /Library/LaunchDaemons/com.canonical.multipassd.plist
```
関連issue
https://github.com/canonical/multipass/issues/1158#issuecomment-548073024
ローカルのJupyter notebookファイルをnbviewerで公開する方法

GitHubやgistで公開したり、ローカルにダウンロードせずに、サーバ上にあるnotebookファイルの中身を確認したい！

dockerでカレントディレクトリを`/notebook`にマウントして、nbviewerの`--localfiles`オプションで`/notebook`を公開する。
`--localfiles`オプションの使い方の情報がなさすぎて困った。

```
docker run --rm -p 8080:8080 -v $(pwd):/notebook jupyter/nbviewer python -m nbviewer --port=8080 --localfiles=/notebook
```

これで、`http://[hostname]:8080/localfile/`にアクセスするとファイル一覧が表示される。
`Serving local notebooks in /notebook, this can be a security risk`だそうですので注意。