OmniAICreator/Qiita-1.07M huggingface dataset:
PHP/MySQLで作ったポートフォリオの振り返り

#初めに
開発環境はmacOSで
PHPのバージョンは７.3を使用しています。
あくまで、アウトプットを目的として書いた記事なので、理解しにくい点、間違った内容があるかもしれません。ご容赦ください。

##ポートフォリオについて
名前は『BookList』というアプリで、本のECサイトという設定で開発しました。
私は本を読むことが好きで、本を専門としたECサイトは、まだ少ないと感じ
中古本を買う時に、自分が大切にしていること、買い手、売り手の気持ちになって、出品者は自分の商品を誰にどんな時に読んで欲しいのか、自分の商品のPR文の機能であったり
買い手は円滑に購入できるように、コメント機能を追加したり、
本をよく読む自分が利用したくなるようなECサイトを目指して開発しました。


##アプリの機能一覧

|No|機能|機能について|
|:---:|:---:|:--:|
|1|新規登録機能||
|2|ログイン機能||
|3|ゲストログイン機能|新規登録しなくても、予め用意されているユーザー|
|4|パスワードのハッシュ化|[この機能は記事にまとめました](https://qiita.com/tom111/items/e2c4b7572814f921dddb)|
|5|商品の出品機能|商品の状態、種類、画像、タイトル、値段、著者などを登録|
|6|商品の編集|出品した、個数、値段、PR文、ステータスなどを編集|
|7|商品の削除|出品者は自分の商品を削除|
|8|コメント機能|商品の購入前に、出品者に対してコメントできる|
|9|コメント削除機能||
|10|商品の検索機能|商品の状態、種類で商品を検索できる|
|11|ページネーション|商品一覧ページ、商品別コメントページにそれぞれ|
|12|カートに追加|商品をカートに追加|
|13|カート削除|カートに追加した商品を削除|
|14|商品購入機能|商品を購入する|


ECサイトで必須となる、出品->カートに追加->購入機能を中心に、本を扱うアプリなので、本の状態や、著者、コメント機能なども、追加していきました。

##DB設計
![booklist ER図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/527269/761dac9a-cc72-d96b-b0a6-24dc1e91ba90.png)

カラムは```id```しか表示させていませんが、DB設計を通じて、主キーや外部キーの繋がりを理解し、論理的に開発することができました。



##セキュリティ、脆弱性
このポートフォリオは、フレームワークを使わずに、生のPHPとMysqlのみで作ったので、セキュリティ、脆弱性についてはしっかりと対策しました。

###クロスサイトスクリプティング(XSS)

```
//xss対策
function h($str){
  return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
}
```
上記の関数を作り、画面上に```echo print```を使って、値を出力する時は必ず```h(hogehoge)```とエスケープ処理をしました。

### SQLインジェクション
これに関しては、```form```から```post```されてきた値をそのまま、保存するのではなく、
変数に格納し、PDOのprepareメソッドを利用して作成したステートメントに値をバインドさせ対策しました。

### クロスサイトリクエストフォージェリ (CSRF)
主な脆弱性の一つの```csrf```に対しては、```post```送信を行う全ての```form```に対して、ランダムな文字列を生成し、```post```送信の受け取って操作をする前に、そのトークンの照合行い、異なる文字列の場合には、ログインページに飛ばすように処理しました。
トークンの文字列が盗まれ、再利用されないように、トークンの破棄も忘れず、実装しています。


##工夫した点

###1.MVCを意識する
フレームワークを使っていなくても```model```、```view```、```controller```とそれぞれのフォルダを作り、データの流れを意識しながら、開発を進めました。

###2.高品質なコーディングを心がける
なるべくわかりやすい関数名、変数名を命名し、```require```,```include```などの関数を用いて、templeteファイルなどで共通化を取り入れました。さらにコメントを積極的に、コード内に書き込み可読性の高いコーディングに仕上げました。

参考 [github](https://github.com/tomii1031/booklist)


##デプロイについて
conohaのvpsを使って、デプロイしました。
ssh接続し、本番環境にファイル、データベースの設置が完了したら、
セキュリティを高めるために、rootログインを禁止にし公開鍵認証の設置、filewallの設定でポート番号を変更しました。
そして、ドメインを購入して、conohaのIPアドレスに割り当てました。

[BookList](http://www.booklist.work)

##課題

###デザイン面
バックエンドエンジニアを目指しているので、凝ったデザインではなく、シンプルなもので仕上げたので、もっと使い勝手がよくなるように、レスポンシブにも対応させていきたいです。

###DBの発行のしすぎ
重複したDBの値などもあり、処理が増えてしまっている。



##まとめ
・フレームワークを使っていないので、多機能なアプリとは言えないが、あえてPHPのみでの開発でプログラミングの楽しさや、エラーが発生した時の自走力が身につきました。

・セキュリティに関する知識、対策、SQLの理解なども深めることができました。フレームワークを利用すると、あまり直接SQL文を書くことがないのでとても勉強になったし、汎用性のきく知識だと感じています。


最後まで、読んでいただきありがとうございました！！
【ECサイト開設まとめ】どのサービスを使えばいいのか

#はじめに
昨今、様々なECサイトや決済サービスが出ており、訳がわからないのでまとめます。

#いきなり結論
####とにかくパッと作りたいのならBASEかStores.jpをお使いください。
BASE https://thebase.in/
Stores.jp https://stores.jp/

####そこそこちゃんと作りたいのなら、決済なしのWebサイト＋Amazon or 楽天,もしくは Shopifyをお使いください。

Shopify https://www.shopify.jp/

####すごくしっかりしたものが必要なら、イチから開発 + GMO Payment Gateway(決済サービス)で構築してください。
GMO Payment Gateway　https://www.gmo-pg.com/service/mulpay/?ref=head_menu

####以下補足説明↓

#サービスがあり過ぎてよくわからん！
って、みんな思うよね。
![Screen Shot 2021-03-15 at 21.06.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242700/d2d05dbd-436e-9101-b3b1-0befd3982388.png)


#実はECって結構大変....
やることたくさん。
![Screen Shot 2021-03-15 at 21.06.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242700/93536b3d-e2ec-c60a-82a5-5b879d35a1fd.png)

製造管理もしなくちゃいけないし、発送は手間だし、DM送るのも大変。それに加えて欠陥品を売ってしまったらもう大変。なので、コロナ前はそんなに普及していなかった。

が、そんなことを言ってなれない。

#ECに必要なものはなんなのか？
ECサイトを作るには、
####決済プラットフォーム
もしくは
####ECプラットフォーム
のいずれかが必要です。

![Screen Shot 2021-03-15 at 21.07.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242700/12771271-8572-ffc5-732c-178299a62572.png)
なんとなく、アマゾン&楽天とpaypayの違いで理解してください。

#パターンを考えてみる
![Screen Shot 2021-03-15 at 21.07.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242700/601d6d05-5a15-3470-db53-48359333969b.png)



#何を使うべきか選ぶコツは...
###経済合理性。

#お金のお話
![Screen Shot 2021-03-15 at 21.07.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/242700/d2ac64c8-f41d-6000-ee7e-9fa2d3435e7a.png)
アマゾンと楽天は結構高く、他のサービスは決済手数料が3.3%~3.6%。
この中でpay.jpは2.59%と際立って安い。また、GMO Payment Gatewayは支払い方法がたくさんありおすすめです。（開発大変だけど。）

#シミュレーション
売上が5,000万円あれば、10%は500万円。3.6%は180万円なので、
320万くらいかけてイチから開発しても十分元が取れます。

#まとめ
年間5,000万くらい売上があがるならイチから開発してもいいですが、そうでないのなら外部サービスを使うのがいいのではないでしょうか。

#最後に
これってqiitaに書くこと???
Railsチュートリアル第4章　push分振り返り


##組み込みヘルパー
Railsの組み込み関数stylesheet_link_tagを使って、
application.cssをすべてのメディアタイプで使えるようにしている。
stylesheet_link_tagを使って、application.cssをすべてのメディアタイプで使えるようにしている。

```app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= stylesheet_link_tag    'application', media: 'all',
                               'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag    'application',
                               'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <%= yield %>
  </body>
</html>
```

##カスタムヘルパー
Railsのビューでは膨大な数の組み込み関数が使えるが、新しく作成することもできる。
新しく作ったメソッドはカスタムヘルパーと呼ばれる。

上のapp/views/layouts/application.html.erbにて、
``<%= yield(:title) %> | Ruby on Rails Tutorial Sample App``の行がある。
title消しても、「 | 」が残る。

ページタイトルがうまく表示されない問題を解決するため、full_titleというヘルパーを作成する

```app/helpers/application_helper.rb
module ApplicationHelper

  # ページごとの完全なタイトルを返します。
  def full_title(page_title = '')
    base_title = "Ruby on Rails Tutorial Sample App"
    if page_title.empty?
      base_title
    else
      page_title + " | " + base_title
    end
  end
```
作成したヘルパーを使って、レイアウトをシンプルにする


```app/views/layouts/application.html.erb

変更前　<title><%= yield(:title) %> | Ruby on Rails Tutorial Sample App</title>
変更後　<title><%= full_title(yield(:title)) %></title>
```

Homeページのタイトル確認用にテストを更新

```test/controllers/static_pages_controller_test.rb

require 'test_helper'

class StaticPagesControllerTest < ActionDispatch::IntegrationTest
  test "should get home" do
    get static_pages_home_url
    assert_response :success
    assert_select "title", "Ruby on Rails Tutorial Sample App"
  end
以下省略
```
テストスイートを実行して、テストが失敗することを確認

```
$ rails test
3 tests, 6 assertions, 1 failures, 0 errors, 0 skips
```
provide の行削除

```app/views/static_pages/home.html.erb

<h1>Sample App</h1>
<p>
  This is the home page for the
  <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a>
  sample application.
</p>

```

これでテストはパスする。

``$ git commit -am "Add a full_title helper"``
``$ git checkout master``
``$ git merge rails-flavored-ruby``

##文字列とメソッド
Ruby を学ぶためのツールとして、主にRailsコンソールを使っていく。
irb（IRB: Interactive RuBy）を拡張して作られているため、Rubyの機能をすべて使うことができる。

irbの設定ファイルを追加する
``$ nano ~/.irbrc``

```~/.irbrc

IRB.conf[:PROMPT_MODE] = :SIMPLE
IRB.conf[:AUTO_INDENT_MODE] = false
```

###Railsコンソールについて
デフォルトでは、コンソールはdevelopment（開発）環境という、
Railsによって定義された3種類の環境のうちの1つで起動する。
他の2つはtest（テスト）環境とproduction（本番）環境。

コンソールの中で何をしようとも、何かを壊すことはまずありえない。
もしRailsコンソールが何かおかしな挙動になったら、
``Ctrl-C``でコンソールから強制的に抜け出したり、
``Ctrl-D``で正常にコンソールを終了させることも可能。
【RubyonRails】no such tableのエラー

##【RubyonRails】no such tableのエラー

Devise導入時にこんなエラーが、

```ruby:terminal
>rails db:migrate
== 20210315111515 AddDeviseToUsers: migrating =================================
-- change_table(:users)
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:

SQLite3::SQLException: no such table: users: ALTER TABLE "users" ADD "email" varchar DEFAULT '' NOT 
NULL
C:/Users/sample/db/migrate/20210315111515_add_devise_to_users.rb:7:in `block in up'      
bin/rails:4:in `<main>'

Caused by:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such table: users: ALTER TABLE "users" ADD "email" varchar DEFAULT '' NOT NULL
C:/Users/sample/db/migrate/20210315111515_add_devise_to_users.rb:7:in `block in up'      
C:/Users/sample/db/migrate/20210315111515_add_devise_to_users.rb:5:in `up'

Caused by:
SQLite3::SQLException: no such table: users
C:/Users/sample/db/migrate/20210315111515_add_devise_to_users.rb:7:in `block in up'      
C:/Users/sample/db/migrate/20210315111515_add_devise_to_users.rb:5:in `up'
bin/rails:4:in `<main>'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)
```

usersのテーブルが見つかりませんってことっぽい
でも確認したらsersテーブルあるんだよなぁ

なのでもう一度


```ruby:terminal
>rails g devise user
>rails db:migrate
```
を実行。
治りませんでした。

なんでだろうと思い新しくプロジェクトを作成しrails db:migrateをするとそちらではできました
何が違うのかなとfileをみていたらエラーが起こる方は

```20210315111515_add_devise_to_users.rb
class AddDeviseToUsers < ActiveRecord::Migration[5.2]
  def self.up
    change_table :users do |t|
```

と記述してあり
エラーが起こらない方は

```20210315115722_devise_create_users.rb
class DeviseCreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
```

と記述してあり、change_tableをcreate_tableに直したら実行できました！！！！
このエラーに結構苦戦したため備忘録として残しておきます
【npm】バージョン表記に使われる記号「^1.2.3の ^ 」と「~1.2.3の ~ 」の意味

npmのpackage.jsonを見ると「^2.7.5」や「~2.7.5」のようにバージョン情報に「 ^ 」「 ~ 」の記号がついていますが、この記号の意味をここにメモします。

<br><br>

#バージョン情報の事前知識

バージョン情報は「2.7.5」のように「数字① . 数字② . 数字③」という形をしています。

このそれぞれの数字は下のように名前がついており、それぞれに表す意味があります。

数字①
名前：メジャー
意味：大規模な変更の回数を示す。パッケージを使っている側は多くの場合、バージョン変更に合わせてコードを書き換える必要がある。

数字②
名前：マイナー
意味：小規模な変更の回数を示す。

数字③
名前：パッチ
意味：バグの修正回数を示す。

<br><br>

#「^」と「~」の読み方と意味

###「＾」
読み方：キャレット
意味　：メジャーは一致しマイナーとパッチは指定されたもの以上
例）パッケージを^2.7.5で指定した場合
　　2.7.10や2.9.8はOKだが、3.2.10はNG

<br>

###「~」
読み方：チルダ
意味　：メジャーとマイナーは一致し、パッチは指定されたもの以上
例）パッケージを~2.7.5で指定した場合
　　2.7.10はOKだが、2.8.5はNG

<br><br>

#参考元
https://qiita.com/righteous/items/e5448cb2e7e11ab7d477
【Rails】toggleとtoggle!の使い方

<h2>toggleとtoggle!</h2>
toggle(:hoge)とtoggle!(:hoge)の大きな違いは<b>「データベースにセーブされるかどうか」</b>と<b>「戻り値」</b>の違いになります。

・<b>toggle</b>:インスタンスに保存されているbooleanの値を反転する(データベースには変更を反映しない)。処理成功時に指定のbooleanを反転させたインスタンス自身をreturnしてくれます。(selfを返す)

・<b>toggle!</b>:インスタンスに保存されているbooleanの値を反転させて、データベースに保存してくれます。処理成功時にtrueをreturnしてくれます。

toggle(:hoge)でデータベースに値を保存したい場合はsaveメソッドを使えば保存できます。

<h2>使用例</h2>

以下、toggle(:activated)の使用例です。

```ruby:toggle
rails c
#データベースからデータを取得。インスタンスのactivatedの値がtrueである
user = User.first
>User id: 1, name: "Example User", email: "example@railstutorial.org" activated: true
user.activate
>true

#userインスタンスのacticatedの値がfalseになる
user.toggle(:activated)
>User id: 1, name: "Example User", email: "example@railstutorial.org" activated: false


#再度データベースから代入しなおしたら、user.acticatedがtrueのまま代入される
user = User.first
>User id: 1, name: "Example User", email: "example@railstutorial.org" activated: true
```

以下、toggle!(:activated)の使用例です。(update_at等、一部割愛しています)

```ruby:toggle!
rails c
#データベースからデータを取得。インスタンスのacticatedの値がtrueである
user = User.first
>User id: 1, name: "Example User", email: "example@railstutorial.org" activated: true
user.activate
>true

#userインスタンスのacticatedの値がfalseになる
user.toggle!(:activated)
(0.1ms)  begin transaction
  User Update (4.1ms)  UPDATE "users" SET "updated_at" = ?, "activated" = ? WHERE "users"."id" = ? ["activated", 0], ["id", 1]
   (5.7ms)  commit transaction
>User id: 1, name: "Example User", email: "example@railstutorial.org" activated: false


#再度データベースから代入しなおしたら、user.acticatedがfalseで代入される(つまり、データベースに変更が反映されている)
user = User.first
>User id: 1, name: "Example User", email: "example@railstutorial.org" activated: false
```

<h2>参考</h2>
Rails の toggle と toggle! の違い
https://www.eiji56.com/2017/06/toggle/
【Laravel】関数名の末尾のコロン型（:array, : int, : stringなど）は何を表しているのか？

Laravelで定義した関数名の末尾にコロン型（:array, : int, : stringなど）がついていたため、何の意味があるのか調べてみた。

結論からいうと、**戻り値の型を指定**している。


```php:
function 関数名(引数の型 引数):戻り値の型
    {
        処理
    }
```

`:`の後ろはスペースを開けてもいい。`:int`でも`: int`でもどちらでも問題ない。

指定した型と異なる場合はTypeErrorが発生する。

```:error
Errors with:
Fatal error: Uncaught TypeError: Return value of 関数名() must be an instance of 型
```


<br>

```php:例
#例１
function origin(string $path):string {
        処理
    }

#例２
function counter(?string $src, int $number):int {
       処理
    }

#例3
function page_arr(int $current, int $last):array {
       処理
    }
```


<br>
[PHP公式 戻り値](https://www.php.net/manual/ja/functions.returning-values.php#functions.returning-values.type-declaration) 下部の英語の部分に記載されている。
CUDA 10.1 + Windows 10でPyOpenCLを使った際の'CL/cl.h'が無い場合の解決方法

CUDA 10.1 + Windows 10 (64bit)環境下でPyOpenCLを使う際にはまった所の解決方法を書きます
import pyopenclして、
下記のように'CL/cl.h'が無いよと言われた場合、

```Cannot open include file: 'CL/cl.h': No such file or directory error:```

下のカリフォニア大学アーバイン校のサイトからプリコンパイルされた.whlをインストールすることで解決できます。

https://www.lfd.uci.edu/~gohlke/pythonlibs/#pyopencl

例えば、64bit環境下でPython 3.7の場合、
pyopencl‑2021.1.3+cl12‑cp37‑cp37m‑win_amd64.whlをダウンロードして下さい。

参考URL:
https://forum.image.sc/t/error-while-installing-pyopencl-on-windows-10/44534

加藤 翼
株式会社インスパイアサーチ CEO / サーチエンジニア
https://www.inspiresearch.io
SQL テーブルとカラムの名前変更

#【SQL テーブルとカラムの名前変更】

業務内でテーブルやカラムの名前変更や定義の変更がありました。
簡単な内容ではありますが、備忘録として残します。

##RENAME

<strong>・テーブル名を変更</strong>

```sql
ALTER TABLE table_name RENAME TO new_table_name
```

<strong>・カラム名を変更</strong>

```sql
ALTER TABLE table_name RENAME COLUMN old_column_name TO new_column_name
```

##定義の変更

<strong>・カラム名とカラムの定義を同時に変更</strong>

```sql
ALTER TABLE table_name 
CHANGE COLUMN old_column_name new_column_name column_definition
```

<strong>・カラムの定義を変更</strong>

```sql
ALTER TABLE table_name
MODIFY COLUMN column_name column_definition
```
オリジナルアプリ作成中に理解できていなかった基礎解説

オリジナルアプリ作成中にうまく実装ができず、詰まってしまった箇所の深掘りをアウトプットします！
もし誤った情報や、もっと効率の良い書き方などがあった場合指摘してくださると助かります！
###完成形
※現段階ではオリジナルアプリ自体は完成していません
<img width="250" alt="完成形" src="https://i.gyazo.com/a185e958c0333acd0ca870e13ace0bf1.png">

###目次

- [実装できなかった箇所](https://qiita.com/programming_00/items/ed1dca1ca06c2f55500d#%E5%AE%9F%E8%A3%85%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%8B%E3%81%A3%E3%81%9F%E7%AE%87%E6%89%80)
- [overrideの詳細](https://qiita.com/programming_00/items/ed1dca1ca06c2f55500d#override%E3%81%AE%E8%A9%B3%E7%B4%B0)
- [UIViewControllerのライフサイクル](https://qiita.com/programming_00/items/ed1dca1ca06c2f55500d#uiviewcontroller%E3%81%AE%E3%83%A9%E3%82%A4%E3%83%95%E3%82%B5%E3%82%A4%E3%82%AF%E3%83%AB)
- [まとめ](https://qiita.com/programming_00/items/ed1dca1ca06c2f55500d#%E3%81%BE%E3%81%A8%E3%82%81)

##実装できなかった箇所
今回実装しようとした内容は、曜日表記の日本語化です
自分の作りたいオリジナルアプリのコンセプトが『高齢者の方でも使いやすいアプリ』なので、横文字や英語はなるべく使わないようにしたいため、日本語に変更します。
####英語表記のカレンダー
<img width="500" alt="英語表記" src="https://i.gyazo.com/fd6fd72495b89761732bfcf3dc591100.png">

####日本語化できなかった記載方法

```ViewController.swift
    override func viewWillAppear() {
        super.viewWillAppear()
            // calendarの曜日部分を日本語表記に変更
            calendar.calendarWeekdayView.weekdayLabels[0].text = "日"
            calendar.calendarWeekdayView.weekdayLabels[1].text = "月"
            calendar.calendarWeekdayView.weekdayLabels[2].text = "火"
            calendar.calendarWeekdayView.weekdayLabels[3].text = "水"
            calendar.calendarWeekdayView.weekdayLabels[4].text = "木"
            calendar.calendarWeekdayView.weekdayLabels[5].text = "金"
            calendar.calendarWeekdayView.weekdayLabels[6].text = "土"
    }
```


参考記事などを見ながら進めていましたが日本語に変化しませんでした😭
##overrideの詳細
なぜ実装できないのかエラー文も出ていないので、上記で記載されている`override`について詳しく深掘りしてみました

overrideとは、端的の言うと`親のメソッドを上書きして使いたい時`に使います。
今回の実装内容としては`親（デフォルトのカレンダー）の曜日を（日本語表記）に上書き`したいため使い方としては間違っていないはず！

あと残された可能性としては`viewWillAppear`の部分か、そもそも日本語に表示するための記載が間違っている
次は`viewWillAppear`の部分を詳しく深掘りします

##UIViewControllerのライフサイクル


`viewWillAppear`について調べた結果
結論から言うと、この記載が原因で日本語表記にできていませんでした。
`viewWillAppear`以外に`viewDidLoad`、`viewWillDisappear`などがあります
それぞれ初期表示、画面に表示される直前や直後、画面から非表示になる直後や直前など意味が異なってきます。
今回実装したい内容は初期表示時に変わってもらいたいので`viewDidLoad`を使用します

####修正後

```ViewController.swift
    override func viewDidLoad() {
        super.viewDidLoad()
            // calendarの曜日部分を日本語表記に変更
            calendar.calendarWeekdayView.weekdayLabels[0].text = "日"
            calendar.calendarWeekdayView.weekdayLabels[1].text = "月"
            calendar.calendarWeekdayView.weekdayLabels[2].text = "火"
            calendar.calendarWeekdayView.weekdayLabels[3].text = "水"
            calendar.calendarWeekdayView.weekdayLabels[4].text = "木"
            calendar.calendarWeekdayView.weekdayLabels[5].text = "金"
            calendar.calendarWeekdayView.weekdayLabels[6].text = "土"
    }
```

####修正後の表示

<img width="250" alt="完成形" src="https://i.gyazo.com/a185e958c0333acd0ca870e13ace0bf1.png">

無事曜日の表記を日本語化することに成功しました！

##まとめ
今回『問題解決のプロセス』と『基礎知識の重要さ』を実感しました。
基礎が理解できていなかったためどこが間違っているのかすらわかりませんでした、こういった場面は今後もありそうですが今回のようにわからない場所をがむしゃらに調べることの重要性も改めて理解できました！
それらの知識をQiitaでしっかりアウトプットしていきます
AWS Batch とは

## 勉強前イメージ

バッチスケジューラー的なもの？

## 調査

### AWS Batch とは

フルマネージド型のバッチ処理実行サービスです。
フルマネージド型なのでインフラを構成する必要がなく、設定後すぐ使えるようになります。

### AWS Batch の特徴

- フルマネージド型

上記でも記載しましたが、
フルマネージド型なので、インフラ面での設定等はほぼなく
webから設定するだけで稼働させることが出来ます。
また、実行するインフラを [Fargate](https://qiita.com/miyuki_samitani/items/b1f29abfbf89b27d7ff4) やEC2インスタンスなど選ぶことが出来ます。

- 依存関係があるジョブを実行することが出来る

よくジョブには前後の依存関係がある場合が多く、
依存関係がある場合でも配列ジョブというジョブ定義を行うことにより処理制御を行うことが出来ます。

- ジョブに優先度を持たせることによって、リソースの最適化を行う

ジョブに優先度を持たえることができ、それによってリソースを最適な形で割り当ててコストを抑えることが出来ます。

- モニタリングやログ記録

ジョブの主要なメトリクスについてはマネジメントコンソール上で確認が出来、
その他の詳細なログ等に関してはCloudWatch Logsでも確認することが出来ます。

### 用語

AWS Batchでは様々用語があるので、今回は主要な下記の4つを確認いたします。

- ジョブ
- ジョブ定義
- ジョブキュー
- コンピューティング環境

![1AWS Batch - Google Chrome 2021-03-15 19.47.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129517/bad2ad5c-310f-fe01-eba7-3633bd3b68e1.png)

#### ジョブ

AWS Batchで実行される作業の単位です
後ほど記載するジョブ定義を参照する必要があります。

#### ジョブ定義

実行方法を定義する、ジョブの雛形となるテンプレートのようなものでジョブの実行方法を定義します。

#### ジョブキュー

ジョブキューは投入されたジョブの行列で
ジョブが投入されるとジョブキューに送信され、コンピューティングリソースにスケジューリング出来るまで待機します。

#### コンピューティング環境

ジョブが実行するコンピューティング環境になります。
様々種類を選ぶことが出来ます。

![3AWS Batch - Google Chrome 2021-03-15 19.59.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129517/d94319bc-ec11-1af4-fa1c-d4127cb17ad9.png)

## 勉強後イメージ

バッチスケジューラーというより、「ジョブを実行するもの」のイメージ。
定期実行は他のサービスと連携したらいけると思う
cronって感じではない

## 参考

- [AWS Batch のよくある質問](https://aws.amazon.com/jp/batch/faqs/?nc=sn&loc=5)
- [AWS Batchの使い方、メリット、利用料金について](https://www.acrovision.jp/service/aws/?p=1048)
- [AWS再入門ブログリレー AWS Batch編](https://dev.classmethod.jp/articles/relay_looking_back_aws-batch/#aws-batch%E3%81%AE%E5%9F%BA%E7%A4%8E%E7%94%A8%E8%AA%9E)
System specを動かすのにはまった

## 概要
rspecでSystem specを流そうと思いましたが、いろいろハマるところが多かったので、自分がハマったところを記載しておきます。

## 環境
- 共通
    - Ruby 2.5.8
    - Rails 5.2.4
    - rspec 3.10.0
    - selenium-webdriver 3.142.7
- ローカル環境
    - Ubuntu 16.04(WSL2)
    - Chromium 87.0.4280.66
    - ChromeDriver 87.0.4280.66
- CI環境
    - Github Actions  
      :bulb: Chrome環境は特にインストールしなくても大丈夫でした

## ローカル環境でChromeが動かない
最初はWSL1上でSystem specを動かそうとしていましたが、うまく動いてくれませんでした。
いろいろな可能性を試したところWSLをVersion2にすることで動作させることが出来ました。
最終的に動作した内容が下記になります。

:memo: ChromeとChromeDriverのバージョンを合わせないといけないという記述がありましたが、下記をインストールすればChromiumがブラウザとして使われてバージョンも同じになるので簡単そうです。

```bash:chromeドライバのインストール
sudo apt-get install chromium-chromedriver
```

```ruby:rails_helper.rb
Capybara.default_driver = :selenium_chrome_headless
Capybara.register_driver :selenium_chrome_headless do |app|
  options = Selenium::WebDriver::Chrome::Options.new
  options.add_argument('--headless')
  options.add_argument('--no-sandbox')
  options.add_argument('--disable-gpu')
  options.add_argument('--window-size=1280,1024')

  Capybara::Selenium::Driver.new(app, browser: :chrome, options: options)
end
```
```ruby:spec_helper.rb
  config.before(:each, type: :system) do
    driven_by :selenium_chrome_headless
  end
```

## Github Actionsで日本語環境を整える
ローカル環境でSystem specが動くように整えた状態でGithub Actionsで動かしてみたところ、rspec自体は何もしなくて動きました :smile: 
Chrome環境を整える必要があると思っていたのですが、特に何もしなくても動きました。

ただ、System specは失敗になっていて失敗時の画像を見るとChromeの言語が英語になっているようでした。

```ruby:user_spec.rb
require 'rails_helper'

RSpec.feature 'Users', type: :system do
  let!(:user) { create :user }

  scenario "ログインできること" do
    visit login_path
    fill_in "email",    with: user.email
    fill_in "password", with: user.password

    click_button "ログイン"

    expect(page).to have_content("ホーム")
  end
end
```

- 失敗時のスクリーンショット  
   ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64261/6050aa68-3f50-f446-0abd-1e9b641939b3.png)

ChromeDriverの設定に`lang`をオプションとして追加すると解決するというページもちらほらあったのですが、うまくいきませんでした。

```ruby:rails_helper.rb
  options.add_argument('--lang=ja-JP')
```

結果的にはなんて言うことはなく、rspecを動かすときに`LANG`環境変数を設定してあげるだけでした :sweat_smile: 

```yml:run_rspec.yml
    - name: Run rspec
      run: bundle exec rspec
      env:
        LANG: ja-JP
        RAILS_ENV: test
```

最後に日本語フォントを入れてあげないと日本語が表示されないので、事前にインストールするようにしました。

```yml:run_rspec.yml
    - name: Install fonts
      run: sudo apt-get install fonts-ipafont-gothic fonts-ipafont-mincho
```

- 成功時のスクリーンショット  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/64261/729f589d-994c-f2ae-47a8-02d39b019a7e.png)
VTuberっぽく全身を写してYoutubeLiveで配信する

#はじめに
※この記事は執筆時の情報を元に作成されています。
スクショや詳細は随時追加する予定です。


#目的/ゴール
VTuberみたいに全身を写してYoutubeLiveに配信するための備忘録
今回は[Nintendo Switch](https://www.nintendo.co.jp/hardware/switch/)の[リングフィットアドベンチャー](https://www.nintendo.co.jp/ring/)を配信することを目的とします。

完成図
![スクリーンショット 2021-03-15 18.52.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230888/a073b1e8-bc0f-6dd2-3264-ec612e62b2e9.png)


↓は動画版

https://twitter.com/samawo26/status/1369562359778275330?s=20

#環境
VR環境で自分をトラッキングしつつ、ゲーム画面をキャプチャし配信します。
環境概要図
![環境.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230888/8aabb6d1-f942-9ae5-9a68-49bac31c475c.png)

##今回の環境で最低限必要なもの
* PC
 今回はWindows10です。

* HMD
 俗に言うVRゴーグルです。
 今回は[Oculus Quest2](https://www.oculus.com/quest-2/)を[VirtualDesktop](https://www.vrdesktop.net/)を利用してPCに接続して使っています。
##あると嬉しいもの
* キャプチャボード
 コンシューマゲームの配信をする場合は必須。
 今回は[Live Gamer Extreme 2 GC550 PLUS](https://www.avermedia.co.jp/product-detail/GC551)を使用

* トラッカー
 全身の動きを見せたい場合トラッカーがあるとより正確に体の動きを反映できます。
 今回は[viveトラッカー2018](https://htcvive.jp/item/99HANL005-00.html)を3つ使っています。

* 広い空間
 無いと怪我します。


#事前準備
大きく分けて3つ

* ゲームのキャプチャ
* アバター
* Youtubeアカウント

##ゲームのキャプチャ
PC画面を利用するのであれば、この項目は不要
キャプチャボードを使用してPCに映像を読み込めるようにします。
細かい設定等は調べてください。
##アバター
今回はVRChatを使ってアバターの表示を行います。
トラッキングについては、HMD,ハンドトラッキング(Questの機能),viveトラッカーを用いた6点トラッキングを用います。
VRChatの始め方やアバターのアップロード方法等は調べてください。
##Youtubeアカウント
Googleアカウントを持って入れば基本的に付属していると思います。
YoutubeStudioにアクセスして、アカウントでの配信を有効にしてください。
※承認まで24時間程度かかります。

#配信
##PC側の準備
配信ソフトは[OBS Studio](https://obsproject.com/ja/download)(以下OBS)を利用します。
OBSに映像キャプチャとVRChatの画面を追加していい感じに配置します。
VRChatの画面はクロマキー設定をして緑を抜く設定にすると後々楽です。
自分の声を届けたい場合は、オーディオソースも追加します。
##VRの準備
VRChatをSteamVRで起動して、カメラのフィルタをグリーンバックにします。
![VRChat_3840x2160_2021-03-15_16-50-11.304.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/230888/9fb080d8-f70e-2549-3f7c-596ae5bc50a6.png)

また、オーバレイソフトを使いOBSをVR空間上に展開します。
今回は[XSOverlay](https://store.steampowered.com/app/1173510/)を使用します。
配信時の視界スクショ追加予定

##配信開始
Youtube側で配信を開始し、OBS側の配信開始ボタンをクリックすれば配信が始まります。

#困ってること
* なぜかキャプチャ音声が安定しない
* ゲームコントローラとトラッキング用のコントローラを両方持てない
* ハンドトラッキングのトラッキング範囲や精度の問題で腕がよく飛ぶ
* 目が悪いからゲーム画面見づらい

#おわりに
VRはいいぞ

#参考など
* 今回使用しているアバター
[あずきちゃん](https://booth.pm/ja/items/2472916)

* OBSの配信まで
https://vip-jikkyo.net/stream-to-youtube-with-obs
LINQPadに複数テキスト行入力ダイアログを追加

C#erのみなさんは　[Linqpad](https://www.linqpad.net/) を使っている方も多いかと思います。 
ちょっとしたデータ分析では結果を表やグラフで出力でき、nugetも使えてデバッガも動きVisualStudioよりも便利なことも多いです。

そんな利用頻度の高いLinqpadですが、データとしてcsvやjsonなどの複数行テキストを入力したい場合、一度ファイルに保存して、そのファイル名を入力する形となるのが若干億劫です。1行だけなら以下のようにLinqpad標準の機能はあるのですが。

```csharp
var filename = Util.Readline("ファイル名を入力してください");
```
使い捨てのデータをわざわざファイル作成するのも面倒なので、実行中にテキストをペーストできるよう複数行入力ダイアログ機能を入れましょう。

## 利用イメージ

使い方は画像内のコードでイメージできるかと思いますが `MyExtensions.ReadLines()` で読み込みます。メソッド戻り値は`string[]`、キャンセルされた場合は空配列(string[0])が戻ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54535/a27eaacf-73d2-708a-a79c-a519879b5eba.png)


## ソース
以下コードをMy Queries/My Extensionsの中にある`My Extensions` クラス内にコピペし保存すれば どのQueryでも上記の画像のように使えるようになります。

```csharp
public static string[] ReadLines(string title = null)
{
	var win = new System.Windows.Window() { Width = 400, Height = 300, Title = title ?? string.Empty };
	var grid = new System.Windows.Controls.Grid();
	grid.RowDefinitions.Add(new System.Windows.Controls.RowDefinition() { Height = new System.Windows.GridLength(1, System.Windows.GridUnitType.Star) });
	grid.RowDefinitions.Add(new System.Windows.Controls.RowDefinition() { Height = new System.Windows.GridLength(36) });
	var margin3 = new System.Windows.Thickness(3);
	var tbox = new System.Windows.Controls.TextBox()
	{
		Margin = margin3,
		AcceptsReturn = true,
		AcceptsTab = true,
		VerticalScrollBarVisibility = System.Windows.Controls.ScrollBarVisibility.Auto,
		HorizontalScrollBarVisibility = System.Windows.Controls.ScrollBarVisibility.Auto,
	};
	var btnOk = new System.Windows.Controls.Button() { Content = "OK", Margin = margin3, Width = 80 };
	var btnCancel = new System.Windows.Controls.Button() { Content = "Cancel", Margin = margin3, Width = 80 };
	btnOk.Click += (_, _) => { win.DialogResult = true; win.Close(); };
	btnCancel.Click += (_, _) => { win.DialogResult = false; win.Close(); };
	var sp = new System.Windows.Controls.StackPanel()
	{
		Orientation = System.Windows.Controls.Orientation.Horizontal,
		HorizontalAlignment = System.Windows.HorizontalAlignment.Right,
	};
	sp.Children.Add(btnOk);
	sp.Children.Add(btnCancel);
	System.Windows.Controls.Grid.SetRow(tbox, 0);
	System.Windows.Controls.Grid.SetRow(sp, 1);
	grid.Children.Add(tbox);
	grid.Children.Add(sp);
	win.Content = grid;
	return win.ShowDialog() == true ? tbox.Text.Split("\r\n") : new string[0];
}
```
PROXY環境下でのPower Automate Desktopの利用方法

#0.Intro

Power Automate DesktopがWin10で無償で利用できるようになったということで話題になっているので社内でも要望があるかと思い事前調査をしました。
すると自宅で試したときは問題無く利用できたが、社内ではフロー一覧も開けず。そういえば確かに無償になる前にPower Automate Desktopを試していた時もVPNをつないだ状態だと利用できなかったことを思い出しました。
つまりは結局Proxyが原因だろうということで調査。
わかったことは
・PowerBI Desktopは現状PROXY環境に対応していない(らしい)。
・On-Premiss gatewayをPROXY環境に対応させるには以下を設定ファイルに追記する必要がある。

```
<system.net>
    <defaultProxy useDefaultCredentials="true" />
</system.net>
```

#1.改修

この二点を踏まえ試してみました。
上記を設定をPower Automate Desktopがインストールされている
C:\Program Files (x86)\Power Automate Desktop
内の以下のXMLファイルの

```
<configuration>
</configuration>
```

の中に設定してやることで、ひとまずフロー一覧の画面を開くことが出来、フロー作成、実行までは完遂できるようになりました。

PAD.AutomationBrowser.exe.config
PAD.BridgeToHLLAPIx64.exe.config
PAD.BridgeToHLLAPIx86.exe.config
PAD.BridgeToMODIx86.exe.config
PAD.BridgeToSapAutomation.exe.config
PAD.BridgeToUIAutomation2.exe.config
PAD.ChromeMessageHost.exe.config
PAD.Console.Host.exe.config
PAD.DataCollectionSwitch.exe.config
PAD.Designer.Host.exe.config
PAD.EdgeMessageHost.exe.config
PAD.FirefoxMessageHost.exe.config
PAD.MacroRecorder.Host.exe.config
PAD.Runtime.Robot.exe.config
PAD.UISpy.Host.exe.config
PAD.WebAutomationRegistryConfigurator.exe.config

恐らく残りの以下のファイルもすべて設定すればフル機能対応可能かと思われます。

Microsoft.Flow.RPA.Agent.exe.config
Microsoft.Flow.RPA.LauncherService.exe.config
Microsoft.Flow.RPA.LogShipper.exe.config
Microsoft.Flow.RPA.Notifier.exe.config
Microsoft.Flow.RPA.Sap.AgentServer.exe.config
RpaNativeHost.exe.config
UIFlowService.exe.config
UIRecorder.exe.config

但し当然のことながら非公式な方法ではあると思われます。(上記設定ファイルはデフォルトでは書き込み不可の設定になっていましたので)。

#2.課題
PowerBIにせよ、Power Automate Desktopにせよ企業で使われることが前提なのでProxyに対応していないというのは少々考えにくく、なにかしらきちんとしたProxy設定の方法がありそうなものなのですが…
兎も角、とりあえず動かしたいという方がおられたら、お役に立つ内容かと思い公開させて頂きました。
Reversing.kr Easy Unpack Writeup Using Ghidra

Reversing.kr Easy Unpack は OEP（Original Entry Point）を見つける問題。
OEP の可能性がある Far Jump を抽出する Ghidra Script を作成したので，Ghidra研究の記録として残しておく。

問題配布サイト

http://reversing.kr/challenge.php

Easy Unpack では，EXE とテキストファイルが渡され，テキストファイルには
Find the OEP
ex) 00401000
と記載されている。

まずは，IDA free
Graph overview で下の方へ行く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/63e2dea2-cdfd-590a-9e16-4abdaa6819a1.png)

Graph view の最後の方にあるジャンプ先をいくつか入力したら正解だった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/f47d2435-ebf6-5ef9-7f74-8c9863aa22e7.png)

--------

ここからが本題

OEP は VirtualAlloc などで新しく割り当てられたメモリ空間にあるので遠くに jump するはず。
遠くにジャンプするとき jump to any absolute address となるはず。 
Ghidra で Script を組んで Far Jump を自動的に探すことにチャレンジ。

参考サイト：ピンク先生の Ghidra Script 置き場

https://github.com/AllsafeCyberSecurity/ghidra_scripts


Ghidra API

https://ghidra.re/ghidra_docs/api/index.html

Far Jump を抽出する Ghidra Script

```python:far_jmp_search.py
from ghidra.program.model.listing import CodeUnit

jump_count = 0

#get all memory ranges
addr_ranges = currentProgram.getMemory().getAddressRanges()

for addr_range in addr_ranges:
    insts = currentProgram.getListing().getInstructions(addr_range.getMinAddress(), True)
    for inst in insts:
        flow_type = inst.getFlowType()
        if flow_type.isJump():            
            len = inst.getDefaultFallThroughOffset()
            # Exclude near jumps
            if len > 2:
                print("{} {} {}".format(inst.address, inst, len))
                jump_count += 1
            
print('far jump count: {}'.format(jump_count))
```

結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1163572/77f068f0-3f64-5017-50fe-30b6e1af463f.png)
【配列】forEach・map・filter・find・some・reduce

# 処理概要
## forEach
- 配列中のそれぞれの要素について関数を実行する

```js
const numbers = [1, 2, 3, 4];

numbers.forEach(num=> console.log(num));

// 出力
// 1
// 2
// 3
// 4
```

## map
- 配列内のすべての要素に指定した関数を実行する
- 関数の結果を格納した新しい配列を生成する

```js
const numbers = [1, 2, 3, 4];
const squares = numbers.map((num) => {
  return num * num;
});

console.log(numbers); // [1, 2, 3, 4]
console.log(squares); // [1, 4, 9, 16]
```

## filter
- 指定した関数の結果が `true` を返す配列中の要素を格納した新しい配列を生成する

```js
const numbers = [1, 2, 3, 4];
const evenNumbers = numbers.filter((num) => num % 2 === 0);

console.log(numbers); // [1, 2, 3, 4]
console.log(evenNumbers); // [2, 4]
```

## find
- 指定した関数を満たす配列内の最初の要素の値を返す

```js
const numbers = [1, 2, 3, 4];
const firstEvenNumber = numbers.find((num) => num % 2 === 0);

console.log(numbers); // [1, 2, 3, 4]
console.log(firstEvenNumber); // 2
```

- 1つも見つからない場合は `undefined` を返す

```js
const numbers = [1, 3, 9];
const firstEvenNumber = numbers.find((num) => num % 2 === 0);

console.log(numbers); // [1, 3, 9]
console.log(firstEvenNumber); // undefined
```


## some
- 指定した関数を配列中の少なくとも1つの要素が満たした場合に `true` を返す

```js
const numbers = [1, 2, 3, 4];
const isEvenNumbers = numbers.some((num) => num % 2 === 0);

console.log(numbers); // [1, 2, 3, 4]
console.log(isEvenNumbers); // true
```
```js
const numbers = [1, 3, 9];
const isEvenNumbers = numbers.some((num) => num % 2 === 0);

console.log(numbers); // [1, 3, 9]
console.log(isEvenNumbers); // false
```

## reduce
- 配列のすべての要素に対して指定した関数を（左から右の順で）実行し、単一の出力値を生成する

```js
const numbers = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;

// 1 + 2 + 3 + 4
console.log(numbers.reduce(reducer)); // 10

// 5 + 1 + 2 + 3 + 4
console.log(numbers.reduce(reducer, 5)); // 15
```

# 上記を全て forEach で実現するのはだめなのか
## 結論
- `forEach` は極力使用しない

## 理由
- 適切なメソッドを選択することで可読性が上がるため

# 参考
- [Array - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array)
Linux覚え書きシェルスクリプト

# 概要
Linux学習で覚えたコマンドや知識をメモする記事です。

# 事前知識
## 実行権限
実行できる権限があること
権限については、[Linux覚え書きユーザ管理](https://qiita.com/Momomo_ai/items/6e1c91893baf3f58223f)を参照。
## シェル変数と環境変数
+ シェル変数 :実行しているシェルの内部でのみ有効
+ 環境変数 : シェル外で引き継げる


# 基本コマンド

## echo :文字列の出力
```
echo [オプション] 文字列
```
| オプション | 説明 |
|:-----------|:----|
| -n | 改行削除

## シェル変数
変数定義

```bash
a=1
echo $a #変数の内容を参照
```
### 配列
```bash
a[0]=1
a[1]=2
index=1
echo ${abc[index]}
```

### 変数の一覧表示

シェル変数の一覧を表示する

```
set
```

環境変数の一覧表示

```
env
```

### unset: 変数を削除する
```
unset []
```

## export :環境変数を作成する
```
export a
export y=123
```
## read :標準入力を読み込む
```
read [変数名]
```
## 引用符

'(シングルクォート)、"(ダブルクォート)は文字列で表現する
`(バッククォート)はコマンドが実行される

```bash

test=date

echo 'aaa $test' # aaa $test
echo "aaa $test" # aaa date
echo "aaa `$test`" # aaa thu mar 20YY MM 

```

## 引数
実行時にオプションを引数として参照する

```bash
echo $1
echo $2
```
## ￥ :特殊な文字列

+ 特殊な文字を文字列で表示したい場合に、直前に￥をつける
+ 改行として行末に￥を追加する

## source :指定ファイルを読み込んでシェル環境を設定

```
# bash_profileなどを更新した時
source ~/.bash_profile

# シェルスクリプトで定義された変数を環境変数として取り込む
source ./test.sh

```
## 条件分岐 / 繰り返し
### if文
```bash
if 条件式 then ... elif 条件式 else fi
```

| 演算子 | 説明|
|:---|:----|
|a == b | aとbが等しければ真
| a != b | aとbが等しくなければ真
|a -eq b | aとbが等しければ真
| a -ne b | aとbが等しくなければ真
| a -ge b | aがb以上であれば真
| a -le b | aがb以下であれば真
| a -gt b | aがbより大きい時は真
| a -lt b | aがbより小さい時は真

### case文
```bash
case 変数 in
   値A)
     処理1;;
   値B|値3)
     処理2;;
   *)
     # 条件に一致しなかった場合の処理
     処理3;;
esac
```
### for文
```bash
for 変数 in 値のリスト
do
   echo $i
done
```
### while/until文

```bash
while 条件式 # 成立する間繰り返す
do
   処理
done

until 条件式 # 成立しない間繰り返す
do
   処理
done

# カウンタで回す
count=1
while [$count -le 10]
do
   count=`$count +1`
done
```
### select文
```bash
select name in "a" "b" "c"
do
    echo "you selected $name"
done

# 実行結果
# 1) a
# 2) b
# 3) c
# $?
```
### 繰り返しの制御
break : 繰り返しを終了
continue :繰り返しの先頭に戻る

## 複数の条件
AND条件

```bash
条件A -a 条件b -a 条件C
条件a && 条件b && 条件C
```

OR条件

```bash
条件A -o 条件b -o 条件C
条件a || 条件b || 条件C
```

## ファイルの有無確認

```bash
# test コマンドを利用
if test -d path ; then...
# []を利用
if [-d path]; then
```
| 演算子 | 説明|
|:---|:----|
|-f ファイル名 | ファイルなら真
|-d ファイル名 | ディレクトリなら真
| -e ファイル名 | ファイルが存在すれば真
| -L ファイル名 | シンボリックリンクなら真
| -r ファイル名 | 読み取り可能なファイルなら真
| -w ファイル名 | 書き込み可能なファイルなら真
| -x ファイル名 | ファイルが存在して、実行権限があれば真
| -s ファイル名 | サイズが0より大きいなら真

## サブルーチン
### 関数
```bash

function 関数名
{
    処理
}

関数名()
{
    処理
}

# 関数を実行(引数付き)
関数名 aa bb

```
### return
関数内での処理を終了する


```
return 変数名
```

## デバッグ

```bash

sh -x ファイル
```
WindowsでOne Drive for Businessの同期修正方法

#はじめに
最新の機能更新や品質更新プログラムを適用した時にタスクバーにOne Driveのアイコンが表示されず、デスクトップアプリのOne Drive(Business)とブラウザOne Drive(Business)間で同期が取れない事があった。その状態でしばらくして同期を直した時にせっかく作ったデータが消えてえらい目にあったので、自分と同じ思いをしないように簡単に同期修復を行える方法を書きます。

*<font color="Red">＊注意　レジストリを触るので管理者権限を持ったユーザーが必要になります。</font>*

#事前準備

レジストリを直接触る場合は間違ったキーを編集したり、不具合が出た時に切り戻せるようにバックアップを取る必要がありますが、誰でも簡単にできるとは言えない。そこで、修正を行う設定を書き込んだbatファイルで変更を行います。

準備するものは、以下の通りになります。


<b>1.テキストエディタ</b>
最終的にはbat化するので使用します。
拡張子が変更できるものであれば、何でもいいです。

<b>2.レジストリキー</b>
スタートボタン(Windowsボタン)+Rで検索ボックスを起動し、ボックス内にregeditと入力してレジストリエディタを起動して対象のレジストリの場所を確認します。

##1. 現在のレジストリの設定値を確認
現在One Dirveのレジストリがどうなっているか確認するbatファイルを作成します。

<b>1.コマンド</b>

```
cd %~p0
hostname >>%USERPROFILE%\Desktop\OneDrive.txt
whoami >>%USERPROFILE%\Desktop\OneDrive.txt
echo %date% %time% >>%USERPROFILE%\Desktop\OneDrive.txt
reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\OneDrive >>%USERPROFILE%\Desktop\OneDrive.txt
Pause
```
名前を付けて保存後にbatファイルにするために拡張子の変更を行います。

Batファイルの中身はbatファイルの場所に移動後使用している端末名、ユーザー名、日時とOne Driveの設定値を出力するようにしています。

*OneDriveのレジストリキー*
HKEY_LOCAL_MACHINE \SOFTWARE\Policies\Microsoft\Windows\OneDrive


*拡張子変更*
txt→bat

<b>2.実行</b>
batファイルをダブルクリック。

<b>3.設定値の確認</b>
レジストリキーの下に値が記載されているのでDisableFileSyncNGSCがあるか確認をします。

*<font color="Red">＊DisableFileSyncNGSC があると同期ができない状態</font>*

HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\OneDrive
    DisableLibrariesDefaultSaveToOneDrive    REG_DWORD    0x0
    DisableFileSync    REG_DWORD    0x1
    DisableFileSyncNGSC    REG_DWORD    0x1


##2. レジストリの削除

1.で確認したDisableFileSyncNGSCの削除を行います。

<b>1.コマンド</b>

```
cd %~p0
hostname >>%USERPROFILE%\Desktop\OneDrive_del.txt
whoami >>%USERPROFILE%\Desktop\OneDrive_del.txt
echo %date% %time% >>%USERPROFILE%\Desktop\OneDrive_del.txt
reg delete HKLM\SOFTWARE\Policies\Microsoft\Windows\OneDrive /v DisableFileSyncNGSC 
Pause
```
名前を付けて保存後にbatファイルにするために拡張子の変更を行います。

Batファイルの中身はbatファイルの場所に移動後使用している端末名、ユーザー名、日時とDisableFileSyncNGSCの削除するようにしています。

*拡張子変更*
txt→bat

<b>2.実行</b>
batファイルをダブルクリック。
＊実行しているのが一般ユーザーの場合は管理者権限のユーザーを求められます！

<b>3.設定値の確認</b>
1.	で使用したbatファイルを再度実施して現在のレジストリを確認します。

出力ファイルにDisableFileSyncNGSCが消えています。

##3. One Driveの同期

レジストリを確認後端末を再起動ご、ブラウザのOne Driveを開いて同期を選択してデスクトップアプリのOne DriveとブラウザのOne Drive間で同期できることを確認します。

##4. まとめ
・今まではレジストリで直接変えていて結構面倒くさかったからbatファイルを作って大分楽に同期修正ができるようになったよ！
docker-composeの.envファイルの認識場所が変わった

どうやら最近、(おそらく2021/3/5リリースのdocker desktop for windows v3.2.1から)docker-composeの.envファイルの認識場所が変わったようです。特にVSCodeで[Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)を使っている人は注意が必要です。

# 解決策

いままで`.env`ファイルは`docker-compose`コマンドを実行したときのディレクトリ(ワーキングディレクトリ)にある必要があったのですが、今度からは`docker-compose.yml`と同じ階層に保存してください。

# 環境

|||備考|
|:----:|:------:|:------:|
|OS|Windows10||
|docker desktop for windows|3.2.1||
|docker-compose | 1.28.5||
|VSCode|1.54.1||
|[Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) | v0.163.2 ||


# 前提知識

`.env`ファイルとは`docker-compose.yml`中でホストの環境変数を使っているときに、ホストに使いたい環境変数が設定されていない場合の回避策として使えるファイルです。`.env`ファイルについて書いた過去記事あるので参考にしてください。

https://qiita.com/SolKul/items/989727aeeafcae28ecf7

# 変更点

いままでこの`.env`ファイルは`docker-compose`コマンドを実行したときのディレクトリ(ワーキングディレクトリ)にないと認識しなかったのですが、今度からは`docker-compose.yml`と同じ階層にないと認識しなくなったようです。

例えば、

```docker-compose.yml
web:
  image: "webapp:${TAG}"
```

```yml:.env
TAG=v1.5
```

の２つを

```
dc_test/
　└  test_directory/
　   ├ .env
     └ docker-compose.yml
```

と同じディレクトリに保存すれば、`dc_test`階層からコマンドを打ったとしても、

```shell
dc_test> docker-compose -f test_directory/docker-compose.yml config
web:
  image: webapp:v1.5
```

と今まで通り使えます。

# VSCodeのRemote Containerで.envファイルを使っている人

VSCodeで[Remote - Containers](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)を使って、`devcontainer.json`から`dockerComposeFile`の項で`docker-compose.yml`を指定し、リモートコンテナを立ち上げていて、その際プロキシを設定するためなどに`.env`ファイルを使っている方も多いと思います。

いままではVSCodeのルートディレクトリに`.env`ファイルを置く必要があったと思いますが、今からは.envファイルは`docker-compose.yml`と同じフォルダに置くようにしてください。
jetson-io.pyがすぐに終了するときの解決策

Jetsonを用いてSPIピンをenableにしようと思ったのですが、その際に意図しないエラーが2つ？出たので、その解決方法について述べます。

まず前提として、
`sudo mkdir /boot/dtb/`や、
`sudo cp -v /boot/tegra210-p3448-0000-p3449-0000-[ab]0[012].dtb /boot/dtb/`
などの[このサイトに従った設定](https://www.jetsonhacks.com/2020/05/04/spi-on-jetson-using-jetson-io/)は済ませていると仮定します。



# 問題1
```
$ sudo /opt/nvidia/jetson-io/jetson-io.py
```
を実行したときに、起動した瞬間、エラーも何も表示されずにスクリプトが終了する。

## 解決法

この問題は[このサイト](https://forums.developer.nvidia.com/t/jetpack-4-3-l4t-r32-3-1-released/109271/20)を参考に解決させました。


`/opt/nvidia/jetson-io/Jetson/board.py`のソースコードを下記のように編集します。

```
# 133行目くらい
    #dtbdir = os.path.join(self.bootdir, 'dtb')
    dtbdir = os.path.join(self.bootdir, '')
```

この変更後に、もう一度↓のコマンドを入力すると通常通りにピン設定をすることができました。

```
$ sudo /opt/nvidia/jetson-io/jetson-io.py
```

<br>  



# 問題2
問題1を解決して再起動した後に、もう一度`sudo /opt/nvidia/jetson-io/jetson-io.py`で設定が完了しているか確かめようとすると、問題1と同じ現象が起こる。

## 解決策
先ほど変更した`/opt/nvidia/jetson-io/Jetson/board.py` のソースコードを元に戻します。。。

```
# 133行目くらい
    dtbdir = os.path.join(self.bootdir, 'dtb')
    # dtbdir = os.path.join(self.bootdir, '')
```

<br>  

それでも起動しないので、どんなエラーが生じているかを調べるために、下記のスクリプトを動かしてみます。
以下が実行法とエラー出力です。

```
$ sudo /opt/nvidia/jetson-io/config-by-pin.py

> Traceback (most recent call last):
  File "/opt/nvidia/jetson-io/config-by-pin.py", line 51, in <module>
    main()
  File "/opt/nvidia/jetson-io/config-by-pin.py", line 34, in main
    jetson = board.Board()
  File "/opt/nvidia/jetson-io/Jetson/board.py", line 150, in __init__
    self.dtb = _board_get_dtb(self.compat, self.model, dtbdir)
  File "/opt/nvidia/jetson-io/Jetson/board.py", line 91, in _board_get_dtb
    raise RuntimeError("Multiple DTBs found for %s!" % model)
RuntimeError: Multiple DTBs found for NVIDIA Jetson Nano Developer Kit!
```

Multiple DTBsとのことで、`.dtb`ファイルが多いと述べられているようです。

エラーが出ている箇所でprintをしてみると、下記のような出力がされました。
`['/boot/dtb/tegra210-p3448-0000-p3449-0000-b00.dtb', '/boot/dtb/kernel_tegra210-p3448-0000-p3449-0000-b00.dtb']`

<br>  
この`.dtb`ファイルについて、前者が[このサイトで実行した](https://www.jetsonhacks.com/2020/05/04/spi-on-jetson-using-jetson-io/)`sudo cp -v /boot/tegra210-p3448-0000-p3449-0000-[ab]0[012].dtb /boot/dtb/`でコピーされてきた分です。それに対し、後者は なんか知らんやつ です（適当）。


ということで、システムが なんか知らんやつ を認識しないようにします。

```
sudo mv /boot/dtb/kernel_tegra210-p3448-0000-p3449-0000-b00.dtb /boot/dtb/kernel_tegra210-p3448-0000-p3449-0000-b00.dtb.bkp 
```

<br>  
最後に目的のコマンドを実行すると、通常通りに動作することが確認できました。

```
$ sudo /opt/nvidia/jetson-io/jetson-io.py
```


やったね ;)
【JavaScript】axiosでCSVを受け取りブラウザでファイルダウンロードに移行させる方法。



# 前提
サーバーサイドはLaravel、フロントはVue.jsです。
今回はJavaScriptの話がメインなのでPHPやLaravelが分からない方でも支障なく読んでいただけると思います。

# やりたかったこと
ユーザー目線：ブラウザに表示されているダウンロードボタンをクリックすると、CSVファイルをダウンロードできる。

僕目線：LaravelでCSVを吐き出すAPIを用意し、axiosを使ってAPIを叩きCSVをダウンロードできるようにする。
#起こった問題
ブラウザの検索タブでエンドポイントに直接アクセスするとうまくダウンロードできるが、axiosを使ってエンドポイントにアクセスするとダウンロードできない。（CSVの内容はデータとして返ってくるが、ブラウザがダウンロード画面に移行しない。）
# 失敗した方法
CSVを返すAPIを単純に叩いてダウンロードできると思ったのですが、うまくいきませんでした。
※関係ない部分は色々省略してます。

```Javascript:sample.js
downloadCsv() {
    axios
      .get("/api/download/csv", {
      })
      .catch((error) => {
        console.log(error.messagae);
      });
  },
```

CSVのデータはちゃんと返ってきますが、ブラウザがダウンロードに移行しません。（ユーザー目線だと何も起こらない。）

# うまくいった方法
コードを下記のようにかえるとうまくダウンロードできました。

```Javascript:sample.js
import saveAs from "file-saver";
// 省略
downloadCsv() {
  axios
    .get("/api/download/csv", {
      responseType: "blob",
    })
    .then((res) => {
      let mineType = res.headers["content-type"];
      const name = res.headers["content-disposition"];
      const blob = new Blob([res.data], { type: mineType });
      saveAs(blob, name);
    })
    .catch((error) => {
      console.log(error.messagae);
    });
};
```

追記部分を解説していきます。


## リクエスト時の処理
```Javascript:sample.js
.get("/api/download/csv", {
  responseType: "blob",
})
```
データをBLOBとして受け取ります。
BLOBとは`Binary Large OBject`の略で、[IT用語辞典](https://e-words.jp/w/BLOB.html)では以下のように解説されています。
>BLOBとは、データベースのフィールド定義などで用いられるデータ型の一つで、テキスト（文字列）や整数のように既存のデータ型としては用意されていない任意のバイナリデータを格納するためのもの。

つまり、BlOBはテキストファイルだけではなく画像やPDFなどいろいろな形式のファイルを扱うことができる訳です。

そしてJavaScriptでは受け取ったデータをBOLBにするとこで、ファイルにすることが可能になります。

CSVはテキストデータなのでBLOBにする必要はないのでは？と思ったのですが、データをファイルにするために必要なようです。
## レスポンスを受け取った後の処理

```Javascript:sample.js
.then((res) => {
  let mineType = res.headers["content-type"];
  const name = res.headers["content-disposition"];
  const blob = new Blob([res.data], { type: mineType });
  saveAs(blob, name);
})
```

`res.headers["content-type"]`にはLaravel側で設定した`'text/csv'`が格納されています。今回は必ず`'text/csv'`が返るようになっていますが、ダウンロード用のメソッドを色々な形式に対応できるようにするためにこのように書く必要がありますね。

`res.headers["content-disposition"]`にはLaravel側で設定したファイル名が格納されています。

`new Blob([res.data], { type: mineType });`
JavaScriptのBlobオブジェクトのインスタンスを生成しています。
BlobオブジェクトはBLOBをJavaScriptで扱うためのオブジェクトです。
第一引数にファイルの内容の配列、第二引数にファイルの種類を指定して使います。

`saveAs(blob, "file");`
`'file-server'`というライブラリを使ってファイルを保存しています。
詳しくは割愛しますが、ブラウザ間で異なるファイル保存の処理を１行で書けます。




# 文字化けを修正
無事CSVをダウンロードすることができましたが、ファイルの内容が文字化けしていました。
はっきりとした原因は分かりませんでしたが、Laravel側で`SJIS`形式に変換していた部分を`UTF-8`に変換するように変更すると文字化けしなくなりました。

```PHP:sample.php
// mb_convert_encoding($content, 'SJIS', 'auto');
mb_convert_encoding($content, 'UTF-8', 'auto');
```
HTMLのmetaタグでUTF-8に指定しているのが原因なのか？
とりあえず、これで内容も分かるCSVファイルをダウンロードできるようになりました。めでたしめでたし。

# 最後に
aixosを使ってファイルをダウンロードするにはひと手間いることが分かりました。
BLOBについては恥ずかしながら全然知らなかったです。

今回はLaravel側でCSVを作成しましたが、データをJSON形式で送るようにしてVue側でCSVを作成することもできそうです。

他にもこんな方法があるとか、ここ間違ってるよとかあればご指摘等よろしくお願いします！
ではでは。
【Git】リポジトリ作成

# リポジトリ作成

リポジトリ作成するときの毎回の儀式
忘れるのでメモする

# 流れ

対象ディレクトリに移動

```
cd vue-tasks/

```

git初期化

```
git init
```

状態確認

```
git status
```

差分ステージングに追加

```
git add .
```

コミットする

```
git commit -m "initial commit"
```

github上でリモートリポジトリを作成する

リモートリポジトリのアドレスを名前を付けて記録（これをすることでリモートリポジトリへの操作をするときいちいちURLを叩かなくて済む）

- origin：記録する名前

- ``https://github.com/tanoken729/vue-tasks.git（例）``：

リモートリポジトリのURL

```
git remote add origin https://github.com/tanoken729/vue-tasks.git（例）
```

リモートリポジトリに変更履歴をpushする
実行オプションで-uを指定すると、次回以降はそのブランチ名の指定を省略できる（一番初めはorigin masteを省略できない）

```
git push -u origin master
```

リモートリポジトリをgithubで作成したときのREADME.mdがある（githubで作成したときにREADME.md作成した場合）とpushが失敗する

対処法
https://qiita.com/kazuki0714/items/ceda3a6721a9a99082de
もう一個あったはず。

# 参照
[サル先生のGit入門【新しいリポジトリを作成する】](https://backlog.com/ja/git-tutorial/intro/07/)
Laravel Mixで環境に応じて複数の.envで環境変数を管理する

# はじめに
こんにちは。先日、ランニングシューズ以外でいい靴を買ってしまいました、筆者です:smile:

さて、LaravelでWebpack使っているので、**Laravel Mix**を使用しております。

その際に**.env**で環境変数を分ける機会がありましたので、共有します。

# 結論
急いでいる方はこちらご参照くださいませ。**Laravel Mix**のドキュメントの書いてあるんです:ok_hand:
(ただ、スター:star:が超少ないのが気になります...:thinking:)

https://laravel-mix.com/extensions/env-file


# 解説
## 1. `mix-env-file` をインストールする。
```sh
$ npm install mix-env-file
```

## 2. `dotenv` をインストールする。
あとでコンパイルするときにインストールしろと怒られるのでこちらもインストールします。
(ドキュメントには書いてないんです:sob:)

```sh
$ npm install dotenv
```
## 3. package.jsonを編集する。
サンプルはこちら↓

```json:package.json
"scripts": {
  "dev": "cross-env ENV_FILE=./.env.dev npm run development",
  "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
  "watch": "npm run development -- --watch",
  "watch-poll": "npm run watch -- --watch-poll",
  "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
  "prod": "cross-env ENV_FILE=./.env.prod npm run production",
  "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
}
```

上記の場合、以下の動きをします。
`npm run dev`したときは、`./.env.dev`を読みます。
`npm run prod`したときは、`./.env.prod`を読みます。

## 4. `webpack.mix.js` を編集する。
```js:webpack.mix.js
let mix = require('laravel-mix');
require('mix-env-file');

// Then pass your file to this plugin
// If this is not set, this plugin won't do anything and the default .env variables will remain
mix.env(process.env.ENV_FILE);
```

以上です:ok_hand:

# おわりに
**.env**便利でした:smile:
それでは！
DjangoでダミーAPI作成

#背景
Djangoでテスト用のAPIを作成しました。
特徴としてはRequestとResponseをコンソールログで確認できます。
使い方としては外部APIを呼び出しするときに現場ではなかなか外部APIを本物使うことが
できないのでそういったときに使います。
#仕様
テスト用のAPIである。
Requestをコンソールログで確認できる。
Responseをコンソールログで確認できる。
言語はPython3
フレームワークはDjango, Django REST
Post通信である。
#主要なソース
```py
models
from django.db import models

# Create your models here.
from django.db import models
from pygments.lexers import get_all_lexers
from pygments.styles import get_all_styles

LEXERS = [item for item in get_all_lexers() if item[1]]
LANGUAGE_CHOICES = sorted([(item[1][0], item[0]) for item in LEXERS])
STYLE_CHOICES = sorted([(item, item) for item in get_all_styles()])


class Snippet(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    title = models.CharField(max_length=100, blank=True, default='')
    code = models.TextField()
    linenos = models.BooleanField(default=False)
    language = models.CharField(choices=LANGUAGE_CHOICES, default='python', max_length=100)
    style = models.CharField(choices=STYLE_CHOICES, default='friendly', max_length=100)

    class Meta:
        ordering = ['created']
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer

views
@api_view(['GET', 'POST'])
def snippet_list(request):
    """
    List all code snippets, or create a new snippet.
    """
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = SnippetSerializer(data=request.data)
        if serializer.is_valid():
            print(request.data)
            serializer.save()
            print(serializer.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
```
#動きは動画参照ください。
https://www.youtube.com/watch?v=PtKavsgPhjY
初めてのRaspberry Pi Pico ㉚ CircuitpythonでI2CScanner

　PicoにはI2Cバスが2系統ありますが、利用できるピンはいっぱいあります。つまり、いっぱいあるGPIOのうち2組が同時に使えます。
<img width="200" alt="ファイル名" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/713fe93f-0c26-451f-c386-1f872640f86b.png">

　ピンの組み合わせは、次のペアです。奇数がSCLで偶数ピンがSDAです。

[GP1,GP0], [GP3,GP2], [GP5,GP4], [GP7,GP6], [GP9,GP8], [GP11,GP10], [GP13,GP12],[GP15,GP14], [GP17,GP16], [GP19,GP18], [GP21,GP20], [GP27,GP26]
###スキャナ
　見つけてきたスレーブ・デバイス（複数対応）のアドレスを16進で表示します。GPIOのピンは、手動で利用するピンを入力して使ってください。なお、モジュールはbusioではなくbitbangioでも同じように使えます。

```python
from board import *
from busio import I2C

i2c = I2C(GP21, GP20)

while not i2c.try_lock():
    pass

for i in i2c.scan():
    print('addr 0x{0:x}'.format(i))

i2c.deinit()
```

PS.

```
for i in i2c.scan():
    print('addr 0x{0:x}'.format(i))
```

は、

```
print([hex(i) for i in i2c.scan()])
```
でも、16進表示ができます。
[memo] EC2、httpsで画面を開くまで

https://qiita.com/nullponn/items/23b1b6ca671f540f51ae

https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/SSL-on-amazon-linux-2.html

▼暗号スイートの一覧 
openssl ciphers -v 'ALL:COMPLEMENTOFALL'
https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/7/html/security_guide/sec-hardening_tls_configuration