OmniAICreator/Qiita-1.07M huggingface dataset:
【勉強中の新入社員】Cygwin

勉強中の新入社員です。

今日はCygwinのインストール。
相も変わらず画像なしでお送りします。

#Cygwin？
Windows上で動くUNIXらしい。
開発環境が決まってて若干の差はあるかもしれないが、パッケージ、仮想開発環境構築のために入れてみます。

Tomcatと相性が合うかどうか、仕事に役に立つかも不確かですが、
環境構築って結構勉強になります。

人によっておすすめ環境は全然違ったり、
もっといいのがあったり、
必要に応じてインストールせざる負えない場合もあるし、
逆にそれが実サーバには対応してなくてバージョン戻したり、
まあ、自分のサーバでいろいろ試行錯誤できるって逆に大事な気がします。

僕もあまり詳しくないので、これは必要じゃね？と思ういくつかだけ入れてインストーる。

>Install _autorebase 001007-1
Install alternatives 1.3.30c-10
Install base-cygwin 3.8-1
Install base-files 4.3-2
Install bash 4.4.12-3
Install bzip2 1.0.8-1
Install ca-certificates 2.40-1
Install coreutils 8.26-2
Install crypto-policies 20190218-1
Install cygutils 1.4.16-2
Install cygwin 3.1.7-1
Install dash 0.5.9.1-1
Install diffutils 3.5-2
Install editrights 1.03-1
Install file 5.32-1
Install findutils 4.6.0-1
Install gawk 5.1.0-1
Install getent 2.18.90-4
Install grep 3.0-2
Install groff 1.22.4-1
Install gzip 1.8-1
Install hostname 3.13-1
Install info 6.7-1
Install ipc-utils 1.0-2
Install less 551-1
Install libargp 20110921-3
Install libattr1 2.4.48-2
Install libblkid1 2.33.1-2
Install libbz2_1 1.0.8-1
Install libfdisk1 2.33.1-2
Install libffi6 3.2.1-2
Install libgcc1 10.2.0-1
Install libgdbm4 1.13-1
Install libgmp10 6.2.0-2
Install libiconv 1.16-2
Install libiconv2 1.16-2
Install libintl8 0.19.8.1-2
Install liblzma5 5.2.4-1
Install libmpfr6 4.1.0-1
Install libncursesw10 6.1-1.20190727
Install libp11-kit0 0.23.20-1
Install libpcre1 8.44-1
Install libpipeline1 1.5.1-1
Install libpopt-common 1.16-2
Install libpopt0 1.16-2
Install libreadline7 7.0.3-3
Install libsigsegv2 2.10-2
Install libsmartcols1 2.33.1-2
Install libssl1.1 1.1.1f-1
Install libstdc++6 10.2.0-1
Install libtasn1_6 4.14-1
Install libuuid1 2.33.1-2
Install login 1.13-1
Install man-db 2.7.6.1-1
Install mintty 3.4.0-1
Install ncurses 6.1-1.20190727
Install openssl 1.1.1f-1
Install p11-kit 0.23.20-1
Install p11-kit-trust 0.23.20-1
Install rebase 4.4.4-1
Install run 1.3.4-2
Install sed 4.4-1
Install tar 1.29-1
Install terminfo 6.1-1.20190727
Install terminfo-extra 6.1-1.20190727
Install tzcode 2020a-1
Install tzdata 2020a-1
Install util-linux 2.33.1-2
Install vim-minimal 8.2.0486-1
Install which 2.20-2
Install xz 5.2.4-1
Install zlib0 1.2.11-1

初期インストールパッケージは以上のみです。
これ以降必要に応じて追加予定です。

https://qastack.jp/unix/101561/what-are-the-differences-between-bsdtar-and-gnu-tar
・bsdtarとGNU tarの違いは何ですか？
bsdtarとtarが別々にあり、どっちをインストールすべきか悩んでいるうちに見つけました。
原文は英語です。翻訳文でも意味は充分通じるので、日本語分だけ読んでも理解には問題ありません。
同じ文章を頭に入れた状態で、英文を読むって勉強になりますよ！
時間のある方はぜひ。

##Cygwinに必要なパッケージを入れる
今回作りたいのはjava、perl、shellの演習場ですが、
仕事が入りました。
やることは多分簡単だと思うけど、それを更に簡単にやりたくて、
teratermで踏み台サーバを超えて、ファイル転送したくて四苦八苦中。
ちょっとteratermのいじり方勉強してくるわ…

#次はteraterm
やるのは簡単やろうけどさ、環境作りからしたいじゃん。
一応タスクはやっておく。
JavaScriptで配列をsortする方法

この記事を書いた理由
---
JavaScriptを勉強したばかりの時期に、あれ？配列をソートするにはどうしたら良いんだ？
と考える機会が増えてくると思います。以下に、昇順と降順のパターンを羅列してます。今回はidを対象に並び替えを実施してますが、名前を対象にすることも可能なので、少し面倒なやり方を選びました。

---
まずは、配列を作るところからスタート！

```main.js
/*idとnameが含まれている配列*/
let arry = [
  { id: 5, name: "ayaka" },
  { id: 7, name: "uadagawa" },
  { id: 8, name: "katou" },
  { id: 3, name: "kentaro" }
];
```

❶：配列のidを降順で表示させる場合


```main.js

arry.sort(function (a, b) {
  if (b.id > a.id) return 1;
  else return -1;
});
document.write(JSON.stringify(arry));

/*結果
[{"id":8,"name":"katou"},
{"id":7,"name":"uadagawa"},
{"id":5,"name":"ayaka"},
{"id":3,"name":"kentaro"}]
*/
```

❷：配列のidを昇順で表示させる場合

```main.js
arry.sort(function (a, b) {
  if (b.id < a.id) return 1;
  else return -1;
});
document.write(JSON.stringify(arry));

/*結果
[{"id":3,"name":"kentaro"},
{"id":5,"name":"ayaka"},
{"id":7,"name":"uadagawa"},
{"id":8,"name":"katou"}]
*/
```

理屈としては、returnで１や−１と返している部分は、idの並び順です。

ex) 1,3なら、１が１番目に来て、３が２番目に来るって感じ。昇順ならね。
returnの１なんだけど、個人的には＋１だと思ってるｗ

最後に、document.writeの部分は、console.logにしても良いんじゃないのか
という意見もあると思う。

```main.js
document.write(JSON.stringify(arry));
/*ここは、console.logに変更してもらっても構わない。*/
```

codesandboxで、console.logもしくはdocument.writeを記述しても[object][object].......となってしまうのを防ぐために、上記の書き方をしています。jsonパッケージが入ってる場合、少し厄介ですね。

---


初学者からの視点で記述したので、誰かの勉強の手助けになれば嬉しいです！では！
CloudWatchメトリクスとデータポイント

#CloudWatchにおける標準メトリクスの「期間の5分」について

##1. はじめに
- CloudWatchメトリクスのCPU使用率について「期間の5分」は5分毎にデータを取得しているのかな？
- 次に取得するまで4分59秒の間はデータを取得しないのかな？と思い、実際にデータ取得の様子を確認してみました。


##2. 実験
- EC2（RHEL8.2）インスタンスにsshログインしてyesコマンドを実行し、CPU使用率を上げます。
- 下図の様にEC2のCPU使用率のメトリクスをグラフにします。**4.CPUUtilization SampleCount「5」**に注目すると、5分毎にメトリクスが生成される時に「5」個のデータポイントを使っていることが分かります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280358/36d10d9c-9794-4143-8351-e904173074a1.png)

- 次に[BlackBelt](https://d1.awsstatic.com/webinars/jp/pdf/services/20190326_AWS-BlackBelt_CloudWatch.pdf)をよく読むと・・・。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280358/c325728f-9859-5757-fcdb-8079cb090758.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280358/9e3d094f-4ce4-82bc-c203-93659980dc4a.png)

##3. 結論
- 「1分間隔でデータポイントを取得し、5分毎にメトリクスを生成」している動きと分かりました。
- [AWSの公式サイト](https://aws.amazon.com/jp/premiumsupport/knowledge-center/cloudwatch-retrieve-data-point-metrics/)にも説明がありました。
【WIP】STS4　便利コマンド　備忘録

# おすすめショートカットキー、他


###1.必要なパッケージをまとめて自動でインポートしたい時

- 「shift + command + o」

https://gyazo.com/79df5bd9356ee706e5d2805c439ae370

---
###2.ファイルのパスの表記を変更したい時


- 赤枠の下矢印を押下する
- パッケージプレゼンテーションを押下
- 階層をクリック

![スクリーンショット 2020-09-22 14.29.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313771/6d6e1c03-4d22-7997-d7fa-d7d09d3c7eaa.png)


メリット

- ファイルが見やすくなる







変更前

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313771/668bce78-32ac-52f4-441e-3b0732298fc2.png)


変更後

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/313771/92b51c67-8eab-1659-2344-5ed1cc41afad.png)


---


200922 作成。なんども使う機能があればメモしていく



##参考にした記事（いつもありがとうございます。）
---
[Python]ElasticBeanstalkでRDSのDBインスタンスを追加

Elastic Beanstalk
Pythonアプリケーション環境にAmazon RDS DBインスタンスを追加

初めてRDSを使用する場合、EBマネジメントコンソールを使用してテスト環境に[DBインスタンス](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/create-deploy-python-rds.html#python-rds-create)を追加し、そのインスタンスにアプリケーションが接続できることを確認する

DBに接続するには、アプリケーションに適切な[ドライバ](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/create-deploy-python-rds.html#python-rds-drivers)を追加し、コードにドライバをロードした後、EBに用意された環境プロパティにより[接続オブジェクトを作成](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/create-deploy-python-rds.html#python-rds-connect)する
設定と接続コードは、使用するDBエンジンとフレームワークによって異なる

# 環境にDBインスタンスを追加
1. EBコンソールからAWSリージョンを選択
2. ナビゲーションペインで使用する環境を選択
3. ナビゲーションペインで設定を選択
4. データベース設定カテゴリで編集を選択
5. DBエンジンを選択してユーザー名とパスワードを入力する
6. Applyを選択

EBインスタンスの追加には約10分かかる
環境の更新が完了するとDBインスタンのホスト名とその他の接続情報は以下の環境プロパティを通じてアプリケーションに使用できるようになる

|プロパティ名|説明|プロパティ値|
|---|---|---|
|`RDS_HOSTNAME`|DBインスタンスのホスト名|Amazon RDSコンソールの[接続とセキュリティ]タブ:[エンドポイント]|
|`RDS_PORT`|DBインスタンスが接続を許可するポート デフォルト値はDBエンジンによって異なる|Amazon RDSコンソールの[接続とセキュリティ]タブ:[ポート]|
|`RDS_DB_NAME`|EB名`ebdb`|Amazon RDSコンソールの[設定]タブ:[ポート]|
|`RDS_USETNAME`|データベース用に設定したユーザー名|Amazon RDSコンソールの[設定]タブ:[マスターユーザー名]|
|`RDS_PASSWORD`|データベース用に設定したパスワード|Amazon RDSコンソールにでは参照不可|

内部DBインスタンスの設定の詳細については[DBをEB環境に追加する](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/using-features.managing.db.html)を参照

# ドライバーのダウンロード
プロジェクトの[要件ファイル](https://docs.aws.amazon.com/ja_jp/elasticbeanstalk/latest/dg/python-configuration-requirements.html)　DBドライバーを追加する
例) requirements.txt
```
mysqlclient==1.3.12
```

Pythoの共通ドラーバーパッケージ
 - MySQL:`MySL-python`(Python 2)、`mysqlclient`(Python 3)
 - PostgreSQL:`psycopg2`
 - Oracle:`cx_Oracle`
 - SQL Server:`adodbapi`

# DBへの接続
EBは、環境プロパティでアタッチされたDBインスタンスの接続情報を提供する
`os.environ[‘VARIABLE’]を使用してプロパティを読み取り、DB接続を設定する

例)Djangoの設定ファイル - DATABASEディクショナリ

```python
import os
if ‘RDS_HOSTNAME’ in os.environ:
	DATABASES = {
		‘default’: {
			‘ENGINE’: ‘django.db.backends.mysql’,
			‘NAME’: os.environ[‘RDS_DB_NAME’],
			‘USER’: os.environ[‘RDS_USERNAME’],
			‘PASSWORD’: os.environ[‘RDS_PASSWORD’],
			‘HOST’: os.environ[‘RDS_HOSTNAME’],
			‘PORT’: os.environ[‘RDS_PORT’],
		}
	}
```
ShiftItをHammerspoonによって代替する

[ShiftIt](https://github.com/fikovnik/ShiftIt)はmacOSでのウィンドウ配置によく利用されていたアプリだが、現状メンテナンスされていない。ShiftItのメンテナは、代替として[Hammerspoon](http://www.hammerspoon.org/)をお勧めしている。


ここではShiftitの

- 右側へ横50%の幅で寄せる
- 左側へ横50%の幅で寄せる
- 上側へ縦50%の幅で寄せる
- 下側へ縦50%の幅で寄せる
- 縦横100%のサイズにする

という動作を、全く同じキーバインドで、Hammerspoonで代替してみる。

## 環境

- MacBook Pro (16-inch 2019)
- macOS Catalina 10.15.6

## 手順

1. [公式サイトからHammerspoonをダウンロード](http://www.hammerspoon.org/)し、`/Applications`フォルダへドラッグアンドドロップ
2. Hammerspoonを起動し、メニューバーアイコン -> Preferences...
3. 「Launch Hammerspoon at login」をチェックする
4. 「Enable Accesibility」を押して、Macの設定からHammerspoonにアクセス権を与える
5. これで準備が完了した。キーバインドを設定していく。

好きなターミナルを開き、以下のコードを貼り付ける。

```sh
cat <<EOH > ~/.hammerspoon/init.lua
hs.window.animationDuration = 0
units = {
  right50       = { x = 0.50, y = 0.00, w = 0.50, h = 1.00 },
  left50        = { x = 0.00, y = 0.00, w = 0.50, h = 1.00 },
  top50         = { x = 0.00, y = 0.00, w = 1.00, h = 0.50 },
  bot50         = { x = 0.00, y = 0.50, w = 1.00, h = 0.50 },
  maximum       = { x = 0.00, y = 0.00, w = 1.00, h = 1.00 }
}

mash = { 'option', 'ctrl', 'cmd' }
hs.hotkey.bind(mash, 'right', function() hs.window.focusedWindow():move(units.right50,    nil, true) end)
hs.hotkey.bind(mash, 'left', function() hs.window.focusedWindow():move(units.left50,     nil, true) end)
hs.hotkey.bind(mash, 'up', function() hs.window.focusedWindow():move(units.top50,      nil, true) end)
hs.hotkey.bind(mash, 'down', function() hs.window.focusedWindow():move(units.bot50,      nil, true) end)
hs.hotkey.bind(mash, 'm', function() hs.window.focusedWindow():move(units.maximum,  nil, true) end)
EOH
```

終わったら、必ずメニューバーアイコンのHammerspoonから、「Reload Config」すること。

## 結果
これでShiftitの、

- 右側へ横50%の幅で寄せる
- 左側へ横50%の幅で寄せる
- 上側へ縦50%の幅で寄せる
- 下側へ縦50%の幅で寄せる
- 縦横100%のサイズにする

が再現できた。

私はこのくらいしか使わないので他の機能は再現していないが、上記のスクリプトはLuaで書かれておりカスタマイズが可能である。Hammerspoonのドキュメント（特に[キーコード](https://www.hammerspoon.org/docs/hs.keycodes.html)）を読みながら、好きな機能を実装することもできると思われる。

## 参考

- [Shiftitの公式Hammerspoon利用手順](https://github.com/fikovnik/ShiftIt/wiki/The-Hammerspoon-Alternative
)
- [Hammerspoonのキーコード](https://www.hammerspoon.org/docs/hs.keycodes.html)
[覚え書き]AndroidにWindowsからadbコマンドで接続する

FireHD10のLauncherをAmazon謹製からNovaLauncherに変更するためにadbコマンドに関して調べたメモ。

# Android SDKツールを入手する
## ダウンロード
公式サイトからWindows向け「Android SDKプラットフォームツール」をダウンロードする。
[SDK Platform-Tools リリースノート &nbsp;|&nbsp; Android デベロッパー &nbsp;|&nbsp; Android Developers](https://developer.android.com/studio/releases/platform-tools)
## 任意の場所に解凍する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223848/526cbdd7-6fc8-2e60-dcda-466a1173b963.png)
# コマンドプロンプトから操作する
操作するAndroid側は開発者モードになってUSBデバッグを有効にしておきます。
※有効化の方法は各機種で異なるのでググってください。

### Pathを変える
ちょこっと使うだけなので、Pathは通さず操作します。
何度も利用するなら、適当なフォルダーに移動してPathを通しておきます。

```
>cd C:\Users\nandy\Downloads\platform-tools
```
### Android機器との接続を確認する。

```
>adb devices
List of devices attached
G0012B0594171947        device
```

### shellで接続してみる
```
>adb -s G0012B0594171947 shell
maverick:/ $ 
```
Andoroidの操作ができるはず。
部品を画面の中央に配置する方法

![スクリーンショット 2020-09-22 14.12.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/722496/d27d9143-766e-d0bc-c878-29deaa8ad347.png)






（１）AutoLayoutの部分の左から２番目のアイコンをクリックします。
（２）以下２つにチェックをいれる
Horizontally in Container （cf. Horizontal 水平な)
Vertically in Container 　(cf. Vertical   垂直な)
（３） 「Add 2 Constraints」をクリック
→これで自動的に部品が画面の中央に配置されます
[VSCode] 任意のキーを押したときに『◯◯ was pressed. Wating for second key of chord』とエラーが出るのを解決する

## はじめに
下記の環境で任意のキーを打った際に出るエラー
『◯◯ was pressed. Wating for second key of chord』
を解決します。

```
MacOS Catalina (Ver10.15.6)
VSCode (Ver1.49.1)
```

## 問題の原因/解決法

キーボードバインド(キーボードショートカットの割当)が被っている箇所があるため。

例えば今回スペースキーを打った際に『{Space} was pressed. Wating for second key of chord』と出ていたとしましょう。

まず、『コマンドパレット』を開き(cmd + Shift + p)、そこから『keyboard』と打ってOpen keyboard shortcutsを選択してください。
![スクリーンショット 2020-09-22 14.05.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403039/4c64e635-4b86-09f2-9e96-bf0bb92d0d4f.png)

次にkeyboard shortcutsの右上のファイルマークを押してkeybinding.jsonを開きます。
※下記画像の赤丸部分
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403039/4529b133-104f-780e-95a5-daffbafd55ae.png)

```
// Empty
[
  {
    "key": "space e",
    "command": "code-runner.run"
  },
  {
    "key": "ctrl+alt+n",
    "command": "-code-runner.run"
  }
]
```

keybinding.jsonの中でアラートの出ているキーがないか探してみてください。
今回だと『"key": "space e",』が問題だとわかります。

```
// Empty
[
  {
    "key": "ctrl+alt+n",
    "command": "-code-runner.run"
  }
]
```

最後にVSCodeを再起動すれば完了です。

本日は以上です。


## 参考記事
[VSCodeを初期化したが<Space>キーを押すと『{Space} was pressed. Wating for second key of chord』と出るのを解決したい/スタックオーバーフロー筆者質問](https://ja.stackoverflow.com/questions/70591/vscode%e3%82%92%e5%88%9d%e6%9c%9f%e5%8c%96%e3%81%97%e3%81%9f%e3%81%8cspace%e3%82%ad%e3%83%bc%e3%82%92%e6%8a%bc%e3%81%99%e3%81%a8-space-was-pressed-wating-for-second-key-of-chord-%e3%81%a8)
【備忘録】CSSの+(プラス)とは

#プログラミング勉強日記
2020年9月22日
CSSで`+`を見かけたが、意味がわからなかったので調べてみた。

#CSSの隣接セレクタ`+`(プラス)
　`+`は隣接セレクタと呼ばれる。そのあとすぐに来る要素を表す。

```html:HTMLファイル
<h1>こんにちは</h1>
<p>私の名前は田中太郎です</p>
```

```css:CSSファイル
h1 + p {
  color: blue;
}
```

![0922.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642821/c75aeeb7-7e87-cba0-e8c7-0d38d4acd802.png)

#参考文献
[CSSで「+（プラス）」というセレクタの意味は何？](https://www-creators.com/archives/1353)
Anaconda環境構築メモ

※これはWindowsでのお話。もちろんMacだとactivateの際など、多少コマンドは異なります

## やりたいこと
(conda環境にて)
base環境はpython3.8を使っているが、2020/09/22時点ではtensorflowがpython3.8に対応していない。よって

- python3.7用のpy37という環境を作る
- パッケージ類を全て再インストールするのは面倒くさいのでbaseか何かから引き継ぐ(今回はdata分析環境dsから引き継ぐ)

みたいなことをやりたい。

## まず初めに

> conda create -n py37 python=3.7

これでpython3.7用のpy37という環境が作れる

次にこの環境にdsで使用していたパッケージ類を入れたい。
まずdsのパッケージ一覧をtxtで出力する。

> conda list --explicit > ds.txt

ds.txtはcurrent working directoryに作成される。

## ここ大事

dsではpython3.8を使用していたので、ds.txtにはpython3.8をinstallする箇所がある。
具体的には次にような行

> https://repo.anaconda.com/pkgs/main/win-64/python-3.8.5-h5fd99cc_1.conda

ds.txtをエディターか何かで開いてこの行を削除する。

## 最後に

ds.txtの内容をpy37に反映させる。

> conda activate py37

でpy37をactivateした後に

> conda update --file ds.txt

これで新しく作ったpy37にpythonのバージョン以外のdsの環境が引き継がれる。

## 補足

> https://repo.anaconda.com/pkgs/main/win-64/python-3.8.5-h5fd99cc_1.conda

この一文がちゃんとds.txtから取り除かれていれば

> conda create -n py37 python=3.7 --file ds.txt

で、環境の作成+引継ぎができると思うけど試していない。

かなりマニュアルチックなやり方なので、もっと簡単にできる方法があればコメントお願いします。
「プロセスが終了するのを待ちます。」でプロセスが残ったのでkillする

#プロローグ
yum install してたら、プロセスが残ってしまったので解消するだけ

#環境

```bash
[root@localhost ~]# cat /etc/redhat-release
CentOS Linux release 8.2.2004 (Core) 
```

#手順

```bash
[root@localhost ~]# yum install bind
pid 5016 のプロセスが終了するのを待ちます。
```

「いつまでたっても終わらんぞ...」

```bash
[root@localhost ~]# ps|grep "5016"
   5016 pts/0    00:00:04 yum
```

「進んでない。殺すか。」

```
[root@localhost ~]# kill 5016
[root@localhost ~]# echo $?
0
[root@localhost ~]# ps|grep "5016"
   5016 pts/0    00:00:04 yum
```
「死なないだと...」

```
[root@localhost ~]# kill -s 9 5016
[1]+  強制終了            yum install bind
[root@localhost ~]# ps|grep "5016"
```
(完)

#エピローグ
killの戻りは0でしたが、なぜか生きていたので、強制終了しました。
libmysqlclient.so.18がないといわれる

## 対応

### インストール
```
yum update -y
yum install -y gcc mysql-devel mysql-libs

# ↓どっちかに入る
# /lib64/mysql/libmysqlclient.so.18
# /usr/lib64/mysql/libmysqlclient.so.18
```

### それでも動かない場合

環境変数、`LD_LIBRARY_PATH`が通っている場所へ`libmysqlclient.so.18`をコピーする。

※
実体は`libmysqlclient.so.18.0.0`になっているので注意
（`libmysqlclient.so.18`はシンボリックリンク）

特にLambda上で必要な場合などは注意。

```
# Lambda上
LD_LIBRARY_PATH=/var/lang/lib:/lib64:/usr/lib64:/var/runtime:/var/runtime/lib:/var/task:/var/task/lib:/opt/lib
```
PowerShell 7でクリップボードのファイル操作

消えたと思われていたコマンドレットの Get-Clipboard ですが、 PowerShell 7で復活しましたね。ただ、公式の [Issue](https://github.com/PowerShell/PowerShell/issues/12290) によると、PowerShell のクリップボードまわりはテキストのみ扱う方針だそうです（OS に依存しない動作を実現するためのデザインとのこと）。

PowerShell 5以前にあった `-Format` オプションはファイル形式を指定したコピーなど日常のちょっとした操作で便利だったので、.Net Core 化した PowerShell 7でも似たようなことをしてみようと思います。

## ファイルをクリップボードにコピーする
パイプラインで受け取る使い方がメインなので自動変数 `$input` を使っています。

```powershell
function Set-ClipboardFile {
    <#
        .EXAMPLE
        ls | Set-ClipboardFile
    #>
    [Windows.Forms.Clipboard]::SetFileDropList($input)
}
```

※クリップボードに「切り取る」のは以前に [別記事](https://qiita.com/AWtnb/items/6fa068a5fe0af2c3505e) で紹介しています。

## クリップボードのファイルを取得する

```powershell
function Get-ClipboardFile {
    return $([Windows.Forms.Clipboard]::GetFileDropList() | Get-Item)
}
```

## 番外編：クリップボードの画像をファイルに保存する

クリップボード中の画像を取得してファイルに書き出すコマンドも作ってみました。Win+Shift+S でキャプチャした内容を画像ファイルとして保存するときに活躍します。

```powershell
function Convert-ClipboardImage2File {
    param (
        [string]$basename,
        [switch]$force
    )
    $img = [Windows.Forms.Clipboard]::GetImage()
    if(-not $img) {
        return
    }
    if (-not $basename) {
        $basename = Get-Date -Format yyyyMMddHHmmss
    }
    $fullpath = $pwd.Path | Join-Path -ChildPath ($basename + ".png")
    if (Test-Path $fullpath) {
        if (-not $force) {
            Write-Host ("'{0}{1}' already exists!" -f $basename, $extension) -ForegroundColor Magenta
            return
        }
    }
    $img.save($fullpath)
    Write-Host "save clipboard image as " -NoNewline
    Write-Host ("'{0}{1}'" -f $basename, $extension) -ForegroundColor Cyan
}
```

ファイル名を指定しない場合はその時点のタイムスタンプで保存するようにしています。
複数パーティションを含むイメージデータをマウント

# 複数パーティションを含むイメージデータをマウント

## 複数パーティションを含むイメージデータをマウントして中身を確認するサンプル

ここではパーティション が 2 つを含むイメージデータの二つ目のパーティションをマウントして中身を確認する例を示します。

```sh

#!/bin/sh -e

# イメージファイルのファイル名
FILENAME=$1

# マウントポイント
MOUNT_POINT=$2

# 引数で指定したイメージをループバックデバイスに関連づける。ループバックデバイス名は変数 LOOPBACK_DEVICE に割り当てる。
LOOPBACK_DEVICE=$(sudo losetup -P --show -f ${FILENAME})

# ループバックデバイス一覧を表示する
losetup -l

# 複数パーティションを含むイメージデータをマウントするときの肝
# 複数パーティションを含むイメージデータをループバックデバイスにマウントすると
# パーティション1 は /dev/loopXXXp1
# パーティション2 は /dev/loopXXXp2
# というように p数字 という suffix がついたデバイスが作られる。
LOOPBACK_DEVICE_P2=${LOOPBACK_DEVICE}p2

# 割り当てたイメージファイルの各パーティションに対応するループバックデバイス名一覧を表示する。
sudo ls -l ${LOOPBACK_DEVICE}*

# 割り当てたループバックデバイスをマウントする
sudo mount ${LOOPBACK_DEVICE_P2} ${MOUNT_POINT}

# マウントしたファイルシステムの中身を確認する
# 必要に応じて書き換えることが可能
sudo ls -l ${MOUNT_POINT}

# マウント解除する。
sudo umount ${MOUNT_POINT}

# ループバックデバイスを解放する。
sudo losetup -d ${LOOPBACK_DEVICE}
```

https://gist.github.com/m-tmatma/d6ef575fcf1a2c5aed5a10e439bc7f12



# 解説

## losetup によるイメージファイルと loopback デバイスとの関連付け

[losetup -f](https://linuxjm.osdn.jp/html/util-linux/man8/losetup.8.html) コマンドで指定したイメージファイルをループバックデバイスに関連付けます。

* `-P` でパーティションテーブルをスキャンします。
* `--show` で関連付けたループバックデバイスを表示します。

以下のコマンドを実行すると、イメージファイルパスのイメージをループバックデバイスに関連付けてそのデバイス名を表示します。

```
sudo losetup -P --show -f イメージファイルパス
```

例えば `/dev/loop10` に関連付けたとすると、このイメージのパーティション1 は `/dev/loop10p1` に関連付けられてパーティション2 は `/dev/loop10p2` に関連付けられます。

最初のスクリプトでは `${LOOPBACK_DEVICE}p2` とすることで `/dev/loop10p2` でパーティション2 を参照します。

## ループバックデバイスの列挙

以下のコマンドを実行することですべてのループバックデバイスの情報を列挙して表示します。

```
losetup -l
```

## ループバックデバイスに関連付けたイメージファイルをマウントする。

```
sudo mount ${LOOPBACK_DEVICE_P2} ${MOUNT_POINT}
```

${MOUNT_POINT} は引数で与えられたマウントポイントを指定します。ここでは `/mnt/test` とします。

上記のコマンドは以下のようなコマンドになります。

```
sudo mount /dev/loop10p2 /mnt/test
```

これにより、イメージファイルとして指定したイメージのパーティション2を `/mnt/test` で参照できるようになります。
github actionsの手動トリガーによる実行（workflow-dispatch-event）

## 概要
github actions が手動トリガーに対応していたので試してみた
workflow-dispatch-eventを利用することで実行することが可能


## 公式ドキュメント
- https://docs.github.com/ja/actions/reference/events-that-trigger-workflows#status
- https://docs.github.com/en/rest/reference/actions#create-a-workflow-dispatch-event


## Workflowファイルの作成
- workflow_dispatch を 利用したworkflowファイルを作成し、リポジトリに対してpushを行う

```yml:workflow.yml
on: workflow_dispatch

name: Workflow dispatch events

jobs:
  dispatch:
    name: Workflow dispatch events
    runs-on: ubuntu-latest

    steps:
    - name: Hello
      run: |
        echo "hello"
```

- pushが行われると、githubのweb上にworkflowが追加されていることが確認できる
![スクリーンショット 2020-09-22 13.01.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366054/11a8e6c8-be5a-4043-36aa-4adafc80b55a.png)

## Web上で実行
- Run workflowを選択することで、実行するブランチを指定して、workflowを実行することが可能
![スクリーンショット 2020-09-22 13.04.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366054/ebbe4660-7163-b7d5-f11a-a536499300ac.png)


## コマンドラインから実行
- https://docs.github.com/en/rest/reference/actions#create-a-workflow-dispatch-event

### パーソナルアクセストークンの作成
- コマンドラインからの実行にはPERSONAL_ACCESS_TOKENが必要のため発行しておく
    - https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token

### workflow_idの取得
- 発行したPERSONAL_ACCESS_TOKENを利用して、下記コマンドより該当ワークフローのidを取得する。一度今度Web上でworkflowを実行した後であれば、workflow実行履歴のURLから確認することも可能

```shell
$ curl \
  -H "Accept: application/vnd.github.v3+json" \
  -H "Authorization: token [PERSONAL_ACCESS_TOKEN]" \
   https://api.github.com//repos/{owner}/{repo}/actions/workflows
```

###  workflowの実行
- URLに対し POST を行うことで、workflowファイルの実行が可能

```shell
curl \
  -X POST \
  -H "Accept: application/vnd.github.v3+json" \
  -H "Authorization: token [PERSONAL_ACCESS_TOKEN]" \
  https://api.github.com/repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches \
  -d '{"ref":"master"}'
```
[Java]処理時間の計測方法メモ

## コード

```
long hogeStart = System.currentTimeMillis();

// 計測したい処理

long hogeEnd = System.currentTimeMillis();
long hogeTime = hogeEnd - hogeStart;
System.out.println("処理時間: " hogeTime + " ms");
```

## 実際の使用

- 以下をClipy等でスニペット登録しておく

```
long hogeStart = System.currentTimeMillis();
long hogeEnd = System.currentTimeMillis();
long hogeTime = hogeEnd - hogeStart;
```


- 計測したい処理を行入れ替え等で間に挟み込む
![d321cd92f73344804aefe4af36029bfa.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406531/88deb6fc-6c67-1a07-3a09-e38b1e79d6bf.gif)

- 複数処理を計測したい場合は`hoge`を計測したい任意の処理名に置換して使う
![212ada11a3dee2dd897f51e7c50139c0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406531/320fe814-5373-e48d-ae52-75d518ad11fb.gif)
【初学者向け】Rails mergeメソッド

## 記事の目的
初学者の為、間違っているところはご指摘頂けると幸いです。
備忘録・アウトプット目的で投稿です。

2021/1/10：追記
Qiita初投稿でチェリー本の伊藤さんから記事の間違いを指摘頂きました🙇‍♂️

##mergeメソッドとは？
ストロングパラメータの設定の最後にでてくる`merge`。

```ruby 
 def review_params
    params.require(:review).permit(
    :memo,
    :image)
    .merge(user_id:current_user.id)  ←これです
 end
```

##mergeメソッドについて（チェリー本より引用）
なんだこのメソッドは？とRailsの参考書等（Railsチュートリアル、現場Rails）等で調べるもない。
チェリー本をみてみると、メソッドについて説明がなされていた。以下、例文と説明。

```ruby 
h = { us: 'dollar', india: 'rupee' }

{ japan: 'yen' }.merge(h) #=> {:japan=>"yen", :us=>"dollar", :india=>"rupee"}
```

 `merge`メソッドを使うことによって、ハッシュリテラル内（`{ japan: 'yen', h}`）のほかのハッシュのキーと値（`{ us: 'dollar', india: 'rupee' }`）を展開できる。ふむふむ。

##Railsにおけるmergeメソッドを使う意味
で、なんの為の`merge`なんだっけ？
記事をみてみると、「ユーザから受け取った`params`にはないけれども、レコード作成時に追加したい値がある場合は`merge`メソッドで含めることができる」と書いてあった。
例えば、`Review`モデルに関して送られるパラメータ以外に`user_id`を含めたい！そんな時、`merge`メソッドを使うわけですね。

```ruby 
 def review_params
    params.require(:review).permit(
    :memo,
    :image)
    .merge(user_id:current_user.id)
 end
```

チェリー本を見た時、ほかのハッシュのキーと値を展開できるとあるが、「どういう場面で？」と思いながらの「ふむふむ。」だったが、「こう言う時に使えるんだ」と思えました。
チェリー本ありがとう。
bashの位置パラメータの動作確認

# きっかけ
[bashで位置パラメータを10個以上使うときの注意](http://www.denet.ad.jp/technology/2018/10/bash10.html)を読んで、気になったので自分でもテストした結果です。

# 位置パラメータとは

そもそも位置パラメータとは聞き慣れないので調べました。
bashの[man](https://linuxjm.osdn.jp/html/GNU_bash/man1/bash.1.html)によると、
\$1、\$2 ・・・は位置パラメータと呼ばれるものです。
\$*、\$@ は特殊パラメータと呼ばれます。

> パラメータ (parameter) は値を保持するためのものです。
> パラメータは 名前、 数字、後述の 特殊なパラメータ で挙げる特殊文字のいずれかで表現されます。
> シェルでの用法においては、 変数(variable) とは 名前 で表現されたパラメータです。

> 位置パラメータ(Positional Parameters)
>     位置パラメータ (positional parameter) は、1 桁以上の数値で表されるパラメータです。
>     ただし 0 は含みません。

0 はコマンド名だからでしょうか。

> 特殊パラメータ
>     シェルはいくつかのパラメータを特別扱いします。
>     このようなパラメータは参照されるだけであり、 値を代入することは許されません。

# 位置パラメータのテスト

まずは、下記のコマンドをテストします。

```bash:test.sh
#!/bin/bash

echo "\$1    = [$1]"
echo "\$2    = [$2]"
echo "\$3    = [$3]"
echo "\$4    = [$4]"
echo "\$5    = [$5]"
echo "\$6    = [$6]"
echo "\$7    = [$7]"
echo "\$8    = [$8]"
echo "\$9    = [$9]"
echo "\$10   = [$10]"
echo "\$11   = [$11]"
echo "\$12   = [$12]"
echo "\${10} = [${10}]"
echo "\${11} = [${11}]"
echo "\${12} = [${12}]"
```

実行した結果が下記です。

```
$ chmod 744 test.sh
$ ./test.sh a b c d e f g h i j k l
$1    = [a]
$2    = [b]
$3    = [c]
$4    = [d]
$5    = [e]
$6    = [f]
$7    = [g]
$8    = [h]
$9    = [i]
$10   = [a0]     ※[j] と表示されてほしい
$11   = [a1]     ※[k] と表示されてほしい
$12   = [a2]     ※[l] と表示されてほしい
${10} = [j]
${11} = [k]
${12} = [l]
```

上記のコマンドでテストした \$10は、```位置パラメータ $1(中身はa) と 文字「0」``` と解釈され、「a0」が表示されました。


# 特殊パラメータ

ついでに下記の特殊パラメータも試しました。

- \$* : 全ての位置パラメータ。"\$1 \$2 \$3 ・・・"。パラメータの間は、変数IFSの値。
- \$@ : 全ての位置パラメータ。"\$1" "\$2" "\$3" ・・・

```bash:test2.sh
#!/bin/sh
IFS=_
echo $*
echo "\$*   = $*"
echo "\${*} = ${*}"
echo ""
echo $@
echo "\$@   = $@"
echo "\${@} = ${@}"
```

実行します。

```
$ chmod 744 test2.sh
$ ./test2.sh a b c d e f g h i j k l
a b c d e f g h i j k l
$*   = a_b_c_d_e_f_g_h_i_j_k_l
${*} = a_b_c_d_e_f_g_h_i_j_k_l

a b c d e f g h i j k l
$@   = a b c d e f g h i j k l
${@} = a b c d e f g h i j k l
```

```echo $*``` だと、変数IFSの効果が出ないようです。


# 最後に
知っている人は知っている知識なのでしょう。
自分で試して、知っている側になれて良かったです。
クラウドが全盛になってくると、このような知識はますます表に出てこなくなるのでしょうね。
symfony4 FormTypeにoptionを追加する

#リゾルバにOPTIONを追加
```
class FormType extends AbstractType{

    /**
     * 
     * {@inheritDoc}
     * @see \Symfony\Component\Form\AbstractType::configureOptions()
     */
    public function configureOptions(OptionsResolver $resolver){
        $resolver->setDefaults([
            'data_class' => User::class,
            //追加するoption設定
            'loginRole' => ''
       ]);
    }
}
```
##呼び出し側
```
$this->createForm(FormType::class,$user,['loginRole'=>'ADMIN_ROLE']);
```
勉強日誌17日目

昨日は夕方から飲みに行って遊んでリフレッシュ！

休日らしいことしてメンタル整えるのも大事！

てことで洗濯や掃除などしてスタート！

あれ、成績伸び悩んでたと思ったら一気に成績上がってる・・・。
と思ったら少し落ちてるのもある
原因はスペル覚え間違い　TELLE→TELE　とか

正答率　72% (64)

id user1
ユーザ user1　のユーザIDや所属グループを表示したいときに使用するコマンド

useradd -d /home/aaa user
ホームディレクトリ aaa を指定して、新規ユーザ　user1を作成

groupmod -g 000 AA
既存のグループアカウント　AA　のグループIDを　000　へ

passwd -l user
ユーザ　user　を　ロック

id -G user
ユーザ　user の所属する全てのグループIDのみを表示

id -g user1
ユーザ　user1　のプライマリグループIDのみを表示

id
自分のアカウントのIDや所属グループを確認したい時

/etc/default/useradd
useradd　コマンドでオプションを指定せずにユーザを作成するときデフォルトの値が格納されるファイル

getent passwd
ローカルやLDAPサーバで管理しているユーザー情報の一覧表示

/etc/passwd
ユーザのアカウント情報が収められているファイル

userdel -r user
ユーザ　userを削除する際　ホームディレクトリも削除

usermod -p
暗号化済みのパスワード設定

/etc/nologin
一般ユーザーのログイン禁止

正答率 96% (63)

crond
定期的に実行するジョブのスケジュールを管理するデーモン

正答率70% (78)

iconv -l
文字コードの変換を行うコマンド、扱える文字コードを表示

Unicode
32ビットで表現　1つの文字体系で多言語を扱う

tzselect
環境変数　TZ　/etc/timezone ファイルで使用できるタイムゾーンの値を表示

locale
現在のロケールの設定

LC_TIME
ロケールの設定で、日付や時刻の書式設定

LC_TELEPHONE
ロケールの設定で、電話番号

locale -a
ロケールの環境変数に設定できる値の確認

/usr/share/zoneinfo
タイムゾーンの情報がバイナリファイルとして格納されているディレクトリ
Laravel8以降でルーティングがうまくいかない件

Laravel8.Xからrouteの記述方法が変わって面倒になったかもしれません。（自分でも検証済み）
Laravel7.X以前→Laravel8.X以降はエラーになったと思ったが、解決した件。

route/web.php

```
Route::get('/', 'TestController@index');
```
laravel8.X以降はこのようにしないとうまく行かなかった。。
route/web.php

```
use App\Http\Controllers\TestController;
Route::get('/',[TestController::class, 'index']);
```

しかし、Laravel8でも、app/Providers/RouteServiceProvider.php の bootメソッドでnamespaceを指定すればこれまで通りの動作をすることがわかった。
（土日にも関わらず、疑問に対応いただいた私の上司に感謝。。）


app/Providers/RouteServiceProvider.php

```
public function boot()
    {
        $this->configureRateLimiting();
        $this->routes(function () {
            Route::middleware('web')
                ->namespace('App\Http\Controllers')   ← 【これを足す】
                ->group(base_path('routes/web.php'));
            Route::prefix('api')
                ->middleware('api')
                ->group(base_path('routes/api.php'));
        });
    }
```

これで今まで通り（Laravel7以前のようにルーティングがうまくいく）
routes/web.php

```
Route::get('/test', 'TestController@index');
```

以上
Anaconda使用でインタラクティブシェルのタブ操作で長いエラーが出る場合

ちょっとしたプログラムの結果確認でインタラクティブシェルを使用する事は結構あると思います。

「for文後のタブ操作でエラー」という基本操作でなかなか進まず、やっと解決できたので私備忘録兼で共有させていただきます。

かなり長いエラーの最後に出てくる１文は以下
「AttributeError: module 'readline' has no attribute 'redisplay'」

readlineはどうもタブ操作補完等のパッケージですが、検索いろいろしても「バージョンがあってない可能性があるので再インストール等検討」くらいの記事しか出てこず、しかも再インストールしてもダメ。最新バージョン2.1から「2.0」にダウングレードもできず。

やっとたどりついたGitHub記事が以下
https://github.com/pyreadline/pyreadline/issues/64

やや英文だけなので分かりにくいですがパッケージ内の「basemode.py」内の
記事にある2箇所にコード追加で解決しました。

この記事アップした方に感謝。
（GitHubってこういう風に使えるんだと初めて知りました。）
後、このreadlineというパッケージ、どうも2015年くらいからアップデートされてないように見えるのでどうにかならんのかとも思いました。
Diffie-Hellman鍵交換の原理（自分用メモ）

### Diffie-Hellman鍵交換とは
ジャンル: 暗号プロトコル。

### 嬉しいこと
インターネットにおいて、共通鍵をやりとりしたい相手との間で安全に作成できる。

### 原理
合同式は全て $mod\ p$ とする。pは大きめの素数とする。
 $x ^ y ≡ z$ のとき、一般に(x,y,p)からzを求めるのは簡単
 $x ^ y ≡ z$ のとき、一般に(x,z,p)からyを求めるのは難しい

### 手順
AさんとBさんの間で共通鍵を作成したい。x、pは公開情報（ばれてもいい）とする。
#### 手順１
Aさんは秘密の整数aをランダムに選び、$ E ≡ x ^ a $を計算する。
Bさんは秘密の整数bをランダムに選び、$ F ≡ x ^ b $を計算する。

#### 手順２
AさんはEをBさんに送る。（Eは他人にばれても良い）
BさんはFをAさんに送る。（Fは他人にばれても良い）

#### 手順3
Aさんは$ G ≡ F ^ a $を計算する。（AさんはF、aとも知っている）
Bさんは$ H ≡ E ^ b $を計算する。（BさんはE、bとも知っている）
$ G ≡ x ^ {ab} $
$ H ≡ x ^ {ab} $
とGとHは一致するので、これを共通鍵とすればよい。

### ポイント
Aさんの立場から見ると、
i. aを直接送らず、指数にいれることでaを隠蔽（元の式で、z,x,pからyを求めるのが困難なので、E,x,pがばれてもaは短時間ではばれない）。
ii. 送られてきたbの変換後の値のFの使い道として、Fに対して自分の持ってるaを指数部において更に累乗計算することにより$x ^ {ab}$が得られる。結局bは不明だが、（とある数をa乗してからb乗するのと、b乗してからa乗するのが同じという）指数計算の交換法則により、Bさんと同じ結果にたどり着くことができている。

### 良いところ
EおよびFをどちらも傍受されていても、$G(≡H)$ は得られない。(一瞬、$E * F ≡ x ^ a * x ^ b = x ^ {a + b} $が計算できてしまうと思ったが、共通鍵は$x ^ {ab}$なのでセーフ)

### 防げない攻撃
#### 中間者攻撃
AさんがBさんだと思ってCさんと
BさんがAさんだと思ってCさんと
やりとりをすると、Cさん（悪い人）は比較的容易にやりとりを仲介しつつ（AさんやBさんは相手から期待するレスポンスが返ってくるので特に違和感を持つことなく）、内容を盗み見ることができる。
なので、認証部分はRSAで行うのが良いらしい。実際にどう組み合わせるのか気になる。

### RSA鍵交換で何がダメなの？
RSAだと、「前方秘匿性がない」からだそうです。[エンジニアなら知っておきたい、絵で見てわかるセキュア通信の基本#他の公開鍵暗号](https://qiita.com/t_nakayama0714/items/83ac0b12ced9e7083927#%E4%BB%96%E3%81%AE%E5%85%AC%E9%96%8B%E9%8D%B5%E6%9A%97%E5%8F%B7)に書いてありました。なるほどお。

### 感想
概要だけ知りたかったのでここまで。おもしろかった。

### 参考
[ディフィー・ヘルマン鍵共有(wikipedia)](https://ja.wikipedia.org/wiki/%E3%83%87%E3%82%A3%E3%83%95%E3%82%A3%E3%83%BC%E3%83%BB%E3%83%98%E3%83%AB%E3%83%9E%E3%83%B3%E9%8D%B5%E5%85%B1%E6%9C%89)
[Diffie-Hellman鍵交換入門(Qiita)](https://qiita.com/okajima/items/036d7e751234f88fbe9a)
[エンジニアなら知っておきたい、絵で見てわかるセキュア通信の基本](https://qiita.com/t_nakayama0714/items/83ac0b12ced9e7083927)
Python×WindowsAppDriverによるGUIの自動化

#【前置きの前置き】
もともとはWinActorを現場に使ってもらうって計画だったらしく、
一時期契約まではしていたのですが、投資を回収できていないということから、WinActorも含むRPAの予算全カット。
その結果、必要経費０円でRPA化を進むことに・・・。
今回は、GUIの自動操作実現までの奮闘記。

#【前置き】
GUI操作の自動化にもいろいろと種類があります。
最初に行き着いたのはpyautoguiを使った画像認証＋キーボード／マウスの操作なのですが、
これだとキーボードとマウスの操作は確実にやってくれますが、
本当に画面を操作してくれているのか不明です。

そこに画像認証を加えるとかなり精度があがります。
ただ、これにも欠点があります。
・画面の表示上から指定の画像を探すような操作をするため遅い
・デスクトップの背景画像が変えてあると反応しない
・探させる画像１つで動作が変わる

もっと安定したものが欲しいと探して見つけたのが、
「UI Automation」なのですが、
以下の理由で却下
・コーディングが個人的に好みでない
・情報量が少ない（・・・と、個人的に感じた）

そこからいろいろと情報を集めてたどりついたのが、
「WindowsAppDriver」(長いので以下WAD)
以下が採用の理由
・コーディングの見た目がSeleniumに似ている
　└ Seleniumを覚えてれば誰でも使える。その逆も言える
・情報が割と新しく、調べれば簡単に調べられる

##※UI AutomationとWADの根本の仕組み
VB.netやJQueryなどに近いもので
操作対処のIDやその他の属性を指定して、対象を補足し操作するというタイプです。


#【本題】
基本的には、以下のURLに書いてある内容をやってもらえれば動きます。
https://arakan-pgm-ai.hatenablog.com/entry/2019/08/19/000000

##私が工夫した内容は以下の通り
###①動作環境
まず、実行環境には、「開発者モード」の権限を与える必要があります。
そのため、個人使用のPCを実行環境に選ぶという選択肢を早々に消しました。

私が選んだのは、仮想環境にWindows10を入れて使う
（仮想環境が用意できないのであれば、PCの代替えで引き揚げてきたPCでも可）
※代替えの引き上げPCなので故障などのリスクに対する対応を考えておく必要があります
※自動化専用ということもあって自動でログインできるようにしておくというのが肝

その他にクリップボードやマウスやキーボードなどの資源を人間と取り合うのを防ぐ目的と
メンテナンスがしやすくなるというのもあります。

作業者が１０人いれば、１０人のPCにインストールしてあげる必要とか出るかもしれませんし
作業者が複数人集まれば、注意事項とか守らない人も出てきます。
作業者が１人でも担当交代とかいろいろとありますので
作業者が触れない環境を作って、運用する方法というのを取っています。

どうやって自動化したプログラムを動かすか？
これは、タスクスケジューラとかを使うのが一番手軽です。
この辺りの対策もいろいろとありますのでまた後日。

###②間違ってWADの画面を閉じないようにする方法
WADを使った自動化が浸透してくると他のプログラマーもこの環境を使い始めます。
そうするとよくわからない人も見様見真似で使い始めるものです。
そうなると、黒い画面を閉じちゃった・・・。
っとか、やってしまうものです　
　
そうなると、タスクスケジューラなどで起動設定していても
WADが止まっているので動きません。

１番単純な対策は、黒い画面を見せないように起動することでしょう
以下のコードをShell:startupに設置しておくことです

```wad.vbs
dim ws
Set ws = CreateObject("Wscript.Shell") 
ws.Run """C:\Program Files (x86)\Windows Application Driver\WinAppDriver.exe""", 0
```

###③操作対象のアプリケーションがリモートの画面越しの場合
操作できませんので操作対象のアプリケーションをローカルにインストールしましょう
GUIの調査ツールで見るとわかりますが、VNCやXen越しに操作しようとすると
VNCやXenの画面のキャンパス部分の情報しか取れません。

###④入力速度の向上
pyperclipを使って入力対象の文字列をクリップボードに入力し
テキストボックスに対してCtrl+Vを送り付けて貼り付けを行いましょう。
標準の入力の仕組みを使っても良いのですが、
キーボードを叩いているような動きをしますので長い文章を入力するときは
動作速度が遅くなります。

###⑤操作対象のアプリケーションがバッチ起動＆起動済みの場合
前提の話ですが
以下のコードは、電卓を起動して電卓のアプリを補足するというような動きをします。

```
desired_caps["app"] = "Microsoft.WindowsCalculator_8wekyb3d8bbwe!App"
```

そのため
　desired_caps["app"]にバッチのパスを指定してもアプリを補足できません。
　※バッチのプロセスとアプリケーションのプロセスは別物です
　アプリケーションのフルパスを指定しても、自動更新などのバッチに書いてあることをやってくれません。
　新しくアプリケーションを立ち上げてしまうため、今動いているアプリケーションを捕まえることができません。
　
そこで取っている対策は、
・以下のコードを実行し、アプリケーションを起動する

```
subprocess.Popen(['start', バッチのショートカットのPATH], shell=True)
# たしか、start以外にexplorerでもよかったはず。
```

・Pythonのコードの以下の部分を変更すること

```
desired_caps["app"] = "Microsoft.WindowsCalculator_8wekyb3d8bbwe!App"
　↓
desired_caps["app"] = "Root"
```
こうするとデスクトップそのものを補足します。

上記の２つを満たしていれば、
　デスクトップに「●●●」というウィンドタイトルのアプリケーションは起動しているか？
　という確認ができるようになります。
　なので●秒待ってみて、見つからなければエラー／起動できれば、作業続行みたいな形にできます。

###⑥ダイアログの操作方法について
テキストボックスなど同様に属性値を調べてから操作することもできますが
かなり複雑な作りになっているものも存在します。
※特にファイルダイアログ

対策としては、
　ウィンドタイトルを確認した上で
　pyautoguiのキーボード操作と組み合わせると簡単に実現できます。


これだけやっておけば、大抵のことはできるのではないかと思います。
次回はWEBの自動操作辺りについてまとめてみようかと。
git push

すぐ参照できるように！

(githubでリモートリポジトリ作成、初回のみ)
cd ディレクトリ名

(ローカルリポジトリ作成、初回のみ)git init： git使い始める合図
touch ファイル名：　追加するファイルを入力
git add .　：　全部トラッキング状態にする
git commit -m "コメント"：　更新内容を残す
[git log：　履歴確認したいとき！]

(ローカルリポジトリ作成初回のみ)git remote add origin git@github.com:アカウント名/リポジトリ名.git ：　保存先のリモートリポジトリを設定
git push origin master：　保存
Rails Tutorial　第14章 演習14.2.3.2 コメントアウトは2箇所しないとredにならない

##演習14.2.2.3　strongタグにIDを指定

ネットの他の方はrespons.bodyでassert_matchをしていました。私はstrongタグにIDを指定して、assert_selectを使っています。


```ruby:test/integration/users_profile_test.rb
  test "Home stat" do
    log_in_as(@user)
    get root_path
    assert_select 'strong#following', @user.following.count.to_s 
    assert_select 'strong#followers', @user.followers.count.to_s
  end
```


```ruby:ネットの他の方
    assert_match @user.active_relationships.to_s, response.body
    assert_match @user.passive_relationships.to_s, response.body
```

##演習14.2.3.2 コメントアウトは2箇所しないとredにならない


ネットの他の方はコメントアウトは1箇所でした。私は2箇所しないとredにならずgreenのままでした。
 
```ruby:views/users/show_follow.html.erb
           <% @users.each do |user| %>
            <%#= link_to gravatar_for(user, size: 30), user %>
          <% end %>
..
      <ul class="users follow">
        <%#= render @users %>
      </ul>
```

```ruby:ネットの他の方
	<% @users.each do |user| %>
		<%= #link_to gravatar_for(user, size:30), user %>
	<% end %>
```

##演習14.2.6.1 ネットの他の人の答えが2種類

＞各行を順にコメントアウトしていき、テストが正しくエラーを検知できるか
の意味をどうとるかで、ネットの他の人の答えが2種類ありました。
１．「順に」を1行だけずつ
２．1行の次は2行と２行を追加していく

私は１．を選びましたが、それだと次の設問で答えが得られなかったです。なので２．が正解のようです。
理由は、format.jsの行だけをコメントアウトしても、redにならずgreenのままだからです。

```rails:controllers/relationships_controller.rb
      respond_to do |format|
#        format.html { redirect_to @user }
#        format.js
      end
```

```
 FAIL["test_should_follow_a_user_with_Ajax", FollowingTest, 0.9658033109999451]
 test_should_follow_a_user_with_Ajax#FollowingTest (0.97s)
        "@user.following.count" didn't change by 1.
        Expected: 3
          Actual: 2
        test/integration/following_test.rb:36:in `block in <class:FollowingTest>'

ERROR["test_should_follow_a_user_the_standard_way", FollowingTest, 0.9935227480000322]
 test_should_follow_a_user_the_standard_way#FollowingTest (0.99s)
ActionController::UnknownFormat:         ActionController::UnknownFormat: ActionController::UnknownFormat
            app/controllers/relationships_controller.rb:7:in `create'
            test/integration/following_test.rb:31:in `block (2 levels) in <class:FollowingTest>'
            test/integration/following_test.rb:30:in `block in <class:FollowingTest>'
```
[Linux-Mint]Anboxめも

```bash
# Linux-MintのDL
https://linuxmint.com/download.php
# University of Oklahoma
wget http://reflection.oss.ou.edu/linuxmint/isos/linuxmint.com/stable/20/linuxmint-20-mate-64bit.iso

# VirtualBoxにインストール想定
# Since Cinnamon features among the most lightweight desktop environments,
# Linux Mint requires quite a low hardware specification. For Mint 20, the minimum requirement is as follows: 1GB RAM (2GB recommended) 15GB of free disk space (20GB recommended)
20GBとメモリ8GBを確保(min:2GB)

# anbox インストール
sudo snap install --devmode --beta anbox

# Google Play Store インストール
sudo apt install lzip
wget https://raw.githubusercontent.com/geeks-r-us/anbox-playstore-installer/master/install-playstore.sh && chmod 755 install-playstore.sh
./install-playstore.sh
```
Python3 でバイト列から整数(short または int) への変換


[11, 24, 250, 1] のような１バイト以内の値のリストから、16bit または 32bit 整数へ変換する方法です。
とあるデバイスから１バイトずつ値を読み取って、それを 16bit 符号付き整数 (short) のリストへ変換する方法を調べたとき、なかなか見つからなかったので記事にしました。

##長さがわかっているとき

入力データのサイズがあらかじめ分かっている場合は、struct.unpack を使えば簡単です。

```python

import struct

input_data = [11, 24, 250, 1]

# 16bit 符号付き整数に変換する場合（リトルエンディアン）
short_values = struct.unpack('<hh', bytes(input_data))
print("short_values:", short_values)

# 32bit 符号なし整数に変換する場合（ビッグエンディアン）
ulong_value = struct.unpack('>L', bytes(input_data))
print("ulong_value:", ulong_value)

# 32bit 符号付きに変換する場合は int.from_bytes も使える
long_value = int.from_bytes(bytes(input_data), 'big')
print("long_value:", long_value)
```

ほかの型に変換したい場合は、[struct の書式指定文字](https://docs.python.org/ja/3/library/struct.html#format-characters) を見てください。

## どんな長さにも対応したいとき

入力データのサイズが可変なとき、または汎用的にしたい場合は array を使うと便利です。

```python
import array

input_data = [11, 24, 250, 1]

# 16bit 符号付き整数に変換する場合（リトルエンディアン）
short_values = array.array('h', bytes(input_data))
print("short_values:", short_values)

# 32bit 符号なし整数に変換する場合（リトルエンディアン）
ulong_values = array.array('L', bytes(input_data))
print("ulong_values:", ulong_values)
```
Next.jsにjestとenzymeを導入（next/babel使用）

# Next.jsにjestとenzymeを導入（next/babel使用）
以前、[Next.jsにjestとenzymeを導入する](https://qiita.com/lelouch99v/items/1c34ffdb187d376a7638)という記事を書きました。

上記の手順でjestの実行はできたのですが、yarn devでアプリ起動するとなにやらbabelに関するエラーが。。

どうやらNext.js起動すると追加したbabelの設定ファイルの方が読み込まれて、babelのエラーが出てしまっているよう。

そこで、jestで使うbabelをnext.jsのbabelに変更したところ、よりすっきりした設定になったのでメモ。

## next.jsのbabelが使える
next.jsにはデフォルトでbabelが入っており、これがjsxのトランスパイルなどjestにも適用できることが分かりました。
こちらの方がスッキリとした手順・設定で構築できます。

### jestインストール

```
$ npm install --save-dev jest 
```

### jest設定ファイルを生成

```
$ jest --init
```

##### command not found: jest の場合

以下を実行します。

```
./node_modules/.bin/jest --init   
```

### Enzymeインストール

```
yarn add --dev enzyme enzyme-adapter-react-16
```  

Enzymeの利用時は一度だけ`Enzyme.configure()`を呼ぶ必要があるため、下記のスクリプトを追加。

```jest.setup.js
import Enzyme from "enzyme";
import Adapter from "enzyme-adapter-react-16";

Enzyme.configure({ adapter: new Adapter() });
```

Jestのテスト前に実行されるようにする。

```jest.config.js
module.exports = {
  // ...
  setupFiles: ['./jest.setup.js'],
  // ...
}
```

### babel.config.jsを設定

```  
module.exports = {
    "presets": ["next/babel"],
};   
```  


- - - 
### テストファイルのignore

Cypressを導入しており、jest実行でcypressのspecも読まれてしまうので、ignore設定をしました。

```jest.config.js
...  
testPathIgnorePatterns: [
    "/node_modules/",
    "/cypress/"
],
...
```
Python でディレクトリ一覧を取得するテストプログラム

# 作った目的
Pythonでディレクトリ一覧を取得するために調べていたら、pathlibライブラリのことを知りました。
動作確認のために簡単なテストプログラムを作りました。

# 環境

- Windows10
- Python 3.7.7
- Ubuntuでも動作しました。
 
# 一覧の取得
ただ一覧を取得するだけなら下記のコードで可能です。

```python
import pathlib

dir = r"c:\users\username"    # Windows
# dir = "/home/username"        # Ubuntu
pathlib_obj = pathlib.Path(dir)
files = list(pathlib_obj.glob("*"))
for i in files:
  print(i)
```

# テスト プログラムについて

起動すると、ディレクトリの前に数字が表示されます。
その数字を入力してEnterを押すと、指定のディレクトリの一覧が表示されます。
終了は、qを入力してEnterです。

```python:pathlist_test.py
import pathlib


def filelist_print(pathlib_obj):
  # カレントディレクトリの表示
  print(f"===== カレントディレクトリ:{pathlib_obj} =====")

  # ファイル一覧の取得
  files = list(pathlib_obj.glob("*"))

  # ディレクトリとそれ以外を分ける
  dir_list = [pathlib.Path("..")]
  file_list = []
  for file in files:
    if file.is_dir() is True:
      dir_list.append(file)
    else:
      file_list.append(file)

  # ディレクトリの一覧を表示
  dir_no = 0
  for file in dir_list:
    print(f"{str(dir_no).rjust(3, ' ')} Dir  : {file.name}")
    dir_no += 1

  # ディレクトリ以外の一覧を表示
  for file in file_list:
    print(f"    File : {file.name}")

  # ディレクトリの一覧を返す
  return dir_list


def chk_int(input_str):
  if str.isdigit(input_str) is True:
    # 入力文字をintに変換して返す
    return int(input_str)
  # intに変換できない場合は、-1を返す
  return -1


# メイン
if __name__ == "__main__":
  # カレントディレクトリの取得
  pathlib_obj = pathlib.Path().resolve()

  # 以下、ループです
  while True:
    # ディレクトリにある一覧の表示
    dir_list = filelist_print(pathlib_obj)

    # ディレクトリの番号の入力待ち
    input_str = input("input Dir number (q: Quit): ")

    # 入力文字が「q」なら終了
    if input_str == "q":
      break

    # 入力文字をintに変換
    input_int = chk_int(input_str)

    if input_int == -1:
      # 入力文字がintに変換できない場合は、なにもしない
      continue
    elif input_int == 0:
      # 入力値が0の場合は、親ディレクトリへ移動
      pathlib_obj = pathlib_obj.parent
    else:
      if len(dir_list) > input_int:
        # 入力値がディレクトリ番号の場合は、そのディレクトリへ移動
        pathlib_obj = pathlib.Path(dir_list[input_int]).resolve()

```

# 最後に

作り込みはしていませんし、一覧取得の目的は達成したので、ここまでしか作っていません。
VSCodeでマウス操作しないで、複数のディレクトリ構造の確認するときに意外と使えました。
Pythonで画像ファイルの仕分け(2)

## まえがき

[前回](https://qiita.com/Since1967/items/891011dbf813b29b283f)、大量の画像ファイルを年月フォルダに仕分けするところまではやった。その際に重複しているファイルを削除するための手がかりとして次のようなCSVファイルを各フォルダに生成した。

```Console:info.csv
IMG_2607.jpg,BCF3E765,1944106
IMG_2607(1).jpg,BCF3E765,1944106
IMG_2608.jpg,02B27221,3109397
IMG_2608(1).jpg,02B27221,3109397
010(8).jpg,E4A68AB2,3801239
010(9).jpg,3EBBC7BD,1841698
010(10).jpg,B9431E60,103645
```

左からファイル名、CRC32、ファイルサイズとなっておりCRC32とファイルサイズが一致するファイルはほぼ間違いなく同じファイルであろうということで削除対象にする。前記のファイルで言えば２行目と４行目が重複しているのでこれを次のように２グループに分けたい。

```Console:servived
IMG_2607.jpg,BCF3E765,1944106
IMG_2608.jpg,02B27221,3109397
010(8).jpg,E4A68AB2,3801239
010(9).jpg,3EBBC7BD,1841698
010(10).jpg,B9431E60,103645
```

```Console:delete
IMG_2607(1).jpg,BCF3E765,1944106
IMG_2608(1).jpg,02B27221,3109397
```

考え方としては単純に`削除リスト ← 元のリスト - 重複しているリスト`と`生き残りリスト ← 元のリスト - 削除リスト`で良いのだが色々こねくり回してみたものの納得のいく実装に辿り着けなかったので今回は

1. 空の生き残りリストと削除リストを用意する
1. 検査対象が生き残りリストにあれば削除リストに追加する
1. 検査対象が生き残りリストになければ生き残りリストに追加する

といったシンプルな実装にする(やりたいことが込み入った内容ではないのでこれで十分かと思っている)

## 開発環境

- OS : Windows10Home(1903)
- Editor:Visual Studio Code : 1.49.1
- Python : 3.8.3
  - pandas : 1.0.5

## コード

```Python:Classify.py
import os
import sys
import pandas as pd

def classify(path, target):
    lines = pd.read_csv(os.path.join(path, target), header=None)

    d = {}
    servived_dict = {}      # 生き残るもの
    delete_dict = {}        # 削除対象

    # filename,crc32,filesizeを{filename, (crc32, filesize)}にする
    for i in range(len(lines)):
        (filename, crc32, filesize) = lines.values[i]
        d[filename] = (crc32, filesize)

    for key, value in d.items():
        if value in servived_dict.values():
            delete_dict[key] = value
        else:
            servived_dict[key] = value

    def output(full_path, dic):
        with open(full_path, mode='w') as f:
            for key in dic.keys():
                # 削除対象ファイルのフルパスだけ欲しいので
                f.write(os.path.join(path, key) + "\n")

    output(os.path.join(path, "servived.txt"), servived_dict)
    output(os.path.join(path, "delete.txt"), delete_dict)

if __name__ == "__main__":
    full_path = sys.argv[1]
    classify(os.path.dirname(full_path), os.path.basename(full_path))
```

最初のfor文にて後で扱いやすいように検査対象を{filename, (crc32, filesize)}に変換している。タプルにしない場合CRC32とファイルサイズそれぞれが含まれているか検査することになるのでそれは少々ダサいかなと。あと、servived_dictは存在意義があるがservived.txtに出力しても使い道がないので出力は不要(とは言えデバッグ時には有用だった)

## あとがき

まだまだにわかPythonistaなので多分集合演算でサクっと終わってしまうような気もしているが今回はこれにて。次は各年月フォルダに生成されたdelete.txtを参照して削除処理ですか(何で書こうかな・・)
【Ruby on Rails】エラーの日本語表記

# 目標
![日本語.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/700999/383fcc38-c11c-27b8-1ad6-fa40c7f2b08a.gif)

# 開発環境
ruby 2.5.7
Rails 5.2.4.3
OS: macOS Catalina

# 前提
※　▶◯◯　を選択すると、説明等が出てきますので、
　　よくわからない場合の参考にしていただければと思います。

[【Ruby on Rails】エラーメッセージの個別表示](https://qiita.com/japwork/items/fcf2049fb4991de987a2)
こちらの記事と対比させながら見るとわかりやすいです。

# 流れ
1 gemの導入
2 config/application.rbの編集
3 config/locales/ja.ymlの作成、編集


# gemの導入
```rb:Gemfile
gem 'rails-i18n'
```
```:ターミナル
$ bundle install
```


# config/application.rbの編集
下記2行を追加。

```rb:config/application.rb
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 5.2
    config.i18n.default_locale = :ja # ←追加
    config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] # ←追加
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration can go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded after loading
    # the framework and any gems in your application.
  end
```

# config/locales/ja.yml
```yml:config/locales/ja.yml
ja:
  activerecord:
    attributes:
      user:
        email: 'メールアドレス'
        password: 'パスワード'
        password_confirmation: 'パスワード(確認用）'
        name: '名前'
```
<details><summary>補足【user:】</summary>
モデル名となります。同じインデントで記述すれば、
他のモデルの日本語表記も可能です。</details>
cloud9にRuby on Railsの開発環境を作る。

使用している🍎 が古かったので、ローカルで環境を作らずに、cloud9で勉強する為に構築w

##手順１
Rubyのバージョン確認を行う

```terminal
$ ruby -v

#インストールされているRubyのバージョンが表示される
ruby 2.6.0p0 (2018-12-25 revision 66547) [x86_64-linux]
```

##別バージョンのRubyをインストールする
Ruby2.6.2をインストールする場合

```terminal
rvm install 2.6.2
```
インストールする間は少し待ち。その後下記コマンドを実行

```terminal
$ rvm use 2.6.2
```
変更出来ているか再度確認

```terminal
$ ruby -v

#表示
ruby 2.6.2p47 (2019-03-13 revision 67232) [x86_64-linux]
```
##rvm とは
異なるバージョンのRubyを複数導入するためのソフトウェア
Rubyのバージョンを切り替えて使うといったことができまる。
**rbenv**の方が人気があるが、今回は環境構築優先で標準に入っている**rvm**を使用

→rvenv導入手順が完成したらリンク更新予定

## Rails バージョン確認

```terminal
rails -v

# 表示される内容の例1: rails5.0.0が入っている
Rails 5.0.0
# 表示される内容の例2: railsが入っていない
bash: rails: command not found
```
##別バージョンの Rails をインストールする
Rails 5.2.3 をインストールしたい場合は、 次のようにターミナルに入力。

```terminal
# Railsのバージョンを指定してインストールします。
$ gem install rails -v 5.2.3
#インストールしたバージョン確認
$ rails -v

#表示

```
##gem とは
Rubyのライブラリです。ライブラリとはプログラミングの部品を１つのファイルに集めて収納したもの。
Railsはこのgemの集合したものです。イコール**Railsもgem**と言うことになる。

## -v をつける理由
（ちょっと気になったので自分用メモ）

```terminal
#バージョンの指定をする時につかう？
$ -v
```
【VSCode】常に新しいタブでファイルを開きたい！の設定

##1. 現 状
#### ファイルをクリックすると元のタブが上書きされる
VSCodeの初期設定の場合、ツリーからファイルをクリックしていくとタブがどんどん上書きされて開かれると思います。

　　　　　　↓↓この右隣に新しいタブで開きたいのにどんどん上書きされる！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675596/05875d4d-e9f3-a310-2856-4b132bcb11c3.png)

これを「Google Chromeのように新しいタブでどんどん開けたら良いなぁ」と感じたので、<b>常に新しいタブで開くようにする設定</b>のメモを共有します。


##2. やること
#### VSCodeの設定を変更する
① `「⌘ + , 」`か`「基本設定」`からVSCodeの設定を開きます。

② 設定の中の`「ワークベンチ」`の`「エディタの管理」`というカテゴリーに、
`「Enable Preview」`という項目があるので、このスイッチをオフにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675596/78a474fc-27b6-c27c-44cd-46866bcf1210.png)
　　　　　　　　　　　↑Enable Previewのスイッチをオフにする
<br>
これだけです。あとはそのまま設定タブを閉じればOK。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675596/da713726-8704-c94d-3659-1e717db2dce1.png)
　　　　　　　　↑↑こんな感じでどんどん新しいタブを開くことができました
<br>
設定自体は`「Enable Preview」`のスイッチをオンにすれば簡単に戻せるので、軽く試してみてはいかがでしょうか。
<br>
### 追記
設定を変えない場合の別解ですが、
ファイルをダブルクリックで開くと、プレビューモードではなく編集モードとしてファイルが開かれ、
その後に開くファイルでタブが上書きされることなく維持されます。

またシングルクリックで開いたファイル(プレビューモードで開いたファイル)も、そのファイルのタブをダブルクリックすると編集モードに切り替わります。
タブのファイル名をよく見ると、プレビューモードのファイルは斜体のフォントで、編集モードのファイルは普通のフォントで表示されているのがわかると思います。
(Enable Previewの設定のところに説明がありますが)
【JavaScript】let宣言では同じ変数名は使えない、そんなことは分かっているけれど

#let宣言では同じ変数名は使えない、そんなことは分かっているけれど

##エラー内容

**「この変数名は既に宣言されています」**

何度ファイル内を見直して、同じ変数名がlet宣言されていないことを確認するも表示されるこのエラー。
見直せど、見直せど原因がわからない。

##原因

コードレビューしてもらい発覚した原因。
それはファイルをリファクタリングしたときにミスをしており、**ファイルを二重読み込みしていたこと**。


同じ操作を複数のファイルに記述していたheader箇所を外部ファイル化したときに、その外部ファイル（Bとする）内でも該当のJavaScriptファイル（Cとする）を読み込んでいたのだ。


外部ファイルBを読み込んだファイル（Aとする）の両方でJavaScriptファイルCを呼び出していたため、二重呼び出し(二重読み込み)の状態になりエラーが発生していた。

AーBーC
 |
C



```html
<html>
  <head>
    <script defer src="C.js"></script>
  </head>

  <body>
    <header>
     ~~~~~~~
    </header>

  </body>

</html>
```
header箇所を外部ファイル化するにも関わらず、htmlタグやheadタグ、bodyタグを記述してさらにJavaScriptファイルCまで読み込んでいたことがエラーの原因だった。


```html

<header>
　～～～～
</header>
```

本来、外部ファイルBではJavaScriptファイルCを呼び出す必要はなかったため削除し、header箇所だけ記述しておおもとのPHPファイルAで**require_once('headerファイル');**と呼びだすことでエラー解決できた。