OmniAICreator/Qiita-1.07M huggingface dataset:
Windows 11 Insider Preview (10.0.22631.2050) (KB5028256)で漢字変換ができなくなったときの対策方法

# 1.この記事の内容

2023/7/24にWindows 11 Insider Preview (10.0.22631.2050) (KB5028256)の累積更新プログラムを更新後，漢字変換ができなくなりましたが，いろいろ調べてIMEの設定であったことが分かったので，記事に残します．

# 2.問題の現象

2023/7/24にWindows 11 Insider Preview (10.0.22631.2050) (KB5028256)の累積更新プログラムを更新後，キーボードで入力した文字が変換できないまま確定されてしまう現象が発生しました．

具体的には，文字入力後に変換候補を表示することができず，例えば「テスト」と入力したいときに，

- ローマ字入力の場合 → ｔえｓｔ
- かな入力の場合 → てすと

と確定された状態で出力されてしまう現象です．

# 3.対策

Microsoft IMEの互換性の設定で，「以前のバージョンのMicrosoft IMEを使う」を「オフ」に設定することで，問題の現象が解消しました．

![ime_setting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/983907/976b9e08-9d08-6d68-e6bd-beae0827bd72.png)

# 3.さいごに

過去のバージョンで「以前のバージョンのMicrosoft IMEを使う」を「オン」にしないと文字入力がバグる現象が起きていましたが，本記事を執筆するにあたり最新バージョンでは解消されているようでした．

似たような問題に遭遇している方の助けになれば幸いです．
自動作曲アプリを書く

# はじめに
<!-- 発端や概要を記載 -->
卒業研究で自動作曲システムを作ったので、応用してブラウザで動くアプリを作ろうと思いました。

# 目次
<!-- タイトルとアンカー名を編集 -->
1. [アプリの概要](#Chapter1)
1. [フロントエンド](#Chapter2)
1. [バックエンド](#Chapter3)
1. [ソースコード](#Chapter4)
1. [今後の課題](#Chapter5)

<!-- 各チャプター -->
<a id="#Chapter1"></a>
# アプリの概要
キーを選択し、ダウンロードボタンをクリックすると、MIDIファイルが入ったZIPファイルがダウンロードされます。
![スクリーンショット 2023-07-24 181911.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2733784/72a82c9b-ae9d-3497-d0a2-8d622515df59.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2733784/6c069911-2cc6-93a6-65a2-e3b2ba506972.png)

![スクリーンショット 2023-07-24 192305.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2733784/25740d9b-902c-4242-c072-ef92f5bc35ff.png)

ZIPファイルを解凍するといくつかのMIDIファイルが入っています。
この中からchord.midとmelody.midを選んで、DAWソフトで適当なピアノ音源を付けてみます。
URL: https://on.soundcloud.com/reEthioEcfb3i8TR8

ドラムを付けるとこんな感じ
URL: https://on.soundcloud.com/Cc9TzK16oT8qPmH79

<a id="#Chapter2"></a>
# フロントエンド
Reactで書きました。
キーを選択してダウンロードボタンを押すと、バックエンドにキー情報のパラメータを持ったリクエストが送られるようになっています。

<a id="#Chapter3"></a>
# バックエンド
Goで書きました。
フロントエンドから送られたリクエストのパラメータから、音楽(MIDIファイル)を作ります。
キーに合わせて、コード、メロディ、ドラムのMIDIファイルを生成し、ZIPファイルにしてクライアントに送信します。
コード進行を生成する際は、和音の情報を持つデータベース(SQLite)を使用します。

<a id="#Chapter4"></a>
# ソースコード

https://github.com/Rocky-6/generate_trap_beat

Dockerを使用してローカル環境で使えるようになっています。
AWSのEC2上にもデプロイしています。
URL: http://35.200.71.243:3000/

<a id="#Chapter5"></a>
# 今後やりたいこと
・曲調のパラメータを増やす
・ブラウザ上で音楽を流せるようにする
・変更してすぐにデプロイできる環境を構築する
新人に贈る助言の一つ

IT業界で仕事をし始めたのは、３０歳になってから。　

２０代も、プログラムは組んでいたし、データ解析したからもらえた賞もある。

それでも仕事は営業と人事。　
誰に聞いてもIT業界で働いていたとは認めてもらえない。

２０代で、IT関連の発表は、対外発表で、人事記録の電産化（水道協会）くらい。　

そんなIT業界に２０代で入り損ね、３０代で、外資系企業に入り損ねた人生で、新人に贈る助言を一つ。

 自己中を通し、振り回されて、世の中のためになればいい
# 自己中
10歳まで　自己中の方がいいという気がする。　
小さい頃から、まわりに気を使いすぎた結果を想定する。　

一生周りに気を遣うだけの人生。　
周りが、ずっと上り調子であれば、自分の人生も上り調子のままでいいかも。　
困るのが、裸の王様のとりまきになって、裸の王様ごと、最後は倒れてしまうことかも。　

まわりに気を遣うのなら、気を使う相手が、裸の王様でないことを確認しよう。　
裸の王様のまわりには、気を遣う人が山のようにいて、そこで気を使ってもなんにもならない。　

５年、１０年は、精神的に楽な生活ができるだろう。裸の王様の言うことだけ気９ていればいいのだから。

自己中を勧めるのには、訳がある。高校の頃、一学年上と、一学年下で、自殺があった。
一人は、ほとんど知らない人。一人は、それなりに知っている人。

自殺を止められなかった自分が嫌になった。
いまだに、何をすれば、自殺が止められたかはわかっていない。　

葬儀に出たわけでもなく、追悼集会を開催したわけでもない。

今、言えることは、自己中でもいいよってことくらい。　

自己中すぎると、自分が死ぬことは、自分で決めるんだっって、落とし穴にはまっていくのかもしれない。　

かけられる言葉は、１０年後に君を待っている人がいるから、今死なないでということくらい。

１０年間、自分のやりたいことをやり続けられれば、世の中の誰かが評価してくれるのが経験則。
#　学校、組織、会社、顧客に振舞わされる
２０歳までは、学校、組織、会社、顧客に振舞わされるのもいいだろう。　
自己中と、組織文化の均衡がとれるようになれば。　

学校、組織の中は、裸の王様がいっぱいいるか、村社会をつくりあげているか。
そんなものにつきあうのも、１０年以内なら、人間として駄目にならないかも。

顧客がして欲しいと言ったことの半分は無駄。仮説(11) 図(24)
https://qiita.com/kaizen_nagoya/items/c23b73684e22d0b0c1cd

お客様からお聞きした話。
https://qiita.com/kaizen_nagoya/items/94a17a78f33dc8356a15

顧客指向のプログラミング(customer oriented programming)。仮説(82)
https://qiita.com/kaizen_nagoya/items/6f3bc42253486b4b4818

顧客指向の要件定義。仮説(91)
https://qiita.com/kaizen_nagoya/items/79c608ebba14db53d353





　
20歳まで、相手に振り回されて
30歳までに 世の中に出ればいい。
2023/7/25主にITとかセキュリティの記事

Windowsリモートデスクトッププロトコル（RDP）に複数の脆弱性、注意を(7/23)
https://news.mynavi.jp/techplus/article/20230723-2732337/

Apple、ビジネス向けのセキュリティ機能を紹介する新たなページを公開(7/21)
https://iphone-mania.jp/news-545979/

OpenText、 サイバーセキュリティ脅威レポート(日本語版)を発表多層防御によるセキュリティアプローチの必要性を強調(7/22)
https://japan.cnet.com/release/30879358/?ref=rss

AWS Well-Architected Framework で出てくる難しい言葉を俺なりに分かりやすくしてみた(7/22)
https://qiita.com/mroldtype/items/b7952cff09052a1aa16d

フランスのメガバンクが初の仮想通貨取引・保管ライセンスを取得(7/21)
https://nextmoney.jp/?p=59852

フィッシング詐欺に遭ったかも？ すでに銀行口座から引き落とされていたらどうなる？(7/22)
https://news.yahoo.co.jp/articles/8605b7031d1ad20317de0b1aa73ab5fcc1b94e13?source=rss

大切な個人情報を守ろう！ 最近よく聞く「ワンタイムパスワード」とは？(7/23)
https://news.yahoo.co.jp/articles/92c46be3bf9460d56cadd468958b1459ebce9488?source=rss

＜独自＞サイバー防衛人材育成へ新団体　元次官ら調整役に(7/23)
https://www.sankei.com/article/20230723-IHSF7KVBKZPXVNG3A46WYN2RGI/

レシートでポイ活っておいしい話だけど安全なの？ アプリの選び方や注意点は？(7/23)
https://news.yahoo.co.jp/articles/59cf85ed9b09b4b3531fcfb85cbe886d34826ab8?source=rss

三菱電機と富士フイルムHDに聞く、サイバー攻撃被害の教訓とその後の対策(7/24)
https://xtech.nikkei.com/atcl/nxt/column/18/02532/072100001/

開発者支援に力を注ぐGoogle Play、「サイドローディング」義務化をどう見ているのか(7/24)
https://xtech.nikkei.com/atcl/nxt/column/18/00086/00269/

「システムトラブルの対応中なのに、休むことも仕事だなんて…」(7/24)
https://xtech.nikkei.com/atcl/nxt/column/18/02518/00008/
pushとpullで毎回Enter passphraseと言われてしまう(Windows)

pullとpushをするたびに毎回passphraseを求められる．
面倒くさい．．．passphraseを入力しないでsshの操作を行いたい．けど若干手間取ったのでその過程を備忘録的に残しておく．

```
Enter passphrase for key '/c/Users/user/.ssh/id_ed25519':
```
うーん...公開鍵と秘密鍵関連のsshらへんの設定がうまくいっていない模様．

まず，接続先がsshになっているかを確認．httpsになっていると毎回passphraseを求められる．もし`git@github.com:`で始まっていたらOK．httpsで始まっていたらssh用のものに変更が必要．

```
git config remote.origin.url
// httpsで始まっていたら設定を変更する
git remote set-url origin git@github.com:<ユーザID>/＜リポジトリ名＞.git
```

まだpassphraseを聞かれる．

リモートとやり取りする際の設定ファイルの有無を確認する．
```
cd ~/.ssh
ls
// configが存在しているか確認．存在していない場合は作成する．
touch config
またはPowerShellの場合，touchが使えないので
New-Item config
```

configには以下の内容を記述する.
VSCodeで編集する場合には同じディレクトリで`code config`で開ける．
```
Host github
HostName github.com
IdentityFile ~/.ssh/id_ed25519
User git
```

sshの接続を確認してみる
```
ssh -T git@github.com
```
しかし，まだpassphraseを求められる．
```
Enter passphrase for key '/c/Users/user/.ssh/id_ed25519':
```

ssh-agentを使ってみる．しかし，そんなファイルはないと言われてしまった．
```
ssh-add C:\Users\user\.ssh\id_ed25519
// 存在してないと怒られる
Error connecting to agent: No such file or directory
```

windowsの場合?，ssh-agentが起動していないことがある．
タスクマネージャーから確認しに行く．スタートでタスクマネージャーを検索し，開く．タスクマネージャーの検索窓で`openSSH`と検索．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1789530/0201f6fe-63aa-f566-04c8-b866b16ee509.png)
こんな感じのが出てくる(はず)
右クリックしてサービス管理ツールを開くを選択．別ウィンドウが開く．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1789530/5fceeb03-3d4c-3d0f-afa8-f9296a2fcefa.png)

ここで，OpenSSHを選択し，ダブルクリック．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1789530/f9b2b2e5-8fe6-543a-1ef2-e6539f7fce7a.png)

スタートアップの種類が無効になっていたら自動にし，OKをクリック．これでPC起動と同時にssh-agentが起動するようになった．

再度ssh-addできるか確認．今度はpassphraseを聞かれた．普通にpassphraseを入力し，`Identity added: c:\Users\user\.ssh\id_ed25519 `と言われて通った．
もう一度sshを確認してみる．今度は何も聞かれずに通った！
```
ssh -T git@github.com
Hi yukino-sasaki! You've successfully authenticated, but GitHub does not provide shell access.
```

しかし，pullをしてみるとまだpassphraseを聞かれる．．．と思っていたら，先ほどのssh-agentの設定をgitに反映させないといけないらしい．Gitの設定を、WindowsのOpenSSHを見に行くように変更する．これがないとVSCodeからpushしようとしたときにid_rsa.pubというデフォルトの名前以外で鍵を作った場合の C:\Users\user/.ssh/config を見に行ってくれない．

```
git config --global core.sshCommand "C:/Windows/System32/OpenSSH/ssh.exe"
```

これで無事にpullができた！

参考
https://untitledreport.com/windows%E7%92%B0%E5%A2%83%E3%81%AB%E3%81%A6git%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA%E3%81%B8ssh%E6%8E%A5%E7%B6%9A%E3%81%99%E3%82%8B/
Twitterのアイコンを消すChrome拡張を作りました

https://github.com/narikei/remove-twitter-icon/releases/tag/0

なくなった方がスッキリする

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7132/605d1344-5261-9b68-9f57-001ab9770a5f.png" width="300">

```js
(() => {
  const removeIcon = () => document.querySelector('h1[role="heading"] svg')?.remove();

  removeIcon();

  setInterval(() => {
    removeIcon();
  }, 100);
})();
```

コードはシンプルに100ms毎にh1のsvgがあるのかをチェックして、あれば要素を削除しています。
[AWS IAM Identity Center] SSO環境下でAWS CodeCommitの認証を行う

※この記事は[自分のブログ](https://blog.morikapu.com/awssso-with-awscodecommit/)から持ってきたものです。Qiitaにも残しておきたいため投稿しています。

会社などでAWS IAM Identity Center + SSOを使用しているときにちょっと躓いたのでメモ。
AWS Cliなどはインストールしてある前提とする。

# 環境
Windows + Git for Windows + AWS Cli

# 1. SSOの設定
コマンドプロンプトで以下を実行
※aws configure sso はGit Bashでは実行できないので注意
```
aws configure sso
```

質問にいくつか聞かれるので入力していく。
SSO Start URLなどはAWS IAM Identity Centerポータルページで「Command line or programmatic access」を押すと表示される。
```
SSO session name (Recommended): <適当な名前>
SSO start URL [None]: <ポータルページで入手したURL>
SSO region [None]: <ポータルページで入手したSSO start URLの次に記載されているリージョンコード>
SSO registration scopes [sso:account:access]: <何も入力せずEnter>
```

この後、認証ページに飛ばされるので許可する。
許可後、再びいくつか質問されるので入力していく。

```
CLI default client Region [None]: <デフォルトのリージョンコード 例: ap-northeast-1>
CLI default output format [None]: <何も入力せずEnter>
CLI profile name [<アカウントID>]: <分かりやすいプロファイル名を入力してEnter（後で使います。）>
```

# 2. .gitconfigファイルの編集
続いて、ホームフォルダの`.gitconfig`ファイルをテキストエディタなどで開き、以下を追記する。
```
[credential "https://git-codecommit.<リージョンコード>.amazonaws.com"]
    helper = !aws --profile <CLI profile nameで入力した名前> codecommit credential-helper $@
    UseHttpPath = true
```

# 3. 通常通りCloneなどする
あとは通常通り`git clone`を行うことができる。途中、Git Credential Managerというウィンドウが表示されるが×を押して閉じてOK。

# トークンの有効期限が切れたら
トークンは永続的ではないので、有効期限を迎えると以下のエラーが表示されることがある。
```
Error when retrieving token from sso: Token has expired and refresh failed
```

そのため、コマンドプロンプトで以下のコマンドで実行・再ログインしトークンを更新する。
```
aws sso login --profile <CLI profile nameで入力した名前>
```
Alpineでsudoを使う

rootで常用するのはよくないですから、sudoと一般ユーザーで最小限の権限で使いたい。

# インストール
rootで
```shell
apk add sudo
```
を実行。
# 設定
rootで
```shell
visudo
```
を実行。
この場合はtestというユーザーに全権限を借りれるようにする。詳しい書式はmanや他の人のドキュメントを参照してください。
```d:/etc/sudoers
test ALL=(ALL:ALL)
```
書式チェックしてもらう
```shell
visudo -c
```
怒られなかったら設定完了です。
Fast APIとAzure SQL Databaseの接続方法（ローカルはMySQL）

## PythonのFastAPIとAzure SQL Databaseを接続したい
FastAPIでAPIを開発してデプロイする時、開発環境のDBはMySQL、本番環境ではAzureのSQL Databaseを利用するとします。

## 開発環境用（MySQL）のデータベース接続方法
#### 1. 必要なライブラリをインストール
mysqlへの接続に利用...```pip install pymsql```
環境変数の読み込みに利用...```pip install python-dotenv```

#### 2. 接続情報をenvファイルに記載
```.env
SERVER_URL=<MySQLサーバーのURL>
DATABASE=<データベース名>
USER_NAME=<ユーザー名>
PASSWORD=<パスワード>
SERVER_PORT=<ポート番号>
```

#### 3. 接続するデータベースURLを作成
```config.py
import os, urllib
from dotenv import load_dotenv

load_dotenv()

SERVER_URL=os.environ["SERVER_URL"]
DATABASE=os.environ["DATABASE"]
USER_NAME=os.environ["USER_NAME"]
PASSWORD=os.environ["PASSWORD"]
SERVER_PORT=os.environ["SERVER_PORT"]

DATABASE_URL = f"mysql+pymysql://{USER_NAME}:{PASSWORD}@{SERVER_URL}:{SERVER_PORT}/{DATABASE}"
```

#### 4. database.pyで読み込み
```database.py
engine = create_engine(
    DATABASE_URL,echo=False, pool_pre_ping=True
)

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()
        
```

## 本番環境用(Azure SQL Database)のデータベース接続方法
#### 1. 必要なライブラリをインストール
接続にはODBC Driverを利用します。もしローカルで本番環境との接続を確認したい場合は、ローカル環境にもODBC Driverをインストールする必要があります。
##### Mac用
https://learn.microsoft.com/ja-jp/sql/connect/odbc/linux-mac/install-microsoft-odbc-driver-sql-server-macos?view=sql-server-ver16

##### Linux用
https://learn.microsoft.com/ja-jp/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver16&tabs=alpine18-install%2Calpine17-install%2Cdebian8-install%2Credhat7-13-install%2Crhel7-offline

##### Windows用
https://learn.microsoft.com/ja-jp/sql/connect/odbc/download-odbc-driver-for-sql-server?view=sql-server-ver16

#### 2. 接続情報をenvファイルに記載
こちらに記載する情報はAzure Portalから作成したSQL Databaseのリソースに行って確認してください。
```.env
SERVER_URL=<SQL DatabaseのURL>
DATABASE=<データベース名>
USER_NAME=<ユーザー名>
PASSWORD=<パスワード>
DRIVER='{ODBC Driver 18 for SQL Server}' or <インストールしたバージョンのODBC>
```

#### 3. 接続するデータベースURLを作成
SQL Databaseへの接続方法は何種類かありますが、今回はODBCとSQL認証を利用しています。
他にもC#やPHP、Goには専用の方法があったりしますので、Azure PortalのSQL Databaseリソース内の「接続文字列」を確認してみてください。
```config.py
import os, urllib

load_dotenv()

SERVER_URL=os.environ["SERVER_URL"]
DATABASE=os.environ["DATABASE"]
USER_NAME=os.environ["USER_NAME"]
PASSWORD=os.environ["PASSWORD"]
DRIVER=os.environ["DRIVER"]

DATABASE_URL = urllib.parse.quote_plus(f'Driver={DRIVER};Server=tcp:{SERVER_URL};Database={DATABASE};Uid={USER_NAME};Pwd={PASSWORD};Encrypt=yes')
DATABASE_URL = f'mssql+pyodbc:///?odbc_connect={DATABASE_URL}'
```

#### 4. database.pyで読み込み
こちらはローカル環境の時と同様です。
```database.py
engine = create_engine(
    DATABASE_URL,echo=False, pool_pre_ping=True
)

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()
        
```
yaml内でエスケープに苦戦した話

## あらすじ
yaml内で以下のような処理をしたい（イメージ）
<pre>RES = $(curl -X POST -d [なんかjsonデータ] [以下、URLなどの処理に必要なあれこれ])
# 以降でRESの値を使った処理をつらつらと
</pre>
渡したいjsonデータはこんな感じ。
<pre>{
	"親階層": {
		"子階層1": "データ1",
		"子階層2": "データ2"
	}
}
</pre>
このとき、方法としては3パターン検討していた。
その内1つがちょっと躓いたので備忘録代わりのメモとして残す。


## ①jsonファイルにして渡す
適当なjsonファイル(data.jsonとか)を作成し、中身を記載。
中身はあらすじのjsonデータをそのまま使用します。

できたら以下のような形でcurlに渡す。
<pre>RES = ${curl -X POST -d @test.json [以下、URLなどの処理に必要なあれこれ])
# 以降でRESの値を使った処理をつらつらと
</pre>

すんなり通った。

## ②直に渡す
データを以下のようにエスケープ。
<pre>'{"親階層": {\"子階層1\": \"データ1\",\"子階層2\": \"データ2\"}}'
</pre>

こいつをそのままdオプションに当てはめる。
<pre>RES = ${curl -X POST -d '{"親階層": {\"子階層1\": \"データ1\",\"子階層2\": \"データ2\"}}' [以下、URLなどの処理に必要なあれこれ])
# 以降でRESの値を使った処理をつらつらと
</pre>
ちょっと苦戦したものの通った。

## ③変数に格納して渡す
こいつが一番問題児だった。

以下のように変数を作成。
<pre>DATA: '{"親階層": {\"子階層1\": \"データ1\",\"子階層2\": \"データ2\"}}'
</pre>

こいつをdオプションに当てはめるのだか、そのまま($DATA)指定しても構文でエラーになってしまう。
エスケープを色々試した結果、正解はこんな感じ。
<pre>RES = ${curl -X POST -d "$DATA" [以下、URLなどの処理に必要なあれこれ])
# 以降でRESの値を使った処理をつらつらと
</pre>
変数をダブルクォーテーションで囲むことでした。
わからんて...........................

以上。
Androidで読み取り専用のNFCタグを作成する

## この記事は
Android端末でNFCタグにURIを書き込む際に二度と上書きされないようにNFCタグを読み取り専用にしたい場合が出てくると思います。今回はコーディング一切無しでそれらを実現する方法をご紹介します

## 用意するもの
### 使用するアプリ
NFCタグを読み取り・書き込みできるアプリがPlayStoreにあるのでそちらをダウンロードしましょう

https://play.google.com/store/apps/details?id=com.wakdev.wdnfc&hl=ja

### 使用するNFCタグ
特に理由はないですが比較的安価で評価が高いものを選びました

https://www.amazon.co.jp/gp/product/B0866WL9QK/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&th=1

## 具体的な方法
### 書き込み可能か確認
まず、何もデータが入っていない空の状態のNFCタグを読み取って書き込み可能であることを確認します。「読む」のタブに切り替えるとNFCタグの読み取り待機状態になるのでお手持ちの端末のNFC読み取り位置に合わせて「書き込み可能」かつ「読み込み専用を作成可能」であることを確認してください

|読み取り待機|読み取り完了|
|----|----|
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479939/e232a8f5-da17-71c1-2df3-a3c38a8111d4.png" width="300" /> | <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479939/e947481a-48e5-6f32-93c3-88195acbe03f.png" width="300" />

### URIを書き込み
URIを実際にNFCタグに書き込んでいきます`https://`で始まるものもいいですしディープリンクでも大丈夫です

「書く」のタブに切り替えて「カスタムURL/URI」を選択します
|書き込みタブ選択|カスタムURL/URIを選択|
|----|----|
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479939/fc37ba73-e512-2818-9c48-19b653f9a027.png" width="300" /> | <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479939/29bf9384-271c-5e84-9dc4-d0388776464f.png" width="300" />

用意したURIをフィールドに入力して「OK」→「書く」を選択しましょう

|URI入力|書くを選択|
|----|----|
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479939/72742ea0-b019-c6cf-939b-770b187ac084.png" width="300" /> | <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479939/719acc96-a79c-bc44-f4f6-578b0164a6cc.png" width="300" />

NFCタグへの書き込み待機状態となるのでNFCタグを端末にかざして書き込んでください
再度NFCタグを読み込むと指定したURIがレコードに書き込まれていることが確認できます

|書き込み待機|レコードに指定したURIが書き込まれたことを確認|
|----|----|
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479939/353c83a5-4238-5033-7a7c-892d9ccc538b.png" width="300" /> | <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479939/d94ab6ba-84d0-ed7d-94ac-272818a74bbf.png" width="300" />

### 上書きできないように読み込み専用に設定
このままでは悪意のある攻撃や意図しない上書きがされてしまう恐れがあります。そのためにNFCタグを実際に運用する際は読み込み専用にする必要があります

「その他」のタブに切り替えて「タグをロック」を選択します
読み取り専用にしたいNFCタグを端末にかざしてロックします（データがあらかじめNFCタグに書き込まれている想定です）
|タグをロックを選択|読み込み専用にするための書き込み待機|
|----|----|
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479939/7f66fa1b-6d55-fd8a-8dee-ad85df2c20e6.png" width="300" /> | <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479939/ca2fcf0a-33cc-1f05-8c96-91746418a7d0.png" width="300" />

再度NFCタグを読み込むと「書き込み可能」の値が`No`になっていることが分かります
書き込みをしようとすると書き込みエラーが発生して書き込めないようになっていることも確認できます!
|書き込みができないことを確認|読み込み専用にするための書き込み待機|
|----|----|
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479939/6a81cd4a-4068-8a49-8dde-4801c79a2d9b.png" width="300" /> | <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/479939/ef11386a-1cf3-6290-8d28-86e0175c75da.png" width="300" />
【AWS Lambda】複数のLambda関数を１つのコンテナイメージにまとめる

## やりたいこと・背景
Lambdaをコンテナから作成する場合、Lambda関数が複数ある場合は関数ごとにイメージをビルドしなければならないのか？という単純な疑問が出てきた。その場合、イメージの管理が複雑になりそうなので、１つのイメージで複数のLabda関数を管理できないかと考えた。


## 結論
ランタイムイメージをビルドする際、各Lambda関数ごとに`ディレクトリを分け`、関数作成時に`ENTRYPOINT` を指定することで実現可能。

同一ディレクトリに main関数 を持つバイナリが複数存在すると、実行時にどの main関数 を呼び出すべきかが不明瞭になり、うまく動作しない。そのため、<font color='darkorange'>**各Lambda関数は独自のディレクトリに配置される必要がある。**</font>

ランタイムイメージで関数ごとにディレクトリを分けることで、各Lambda関数が独立して実行されるようになる。

## 概要図
![複数のLambda関数を１つのイメージで管理 (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/43f60381-2e96-525d-98dd-cdce2fe554d6.jpeg)



## ローカル環境の準備
具体的な手順を以下のとおり

- **フォルダ構成**
```
root/
　├ Dockerfile
　├ main1.go
　├ main2.go
　└ main3.go
```

- **Dockerfile作成**
```docker:Dockerfile
# ベースイメージの指定とビルドステージの開始
FROM golang:1.17-buster as builder

# 作業ディレクトリの設定
WORKDIR /go/src/lambda

# 依存関係のダウンロード
COPY go.mod go.sum ./
RUN go mod download

# ソースコードのコピー
COPY ./ ./

# ビルドの実行
RUN go build -o function1 ./main1.go
RUN go build -o function2 ./main2.go
RUN go build -o function3 ./main3.go


# ランナーステージの開始
FROM debian:buster as runner

# 作業ディレクトリの設定
WORKDIR /app/

# ビルドステージからバイナリのコピー
COPY --from=builder /go/src/lambda/function1 /app/function1
COPY --from=builder /go/src/lambda/function2 /app/function2
COPY --from=builder /go/src/lambda/function3 /app/function3
```
    
- **main1.go, main2.go, main3.go**
```golang:main1.go
package main

import (
       "fmt"
       "context"
       "github.com/aws/aws-lambda-go/lambda"
)

type MyEvent struct {
       Name string `json:"name"`
}

func HandleRequest(ctx context.Context, name MyEvent) (string, error) {
       return fmt.Sprintf("Hello! for func1", name.Name ), nil
}

func main() {
       lambda.Start(HandleRequest)
}
```

- **モジュール初期化**
`$ go mod init example.com/hello`

- **モジュールの依存関係を最適化**
`$ go mod tidy`


## イメージビルド
`$ docker build --no-cache -t lambda-go-multibuild . `
※no-cacheオプションでキャッシュを無効化

#### バイナリ名の指定
`RUN go build -o function1 ./main1.go`
main1.go がコンパイルされると通常は main という名前でバイナリが作成されるが、-oオプションを利用して自分で指定する

## ECRプッシュ
ECRリポジトリからプッシュコマンドを表示させて、ECRにイメージをプッシュする
※詳細な手順は下記を参照
[【Amazon ECR】AWS CLI を使ってリポジトリへプッシュする](https://qiita.com/sakuchi/items/90886663f7adf3941472)


## Lambda関数の作成
ECRのイメージを使ってLamdaを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3412108/e2486a33-8431-ebd0-11ad-0b3eb8897e66.png)
関数名： 他の関数と重複しなければ好きな名前でOK
コンテナイメージURI：先ほどプッシュしたECRイメージを選択
**ENTRYPOINT**： **<font color='darkorange'>/app/function1</font>**
  Dockerfile ランナーステージでコピーした goバイナリのパスを指定
Vue masteryでEvan YouからVueの構造を学ぶ

## Vue mastery
vue masteryとは、vueの学習用のビデオ教材を提供しているサービスである。
この教材の中に、vueの創設者であるEvan YouからVueを学べるコースがある。

https://www.vuemastery.com/courses/vue3-deep-dive-with-evan-you/vue3-overview/

この講義からrender function話が面白かったので、自分なりに以下まとめメモを書いてみる。

## ブラウザの基礎
ブラウザには大きく分けて、レンダリングエンジン、Javascriptエンジン、ネットワーク部、Data Storageの４つの機能を持つ

ここではレンダリングエンジンに注目する。

Webページのレンダリングは
htmlの解析→DOMツリーの構築→CSSのパースとStyleの反映→レイアウト→Paint→Composit&Render
という流れで行われる。

ここでvueは（Reactなども）html parseの際、DOMに干渉することで、高速なリアクティビティを実現させている。
DOMとはブラウザから提供されたWeb APIである。直接DOM（virtual DOMに対して、real DOMと呼ばれる）を操作することもできるのだが、VueやReactは、virtual DOMと呼ばれるデータ構造を持ち、そこからreal DOMを構築する。

## App.mount()で起こること

virtual DOMからreal DOMが構築される過程をマウントと呼ぶ。
公式では以下のように記述されている。

>レンダリングパイプライン
コンパイル：Vueテンプレートは、レンダー関数（仮想DOMツリーを返す関数）にコンパイルされます。このステップは、ビルドステップによって事前に実行することも、ランタイムコンパイラを使用してその場で実行することもできます。
マウント：ランタイム・レンダラーがレンダー関数を呼び出し、返された仮想DOMツリーをウォーキングし、それに基づいて実際のDOMノードを作成する。このステップはリアクティブ効果として実行されるため、使用されたすべてのリアクティブ依存関係を追跡します。
パッチ：パッチマウント中に使用された依存関係が変更されると、エフェクトが再実行されます。このとき、更新された新しい仮想 DOM ツリーが作成されます。ランタイム・レンダラは新しいツリーを走査し、古いツリーと比較し、必要な更新を実際の DOM に適用します。
![render-pipeline.03805016.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2724475/96ab9289-3261-e2ae-8f27-8b6d74345883.png)
[vue公式 rendering-mechanism](https://vuejs.org/guide/extras/rendering-mechanism.html#render-pipeline)

先ほど述べたように、vueにおけるリアクティビティーを、VDOMを利用して効率的に行うために、コンパイラーはテンプレートを解析する際に、レンダリングでVdomを作るときに、vueは今後patch関数によってUpdateを行う際にどこを比較すれば良いか、反対に言うと、どこを見逃して良いかを記録している。このようにして作られるVueのVDOMはCompiler-Informed Virtual DOMと呼ばれる。

レンダー関数構築時、またpatch実行時点でvueがどのようにDOMの更新を最適化するのかを理解することが重要である。その理解が不十分だと、リアクティブだと思っていたものがきちんと反映されなかったり、逆に不必要に再レンダリングを繰り返すことによってパフォーマンスの低下やバグを招きかねない。


## レンダー関数の中身
それではこのレンダー関数はどのように作られているのだろうか？
vueではレンダー関数は`h`を利用して作られる。以下のような形をとる。
```js
import { h, createApp } from 'vue'

const vnode = h(
  'div', // type
  { id: 'foo', class: 'bar' }, // props
  [
    /* children */
  ]
)

// Vueアプリケーションインスタンスの構築
// VDOMを返すレンダーファンクションを引数に取る
// 以下のコードで、idがappのNodeに<p>This is render function</p>というhtmlを追加できる。
const app = createApp({
   render() {
      return h('p', 'This is render function'); 
   },
}).mount('#app');

```
hはhyperscriptの略で、vnodeを返すjavascriptを意味する。
vue固有のものではなく、web APIではDOMParserというものでStringからDOMを返すこともできるし、MITライセンスのhyperscriptも存在する。

https://github.com/hyperhype/hyperscript

Vueファイルのtemplateがレンダー関数にコンパイルされる際に、v-on, v-if, v-forなどは解釈され、適切な最適化が行われたVDOMを返すレンダー関数が作られていく。
vue masteryではstatic hoistingやPatch Flagsという最適化の例が紹介されていた。

## まとめ
vue masteryにおけるdeep dive with Evan Youの講義では、rendering function以外にも、mount functionやpatch function、リアクティビティの実現といったvueの根幹をなす機能がどのようにして構築されているかが解説されている。
月額費用はかかるものの、得るものはかなりあると思うし、また動画はダウンロードもできるので見直すことも簡単である。
まだ理解しきれていない部分も多分にあるので、動画を繰り返し見返しつつ公式ドキュメントやMdnを読み進めていきたい。


### 参考

https://medium.com/jspoint/how-the-browser-renders-a-web-page-dom-cssom-and-rendering-df10531c9969

https://algodaily.com/lessons/introduction-to-js-engines-and-runtimes
LaravelのMVCモデルはRMVCモデルじゃんの話

## Laravelなんとなくわかってきたぞ、、、！

本日もLaravel日和。
本日は、データベースからデータを取得してViewに表示することができたぞ。

Laravelの概要もなんとなくわかってきた。
MVCというのはなんとなく知っていたが、
今日は真理に一歩近づいたかもしれない。

### route
使うのはweb.phpとauth.php(laravel breeze)を使用する。
そのまんまルーティングの設定。
こいつへの記述を忘れてエラーになりまくった
今後は忘れない（忘れる）

### model
主にバックエンド操作を記述する
今回はデータベースへデータを入れたり取り出したり
更新したり、削除したり。
いわゆるCRUD操作をすルため

### view
ブラウザ上に表示される部分。
フロントエンドやね。
react触ってたからここはなんかイメージしやすい。
データベースからとってきたデータとかを自在に表示するのが私の夢。

### controller
こいつが曲者。
modelとviewの架け橋になるやつで、
model→controller→view
view→controller→view
の様に、あちらを立てればこちらが立たず、
両方立てねばエラーが出ちゃうのだ。

という感じで理解しております。

データを取得できる様になったから
明日は取得したデータを整形していい感じに表示させるぞ！


おわり
AzureOpenAI chatgptのログをノーコードで収集する

## はじめに
gptをなどといったOpenAIAPIのログを取得する際の一例として、AzureAPIManagementService(以下APIM)を活用する方法が、[Azure OpenAI Serviceリファレンスアーキテクチャ（6章）](https://www.microsoft.com/ja-jp/events/azurebase/blog/aoai-referencearchitecture-release/)に
て紹介されたので、実際の実装例をご紹介いたします。既存のアプリに変更を加えることなく会話履歴のロギングが可能となるため、こちらの方法が最も手軽ではないかと思います。
## 対象者
この記事は下記のような人を対象にしています。

- OpenAIAPIの会話履歴を収集したい人
- DB構築など面倒なことはなるべく避けて手軽に実装したいたい人
- Azureを使用している人

## 構成イメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2910804/68374f2f-92e9-56da-e9cd-28f5aa5e2057.png)
APIMの大まかなイメージは、あるAPIと代理で通信するAPIをAzure上に作成し、対象とするAPIを管理するようなイメージです。
OpenAIとの通信をAPIMで中継して、会話履歴をLogAnalyticsに出力していきます。AzureopenAIAPIの仕様を一切変えずに実装できるため、アプリで指定しているホストをOpenAIのものからAPIMのものに置き換えるだけでログの収集が可能となります。

## 手順
* __1,APIの作成__
AzurePortalからAPIMのリソースを作成後、画面左のブレードより①APIを選択→②AddAPI と進みAPIを作成していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2910804/7cd5ee1e-7f41-3206-3c49-13f18e3ed66d.png)

* __2,operationの追加__
APIMのエンドポイントを作成します。今回は特段問題ないためワイルドカードを設定しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2910804/c399d480-e60e-0dff-08b2-3a709b55f491.png)



これでAPIMのホストに対するPOST要求はパスに関係なく扱えるようになります。適宜その他のメソッドも登録しましょう。

* 3,Backendの作成

次にBackendを入力していいきます。🖋のマークをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2910804/6a226735-cfd1-2962-8738-afadb51b73bd.png)


以下の画像のように設定します。ServiceURLには使用するAzureOpenAIのホスト名を入力します。（*https://{your_openAI_resource_name*}.openai.azure.com/）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2910804/6dd6b550-bb68-a740-e074-ea1a4fbdd140.png)

以上でAPI作成に関する設定はほぼ完了となります。以降は製品の追加から作成したAPIを追加し、公開することで呼び出し可能となります。


## おわりに
AzureAPIMを利用したログ収集について、前段階となるAPIの作成部分についてまとめました。実際にログを収集するためには、LogAnalyticsの設定も必要となります。時間を見つけてそちらに関しても追記予定です。
gdisk を使ってみる

# 環境

- Ubuntu Linux 22.04
- SEAGATE BarraCuda ST8000DM004

# fdisk では 2T サイズのパーティションになる

```
$ export DIST=/dev/sda 
```
として、
```
$ sudo fdisk $DIST
```
としたら、
```
fdisk (util-linux 2.37.2) へようこそ。
ここで設定した内容は、書き込みコマンドを実行するまでメモリのみに保持されます。
書き込みコマンドを使用する際は、注意して実行してください。

このディスクのサイズは 7.3 TiB (8001563222016 バイト) です。DOS パーティションテーブル形式では 2199023255040 バイト以上のドライブには セクタサイズ 512 バイトを使用することができません。GUID パーティションテーブル (GPT) 形式を使用してください。
```

となっていて、パーティション作っても2Tサイズになってしまった。

```
デバイス   起動 開始位置   最後から     セクタ サイズ Id タイプ
/dev/sda1           2048 4294967295 4294965248     2T 83 Linux
```

いつも容量の小さなストレージしか初期化していなかったので、気が付かなかった。 GPT 形式で初期化するには gdisk らしい。

# gdisk 

```
$ sudo gdisk $DIST
GPT fdisk (gdisk) version 1.0.8

```


```

Partition table scan:
  MBR: MBR only
  BSD: not present
  APM: not present
  GPT: not present


***************************************************************
Found invalid GPT and valid MBR; converting MBR to GPT format
in memory. THIS OPERATION IS POTENTIALLY DESTRUCTIVE! Exit by
typing 'q' if you don't want to convert your MBR partitions
to GPT format!
***************************************************************

```
まずは内容表示
```
Command (? for help): p
Disk /dev/sda: 15628053168 sectors, 7.3 TiB
Model: ST8000DM004-2U91
Sector size (logical/physical): 512/4096 bytes
Disk identifier (GUID): BD0AF411-7034-4543-8038-5163340982AE
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 15628053134
Partitions will be aligned on 2048-sector boundaries
Total free space is 11333087853 sectors (5.3 TiB)

Number  Start (sector)    End (sector)  Size       Code  Name
   1            2048      4294967295   2.0 TiB     8300  Linux filesystem
```
fdisk で作った 2T のパーティションを削除。
```
Command (? for help): d
Using 1

Command (? for help): p
Disk /dev/sda: 15628053168 sectors, 7.3 TiB
Model: ST8000DM004-2U91
Sector size (logical/physical): 512/4096 bytes
Disk identifier (GUID): BD0AF411-7034-4543-8038-5163340982AE
Partition table holds up to 128 entries
Main partition table begins at sector 2 and ends at sector 33
First usable sector is 34, last usable sector is 15628053134
Partitions will be aligned on 2048-sector boundaries
Total free space is 15628053101 sectors (7.3 TiB)

Number  Start (sector)    End (sector)  Size       Code  Name
```
改めてパーティションをフルサイズで作成。
```
Command (? for help): n
Partition number (1-128, default 1): 
First sector (34-15628053134, default = 2048) or {+-}size{KMGTP}: 
Last sector (2048-15628053134, default = 15628053134) or {+-}size{KMGTP}: 
Current type is 8300 (Linux filesystem)
Hex code or GUID (L to show codes, Enter = 8300): 
Changed type of partition to 'Linux filesystem'
```
書き込んで終了

```
Command (? for help): w

Final checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING
PARTITIONS!!

Do you want to proceed? (Y/N): y
OK; writing new GUID partition table (GPT) to /dev/sda.
The operation has completed successfully.
```

特に説明とか見なかったですが fdisk と同様に使用できました。
誤ったコミット情報を修正する: Git LogのAuthorとCommitter変更ガイド


どうもこんにちはたくびー(@takubii)です。
皆さんはGit Logを詳しく見たことはありますか？
普段はVisual Studio Codeのソース管理でしか見ない、あるいは拡張機能を使って確認するだけだったりしませんか？私自身、そうでした。
Githubなどでコミッターの名前を見てみると、自分の意図していない名前でコミットしていたりということがあります。
今回はそういった場合に、どう変更していくのか紹介したいと思います。

## Git Logの確認
まずはGitのログを確認しましょう。
```zsh:terminal
git log --pretty=fuller
```
`git log`でもログの確認はできますが、`--pretty=fuller`オプションを使用することで、Author、Committer情報、さらにその時間まで表示してくれます。
今回はCommitterだけでなく、Authorも自分が意図した名前なのか確認してみましょう。

```zsh:terminal
commit [コミットハッシュ]
Author:     [Author Name] [<Author email>]
AuthorDate: Sat Jun 10 00:44:47 2023 +0900
Commit:     [Committer Name] [<Committer email>]
CommitDate: Sat Jun 10 00:44:47 2023 +0900

    [コミットコメント]

```

コマンドを打って確認すると、上記のようなログが表示されていると思います。
これでコミット情報がより詳しく確認できました。

## Author、Committerの変更
`Author`、`Committer`がもし公開したくない名前やメールアドレスであった場合、これを変更したいと思うでしょう。
以下のスクリプトを実行することによって、全てのコミットログの`Author`、`Committer`を一括で変更することができます。

```zsh:terminal
git filter-branch --env-filter '
OLD_EMAIL="your-old-email@example.com"
CORRECT_NAME="Your Correct Name"
CORRECT_EMAIL="your-correct-email@example.com"
if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
  export GIT_COMMITTER_NAME="$CORRECT_NAME"
  export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
  export GIT_AUTHOR_NAME="$CORRECT_NAME"
  export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
' --tag-name-filter cat -- --branches --tags
```

こちらのスクリプトの`OLD_EMAIL`を変更したい対象のemail(現在設定されているemail等)、`CORRECT_NAME`、`CORRECT_EMAIL`を変更後の値にすることで全コミットに影響を与えることができます。
私の場合、`Author`、`Committer`はどちらも同一でしたが、異なる値を設定したい場合は、`GIT_AUTHOR_NAME`、`GIT_AUTHOR_EMAIL`に違う値を入れれば変えることができます。
※こちらの処理はローカルのGit Logを書き換えるスクリプトなので、念の為フォルダごとコピーし、バックアップをとっておいてください。

スクリプト実行後にもう一度Git Logを確認し、問題なかったら以下のコマンドでリモートに反映させてください。
通常のプッシュでは変更内容を反映できないので、`--force`オプションをつけて強制実行しています。
```zsh:terminal
git push --force --tags origin 'refs/heads/*'
```

最後に忘れずにローカルの`user.name`と`user.email`を変更しておきましょう。

```zsh:terminal
git config --local user.name "変更したい名前"
git config --local user.email "変更したいメール"
```

変更後にしっかりと変更されているか確認するには以下のコマンドを入力し、`user.name`と`user.email`の値を見てください。

```zsh:terminal
git config --local -l
```

## コミット時間の変更

さらに、Git Logの詳細を見ていくとコミットの時間も確認できると思います。
実はコミット時間も自由に変えることができます。
以下のスクリプトはコミットハッシュを指定してコミット時間は変更するものです。
ただし、Author、Committerの変更の場合と異なり一度に一つの変更しかできません。これはコミット時間を変更するたびにハッシュが変更されるためです。

```zsh:terminal
git filter-branch -f --env-filter \
  'if [ $GIT_COMMIT = コミットハッシュ ]
    then
      export GIT_AUTHOR_DATE="新しい日付と時間"
      export GIT_COMMITTER_DATE="新しい日付と時間"
    fi'
```

こちらのスクリプト実行後もAuthor、Committer変更時と同じようにリモートリポジトリに反映させましょう。

```zsh:terminal
git push --force --tags origin 'refs/heads/*'
```

## 終わりに
Git Logの少し変わった操作方法について解説してみました。
あまり、使う機会はないかもしれないですが、プライベートで開発していた際にGit Configが意図していないものになってしまった場合に対応が可能になります。プライバシーに気を使っている方には有効かなと思います。
それでは、今回はここまでとしたいと思います。ここまで読んでいただきありがとうございます。
また機会があればお会いしましょう。
Try Hack Me Cyber Kill Chain メモ

# はじめに

Try Hack MeのCyber Kill Chainに関するメモです。
Cyber Kill Chainの流れおよびそれぞれの段階の意味、目的、内容について記載しています。

## Introduction

Kill Chainという言葉は軍事概念であり、標的の偵察から諸々を経て破壊までの流れを表します。
Lockheed Martinは2011年にこの概念を元としたCyber Kill Chainフレームワークを作成しました。
Cyber Kill Chainを理解することで、サイバー攻撃に対する知識を深め、自己防衛能力を向上させることができます。
Cyber Kill Chainの攻撃の流れは以下の通りです。

- Reconnaissance
- Weaponization
- Delivery
- Exploitation
- Installation
- Command & Control
- Actions on Objectives

## Reconnaissance

Reconnaissance（偵察）とは、攻撃対象のシステム、被害者に関する情報を収集、発見することです。
OSINT（オープンソースインテリジェンス）も偵察行為と見なされ、攻撃者が攻撃の次の段階に移るために必要なステップです。
OSINTとは、公開されたリソース（ニュースや官報、テレビ・ラジオなど）から、電話番号、電子メールアドレス、会社規模などを収集するプロセスを言います。

## Weaponization

偵察が完了すると、対象を攻撃するための武器を作成、入手する段階になります。
武器はマルウェア、エクスプロイト、ペイロードなどがあり、自作したり、ダークウェブから購入したりします。
スキルが高い攻撃者は検出されにくいよう武器をカスタマイズします。

## Delivery

武器を作成したら、対象に武器を送信する段階に入ります。
送信する方法には以下のようなものがあります。

- フィッシングメール
  - 信頼のある人になりすまし、マルウェア、ペイロードが埋め込まれたメールを対象に開かせる。
- USBドロップ攻撃
  - マルウェアなどが保存されたUSBメモリを公共の場などにばら撒く。
- 水飲み場攻撃
  - 多くの人が利用するサイトを侵害し、悪意のあるウェブサイトにリダイレクトさせる。

## Exploitation

フィッシングメールなどにより、対象のシステムにアクセスできるようになったら、次の段階に入ります。
システム内のソフトウェア、サーバなどの脆弱性を悪用し、権限昇格やネットワークの横断などを試みます。

## Installation

前段階でシステムへの侵入に成功しましたが、アクセス遮断やバッチ適用によって攻撃者がシステムにアクセスできなくなる可能性があります。
そのため、攻撃者は永続的なバックドアをシステムに密かにインストールすることを目指します。
バックドアはPHP、JSPなどのプログラミング言語で書かれたスクリプト（Web Shell）などがあります。
また、この段階で攻撃者は検出を避けるためにタイムストンピングという技術でファイルのタイムスタンプを操作する場合があります。

## Command & Control

マルウェアなどに侵害されたマシンは攻撃者によってC2チャネルを経由して遠隔操作されます。
遠隔操作された被害マシンは攻撃者が設定した外部サーバーと通信し、コネクションを確立させます。
これにより被害マシンは攻撃者によって完全にコントロールされます。
C2チャネルには、HTTP、HTTPS、DNSなどが利用されます、

## Actions on Objectives(Exfiltration)

以上6つの段階を経て、攻撃者は侵害したマシンから情報搾取や改ざん、データ破壊、サービス停止等の攻撃者の目的を実行します。
【React】eventの型定義【TypeScript】

## eventの型指定

```ts
type Props = {
  onClick: (event: React.MouseEvent<HTMLInputElement>) => void
  onChange: (event: React.ChangeEvent<HTMLInputElement>) => void
  onkeypress: (event: React.KeyboardEvent<HTMLInputElement>) => void
  onBlur: (event: React.FocusEvent<HTMLInputElement>) => void
  onFocus: (event: React.FocusEvent<HTMLInputElement>) => void
  onSubmit: (event: React.FormEvent<HTMLFormElement>) => void
  onCancel: (event: React.React.SyntheticEvent<HTMLDialogElement>) => void
}
```
CSSアニメーションを実装する上で気を付けたいパフォーマンスの話し

## はじめに

Safariで、css filter(blur)をかけるとなぜか表示されなかったので対処法を書きます。
理由としては要素にハードウェアアクセラレーションが効いていないことが原因で、
要素にGPUレイヤーを作成すれば大丈夫なので、GPUレイヤーを作成するようSafariに促します。

### 対策1. will-changeプロパティを使う
`will-change`プロパティを設定することでGPUレイヤーを作成する事ができます。

```css
.lp_hero__main::before,
.lp_hero__main::after {
  filter: blur(100px);
  will-change: filter;
}
```

### 対策2. transformを使用する
`transform`プロパティを設定することでGPUレイヤーを作成する事ができます。

```css
.lp_hero__main::before,
.lp_hero__main::after {
  filter: blur(100px);
  transform: translateZ(0);
}
```

実装後（after）の画像ではちゃんと薄い青色のブラーが適用されているのが確認できます。
![Group 11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016955/3d1242d6-c37b-5b2f-3a3c-3d87ceab12a1.png)

なぜ、`will-change`・`transform`を設定することで解決できたのか。
「はじめに」で書かれていたGPUレイヤー、ハードウェアアクセラレーションについてもう少し深掘ってみます。

## GPUレイヤーとは？

> GPU（Graphics Processing Unit）は画像処理装置と呼ばれ、CPUの指令で画像処理を行います。CPUとGPUの違いを理解するためには「CPUの指令で画像処理を行い」という点がポイント。例えば3Dグラフィックや写真の画像をパソコンに表示させるためには、画像処理の計算が必要になります。文字情報とちがって画像は情報量が多く、膨大な計算が必要。だから、専用の処理装置GPUが登場するというわけです。
[makuring.com 【3分でわかる】CPUとGPUの違いをやさしいたとえ話で解説！より引用](https://makuring.com/pc/cpu-gpu/)

GraphicLayer は GPU で処理されるレイヤーのことで、Chrome の DevTools 実際で確認することができます。

## ハードウェアアクセラレーションとは？

描画処理を GPU に任せる行為は「ハードウェアアクセラレーション」と呼ばれます。 
`will-change: filter;`もしくは `transform: translateZ(0);`を指定するとハードウェアアクセラレーションが有効になり、GPUを使って描画が行われます。要素のアニメーションがカクツク場合、ハードウェアアクセラレーションを有効しましょう。

## レイヤー（GPU）の生成について
ChromeのDevToolsで確認してみましょう。
`Control+Shift+I`でDevToolsを開き画像の手順に沿って、レイヤの項目を選択
![Group 6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016955/aa1073ea-262e-4a40-d0ed-438cf6fdfd1c.jpeg)

↓ レイヤタブが追加され、レイヤー生成図が表示されます。
ANYTEAMの[aboutページ](https://anyteam.jp/about)で確認してみます。
![Group 7.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016955/8cbee3e3-1d9f-7f85-70e2-b96770ee592c.jpeg)

↓ 実際にGPUレイヤーの生成を確認しましょう。

**will-change: transform の適応**
div.header が #document とは別にレイヤー化され浮いているのが分かります。
![Group 9.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016955/cef17fef-de21-c5e4-b344-2b98b31b0257.jpeg)

**transform: translateZ(0) の適応**
div.footer が #document とは別にレイヤー化され浮いているのが分かります。
![Group 10.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2016955/860a9584-9e82-69d1-73c9-8f16ae269956.jpeg)

この様にしてレイヤーパネルで`will-change`・`transform`が効いているかどうかを確認することができます。

## まとめ

WebサイトのCSSアニメーションを見ていて、カクカクしていてスムーズに動いてくれない時は、GPUが使わておらずCPUで処理されてます。

CPUではなくGPUで動かしてサイトのパフォーマンスを上げるようにしましょう！


## 最後に運動通信社について

運動通信社は「日本を世界が憧れるスポーツ大国にする」というビジョンを達成するべく、一緒に働く仲間を募集しています！
PMやアプリエンジニア、Webエンジニアなど色んな職種を募集しておりカジュアル面談大歓迎なので是非採用フォームよりご連絡ください！
ぜひ一緒に、自分たちも世の中もワクワクするサービスを作りましょう！

https://sportsbull.jp/about/#recruit

Greenも活用中です

https://www.green-japan.com/company/9598
【Swift】簡単なプログラムでさくっとライブラリを試したい

# はじめに
プレイグラウンドでサクッとライブラリを試したかったのですが、できなかったので別の方法を探ります。

![スクリーンショット 2023-07-24 23.35.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366249/dbda5e42-2450-be35-73a0-5e7b0f886d2d.png)

# コマンドラインツールを作る
結論、コマンドラインツールを作ると簡単にライブラリを試すことができました。

### 1. 新しくコマンドラインツールを作成する。
![スクリーンショット 2023-07-24 23.36.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366249/fa9cf8e6-20e1-9921-2889-4277f5c7c8b5.png)

### 2. いつも通りライブラリを追加する
![スクリーンショット 2023-07-24 23.38.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366249/a1f4c8a2-5be9-c259-50bd-1b029e46ad8b.png)

### 3. コードを書いて、実行ボタンを押す

![スクリーンショット 2023-07-24 23.40.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366249/e00a33b1-12cd-c2b6-1542-e98f93e2cd8e.png)

# おわり
簡単にサクッとライブラリを試すことができました🎉
vuex-persistがVue3で動くか試してみた

## 結論

[vuex-persist](https://github.com/championswimmer/vuex-persist)はVue3で動く

## 環境

vue 3.1.1
vuex 4.0.1
vuex-persist 3.1.3

### my-appvue\src\App.vue

```vue
<template>
  <div id="app">
    <CountValue />
  </div>
</template>

<script>
import CountValue from './components/CountValue.vue';

export default {
  name: 'app',
  components: {
    CountValue,
  },
};
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
```

### my-appvue\src\main.js

```js
import { createApp } from 'vue';
import App from './App.vue';
import store from './store';

const app = createApp(App).use(store);
app.mount('#app');

```


### my-appvue\src\store.js

```js
import { createStore } from 'vuex';
import VuexPersist from 'vuex-persist';
import counter from './store/modules/counter';

const vuexLocalStorage = new VuexPersist({
  key: 'sano-vuex', // The key to store the state on in the storage provider.
  storage: window.localStorage, // or window.sessionStorage or localForage
  // Function that passes the state and returns the state with only the objects you want to store.
  // reducer: state => state,
  // Function that passes a mutation and lets you decide if it should update the state in localStorage.
  //
});

const vueStore = createStore({
  strict: process.env.NODE_ENV !== 'production',
  modules: {
    counter,
  },
  state: {},
  mutations: {},
  actions: {},
  plugins: [vuexLocalStorage.plugin],
});
export default vueStore;
```

### my-appvue\src\store\modules\counter.js

```js
const state = {
  step: 1,
  count: 0,
};

const getters = {
  step: (state) => state.step,
  count: (state) => state.count,
};

const actions = {
  increment({ commit }) {
    commit('increment');
  },
};

const mutations = {
  increment(state) {
    state.count += state.step;
  },
};

export default {
  state,
  getters,
  actions,
  mutations,
};

```

### my-appvue\src\components\CountValue.vue

```vue
<template>
  <div>
    <button @click="increment">increment {{ step }}</button>
    <span>total {{ count }}</span>
  </div>
</template>

<script>
export default {
  name: 'CountValue',
  computed: {
    step() {
      return this.$store.getters.step;
    },
    count() {
      return this.$store.getters.count;
    },
  },
  methods: {
    increment() {
      this.$store.dispatch('increment');
    },
  },
};
</script>

```
【PHP】定数constの使用方法

変数は何度でも値を変更することができますが、定数は一度設定した値を変更することができません。定数は変わらない数と覚えておきましょう。

# 使用例

```php
const MAX = 10;

echo MAX;
```

定数は通常の変数と区別するために、基本的に**アルファベットの大文字**で記述するのが慣例となっています。

変数の先頭に$の記号は付けませんので注意しましょう。

### 表示例

```php
10
```

かわらない数なので以下のように書いても一番上の文字が優先的に表示される

```php
const MAX = 10;
const MAX = 11;

echo MAX;
```

### 表示例

```php
Warning: Constant MAX already defined in C:\xampp\htdocs\php_test\index.php on line 4
10
```

すでにMAXという定数は存在しているためエラー文を表示させています。
Leetcode 543. Diameter of Binary Tree

# Problem
この問題は、バイナリツリーにおける直径（ツリー内の任意の二つのノードを結ぶ最長のパス）の長さを求めるものです。ここでのパスとは、2つのノード間のエッジ（ノードを結ぶ線）の数を指します。
> Given the root of a binary tree, return the length of the diameter of the tree.

InputとOutputの例は次になります。

```
Input: root = [1,2,3,4,5]
Output: 3
Explanation: 3 is the length of the path [4,2,1,3] or [5,2,1,3].
```
![image](https://assets.leetcode.com/uploads/2021/03/06/diamtree.jpg)


https://leetcode.com/problems/diameter-of-binary-tree/description/

## Key Idea
深さ優先探索（DFS) を使用します。これは、各ノードで左と右のサブツリーの深さ（最も深いところまでのエッジの数）を計算し、これらを合計します。そして、現在の直径（最長のパス）とこれらの深さの合計とを比較し、長い方を新しい直径とします。

しかし、深さを計算する際、それぞれのノードで再度左と右のサブツリーを探索すると効率が悪くなります。そのため、この問題では "Post-Order Traversal" と呼ばれる手法を使います。これはバイナリツリーを探索する方法の一つで、子ノードを親ノードよりも先に訪れる方式を指します。



## Approach

葉ノードから順に、`depth`と`diameter`という２つのパラメータを考えて行きます。
`depth`は、そのノードから葉ノードまでの最長の長さを表します。
左のサブツリーの`depth`か右のサブツリーの`depth`のどちらか長い方に1を足した値が`depth`となります。

`diameter`は、問題文中にもあるようにツリー内の任意の2つのノード間の最長パスの長さです。
ここで、`diameter`を考える際に、２つパターンがあります。
それは、(1) 該当ノードを通る経路が最長経路である場合と (2)該当ノードを通らない経路が最長経路の場合 です。

下記の画像中のグラフの場合で説明します。
(1) 該当ノードを通る経路が最長経路である場合
`2` のノードの場合は、左のサブツリーの `diameter : 2` と右のサブツリーの `diameter : 1` を足した `3` が`diameter` になります
(2) 該当ノードを通らない経路が最長経路の場合
`1` のノードの場合は、右のノードを持たないので、左のサブツリーの `diameter : 5` が`diameter` になります
どちらのパターンに該当するかどうかは、「左のサブツリーの`depth`と右のサブツリーの`depth`の和」か「そのノード配下でメモした `diameter` 」のどちらが大きいかによって判断されます。
![IMG_9120.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/99549/e4b2c38f-959f-0405-a158-ecd63148c6f6.jpeg)



```python
class Solution:
    def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:


        self.diameter = 0

        def depth(root):
            if root is None:
                return 0       
            left_depth =  depth(root.left) 
            right_depth =  depth(root.right) 

            # そのノードを通る方が最長の場合とそのノードを通らない方が最長の場合の両方存在
            self.diameter = max(self.diameter, left_depth + right_depth)
            return max(left_depth, right_depth) + 1

        depth(root)
        return self.diameter
```


## Complexity
この解法では、計算量は下記の様になります。

- Time complexity: O(n)。このプログラムはバイナリツリーのすべてのノードを一度ずつ訪れます。各ノードでは、ノードの深さを計算するために定数時間の操作（深さの比較と加算）が行われます。ここでnはツリーのノード数です。

- Space complexity: O(n)。このプログラムは深さ優先探索を用いているため、スペース計算量はツリーの高さに比例します。最悪の場合（つまりツリーが線形リストのようになっている場合）、Space complexityはO(n)となります。しかし、ツリーが完全にバランスが取れている場合、Space complexityはO(log n)となります。


## Reference
https://www.youtube.com/watch?v=bkxqA8Rfv04
【Rust】Cloud Runにおいて、panicが発生すると、コンテナがSIGTERMで死ぬ

## 環境
- Rust
- Actix-web 4
- Cloud Run(always on cpu)

## 背景
以前はCloud Runでは基本的にはバックグラウンドでの非同期処理はできなかった。これはリクエスト処理中のみCPUが割り当てることができるという縛りがあったから。  

しかし2021年にアップデートがあり、[CPUの常時割り当て](https://codezine.jp/article/detail/14873) を行うことができるようになり、バックグラウンドでの非同期処理ができるようになった。

現職でのサービスでは、Cloud Runをalways on cpuの設定にして、RustのActix-webを載せてAPIとして使用しており、Arbiterでスレッド生成（イベントループ生成）を行い、そのスレッドでバックグラウンドでの処理を実行している。

## 問題
以下の手順で、問題が発生した。

1. あるリクエストを受けると、バックグラウンドでの非同期処理Aが実行される
1. 他のリクエストを受け付けた際に、アプリ不具合により、panicが発生した
1. バックグラウンドで実行されている非同期処理Aが終わらない（止まっている）
1. Cloud Runのログを見ると、以下のようになっていた
    1. 下記のログ例は明示的にpanicを発生させている

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152020/ac083516-f300-851c-525b-9601b76c1901.png)

## 詳細
- ローカルホストで再現を試みたが、この問題は発生しなかった（worker数をいろいろ変えてみて並列実行したりして試してみた）  
- Cloud Runのドキュメントにはこの挙動について明確な記載を見つけることができなかった。
- とりあえずactix-webをCloud Runに載せるときは、panicしないほうがよさそうだと思われる
    - 処理中の他のリクエストも巻き込んで死ぬ

## 対応と備考
- とりあえずpanicさせければどうということはない
- どうしてもというなら[panicを拾う？](https://doc.rust-lang.org/std/panic/fn.catch_unwind.html)
    - これ書いてるときに発見。あとで試す
- なにかご存じの方いれば教えてください・・
お互いにグローバルIP持ってないけど、VPN接続する(softether)

## VPNserverの構築
### インストール
aptで提供してくださってありがとうございます。今回はソースからビルドしません。
```
apt-get -y update
apt-get -y install softether-vpnserver
```

### サーバーの起動
```
systemctl status softether-vpnserver.service

● softether-vpnserver.service - SoftEther VPN Server
     Loaded: loaded (/lib/systemd/system/softether-vpnserver.service; enabled; vendor preset: enabled)
     Active: active (running) since Fri 2023-05-26 02:34:50 UTC; 9min ago
    Process: 49965 ExecStart=/usr/libexec/softether/vpnserver/vpnserver start (code=exited, status=0/SUCCESS)
   Main PID: 49967 (vpnserver)
      Tasks: 37 (limit: 16777216)
     Memory: 12.2M
        CPU: 3.510s
     CGroup: /system.slice/softether-vpnserver.service
             tq49967 /usr/libexec/softether/vpnserver/vpnserver execsvc
             mq49968 /usr/libexec/softether/vpnserver/vpnserver execsvc

May 26 02:34:50 ubuntu-18 systemd[1]: Starting SoftEther VPN Server...
May 26 02:34:50 ubuntu-18 vpnserver[49965]: The SoftEther VPN Server service has been started.
May 26 02:34:50 ubuntu-18 systemd[1]: Started SoftEther VPN Server.
```

### サーバーの開始と永続化
```
systemctl start softether-vpnserver.service
systemctl enable softether-vpnserver.service
systemctl restart softether-vpnserver.service
```

## hostnameを設定・永続化
```
cat /etc/hostname

taiarivpn

```

## VPNserver Managerの起動
### 新しい接続設定
- ホスト名：172.24.20.18
- 管理パスワード: お好きなもの

### 接続
リモートアクセスを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2738475/f32955e8-242a-be93-25d2-da671c51f93e.png)


### 仮想HUBの設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2738475/55b8cce4-b741-a48e-268e-1922fbf2d14a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2738475/0e1840ab-f146-d7d9-638a-9fc5307e65a8.png)
secureNATをオンにする。

### cmd
ドメインでpingが通るか試しておく。

## クライアントから接続
windowsからのみ接続できる。

1. ネットワークの共有センター
1. 新しい接続
1. 職場に接続
1. VPNを使用
1. vpnazure.netのドメインを入力
vpn接続を行う

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2738475/02464677-6884-efcc-5bf7-2535e9bdc30c.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2738475/fe1942c5-4c55-ffdd-1c86-31eb2eeff49e.png)
待っていると接続されます




参考文献
https://www.proofpoint.com/jp/threat-reference/sstp
https://imamura-net.com/blogpost/19497/
Windows2003 のミラーボリュームを Linux で読む

ミラーボリュームって、NTFS とは異なるので Windows10 とかでマウントしようとしても「異形式」となってしまってメンテナンスができない。

Ubuntu 22.04 LTS で FDISK -l してみたら・・・

```
ディスク /dev/sda: 465.76 GiB, 500107862016 バイト, 976773168 セクタ
Disk model: Hitachi HDT72505
単位: セクタ (1 * 512 = 512 バイト)
セクタサイズ (論理 / 物理): 512 バイト / 512 バイト
I/O サイズ (最小 / 推奨): 512 バイト / 512 バイト
ディスクラベルのタイプ: dos
ディスク識別子: 0x2e672e66

デバイス   起動  開始位置  最後から    セクタ サイズ Id タイプ
/dev/sda1  *           63 409593239 409593177 195.3G 42 SFS
/dev/sda2       409593240 976768064 567174825 270.5G 42 SFS


```

これって読めるのかな・・・Secure File System



# ldmtool を使ってみる・・・失敗

https://askubuntu.com/questions/412248/mounting-an-sfs-partition-drive
この記事を見て、ldmtool をインストール。
```
$ sudo apt-get install ldmtool
```
スキャンしてみたけれど・・・

```
$ sudo ldmtool scan
```
何も出てこない。
```
[]
```
うーむ。

# mount コマンドを試す

readonly でマウントしたいな！
うーん、こうかな・・・

```
$ sudo mount -ro /dev/sda1 /mnt
mount: /mnt: can't find in /etc/fstab.
```
ダメでした。
普通にマウントしたらあっさり読めました。
```
$ sudo mount  /dev/sda1 /mnt
$ ls /mnt
 AUTOEXEC.BAT             'Program Files (x86)'         cygwin
 CONFIG.SYS                RECYCLER                     downloads
'Documents and Settings'  'System Volume Information'   ntldr
.
.
.
```

# nautilus で読む

一旦 umountして、nautilus からボリュームをクリックしても読めました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/195351/7984700a-1fb8-6ce1-4755-13cc21be4711.png)

この状態でのマウントを調べてみます。

```
$ sudo mount | grep sda
/dev/sda1 on /media/nanbuwks/4EE012F9E012E74D type fuseblk (rw,nosuid,nodev,relatime,user_id=0,group_id=0,default_permissions,allow_other,blksize=4096,uhelper=udisks2)
```
# readonly で読む

ということは・・・
```
$ sudo mount -t ntfs -o ro /dev/sda1 /mnt
```
わかってしまえば、かんたん。
```
$ mount | grep sda
/dev/sda1 on /mnt type fuseblk (ro,relatime,user_id=0,group_id=0,allow_other,blksize=4096)
```

昔の苦労した経験が邪魔をして、遠回りしてしまいました。
C#で文字列の前にある@(アットマーク)の意味を知りたい

# 1. はじめに
* C#で変数の文字列をセットする時に、たまに```@(アットマーク)```が先頭にある理由を知りたい

# 2. 開発環境
* C#
* .Net 6
* Visual Studio 2022
* Windows 11

# 3. @(アットマーク)がある理由
* 結論は「C#では```\```エスケープ文字をいちいち書かなくてもよくなる」ということらしい。

```C#
string filePath = @"C:\Users\scoleridge\Documents\";
//Output: C:\Users\scoleridge\Documents\

string text = @"My pensive SARA ! thy soft cheek reclined
    Thus on mine arm, most soothing sweet it is
    To sit beside our Cot,...";
/* Output:
My pensive SARA ! thy soft cheek reclined
    Thus on mine arm, most soothing sweet it is
    To sit beside our Cot,...
*/

string quote = @"Her name was ""Sara.""";
//Output: Her name was "Sara."
```

# 4. (参考)文字列のエスケープ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2852081/ffb5a74e-dd0e-02d2-e274-c462ee2eae4c.png)

https://learn.microsoft.com/ja-jp/dotnet/csharp/programming-guide/strings/?redirectedfrom=MSDN

# 5. 参考文献

http://sogohiroaki.sblo.jp/article/178158799.html

https://learn.microsoft.com/ja-jp/dotnet/csharp/programming-guide/strings/?redirectedfrom=MSDN