クソ雑魚エンジニアのメモ帳

学んだことを書くところ

開発環境のためのCORS有効化(phalconFrameWorkとwebpackとaxios)

背景

趣味で作っているwebアプリが、フロントエンドとバックエンドをRESTfulでつないでいる

  • フロントエンド側はvueとwebpackを使っていて、dev環境では、ビルドしなくてもローカルサーバー立ち上がってほっとりローディングですぐに修正を確認できる
  • サーバーサイドでは、dockerを使っているので、dockerコマンド一つでサーバーが立ち上がる。こちらも即座に反映される(当たり前)

これらの特徴から、フロントエンド内での開発PDCA・サーバーサイド内での開発PDCAはだいぶ早いと思っている。

ただ、フロントエンドとサーバーサイドでの疎通確認がボトルネックになると考えていて、チームでの開発では、フロントエンドの開発者とサーバーサイドの開発者は異なることが多い。なので、疎通確認をしたいのに、フロントエンド(またはサーバーサイド)がうまく起動できない(泣)みたいな状況になると思った。PDCA劇遅の予感がする。

現状

フロントエンドとサーバーサイド両方のローカルサーバーを立ち上げてaxiosを使って疎通しようとしても、疎通できない。

セキュリティの観点から、CORSという機能は制限されているためらしい

オリジン間リソース共有 (Cross-Origin Resource Sharing, CORS) は、追加の HTTP ヘッダーを使用して、ユーザーエージェントが現在のサイトとは別のオリジン (ドメイン) のサーバーから選択されたリソースにアクセスする権限を得られるようにする仕組みです。ユーザーエージェントは、現在の文書のオリジンとは異なるドメイン、プロトコル、ポート番号からリソースを要求するとき、オリジン間 HTTP 要求を発行します。

例えば http://domain-a.com から読み込まれた HTML ページが、 <img> src で http://domain-b.com/image.jpg に対して要求を行う場合です。今日のウェブ上では、多くのページが CSS スタイルシートや画像、スクリプトといったリソースを、コンテンツ配信ネットワーク (CDN) などの別のドメインから読み込んでいます。

セキュリティ上の理由から、ブラウザーは、スクリプトによって開始されるオリジン間 HTTP 要求を制限しています。例えば、 XMLHttpRequest や Fetch API は同一オリジンポリシー (same-origin policy)に従います。これは、これらの API を使用するウェブアプリケーションは、 CORS ヘッダーを使用しない限り、アプリケーションが読み込まれたのと同じドメインからしかリソースを HTTP で要求できないことを意味します。

https://developer.mozilla.org/ja/docs/Web/HTTP/HTTP_access_control

なので、phalconフレームワーク側でCORSの制限を緩和する必要がある。あくまで開発環境の時のみ。

仕様の調査

axiosから、クロスドメインなURLを叩くと、勝手にpreflightが飛んでいくようである

f:id:Kouchannel55:20180516225556p:plain

公式にも書いてあった。今回はJSONでやりとりするため当然のようだ

https://developer.mozilla.org/ja/docs/Web/HTTP/HTTP_access_control#Examples_of_access_control_scenarios

webpackでのproxy設定

簡単な修正方法があった! :surfer:

https://www.yoheim.net/blog.php?q=20170803

webpackの設定ファイルに以下を追記。

    proxyTable: {
      '/api/*': {
        target: 'http://localhost:8111',
        secure: false
      }
    },

僕の場合はこうなった(ほぼテンプレですが)▼

index.js▼

'use strict'

// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')

module.exports = {
  build: {
    env: require('./prod.env'),
    index: path.resolve(__dirname, '../../phalcon/public/index.html'),
    assetsRoot: path.resolve(__dirname, '../../phalcon/public'),
    assetsSubDirectory: 'static',
    // assetsPublicPath: '/',  TODO GithubPagesにあげるtめに修正
    assetsPublicPath: '',
    productionSourceMap: true,
    // Gzip off by default as many popular static hosts such as
    // Surge or Netlify already gzip all static assets for you.
    // Before setting to `true`, make sure to:
    // npm install --save-dev compression-webpack-plugin
    productionGzip: false,
    productionGzipExtensions: ['js', 'css'],
    // Run the build command with an extra argument to
    // View the bundle analyzer report after build finishes:
    // `npm run build --report`
    // Set to `true` or `false` to always turn it on or off
    bundleAnalyzerReport: process.env.npm_config_report
  },
  dev: {
    env: require('./dev.env'),
    port: 8080,
    autoOpenBrowser: true,
    assetsSubDirectory: 'static',
    assetsPublicPath: '/',
    proxyTable: {
      '/api/*': {
        target: 'http://localhost:8111',
        secure: false
      }
    },
    // CSS Sourcemaps off by default because relative paths are "buggy"
    // with this option, according to the CSS-Loader README
    // (https://github.com/webpack/css-loader#sourcemaps)
    // In our experience, they generally work as expected,
    // just be aware of this issue when enabling this option.
    cssSourceMap: false
  }
}

結局だめだった。そもそもリクエスト先が変更されていない。。。

f:id:Kouchannel55:20180516225455p:plain

f:id:Kouchannel55:20180516225527p:plain

愚直にサーバーサイド側でCORSを有効化することにした

phalconでのCORSを有効化(サーバーサイド)

公式で発見。

CorsMiddleware.php

<?php

use Phalcon\Events\Event;
use Phalcon\Mvc\Micro;
use Phalcon\Mvc\Micro\MiddlewareInterface;

/**
 * CORSMiddleware
 *
 * CORS checking
 */
class CORSMiddleware implements MiddlewareInterface
{
    /**
     * Before anything happens
     *
     * @param Event $event
     * @param Micro $application
     *
     * @returns bool
     */
    public function beforeHandleRoute(Event $event, Micro $application)
    {
        if ($application->request->getHeader('ORIGIN')) {
            $origin = $application->request->getHeader('ORIGIN');
        } else {
            $origin = '*';
        }

        $application
            ->response
            ->setHeader('Access-Control-Allow-Origin', $origin)
            ->setHeader(
                'Access-Control-Allow-Methods',
                'GET,PUT,POST,DELETE,OPTIONS'
            )
            ->setHeader(
                'Access-Control-Allow-Headers',
                'Origin, X-Requested-With, Content-Range, ' .
                'Content-Disposition, Content-Type, Authorization'
            )
            ->setHeader('Access-Control-Allow-Credentials', 'true');
    }

    /**
     * Calls the middleware
     *
     * @param Micro $application
     *
     * @returns bool
     */
    public function call(Micro $application)
    {
        return true;
    }
}

app.phpまたはmodule.php(ルーティング)▼

/**
 * Create a new Events Manager.
 */
$eventsManager = new Manager();
$application   = new Micro();

$eventsManager->attach('micro', new CorsMiddleware());
$application->before(new CorsMiddleware());

$application->setEventsManager($eventsManager);

https://docs.phalconphp.com/cs/3.1/application-micro#middleware-events-api-cors

しかしこれでは動かなかった :cry: 。原因はbeforeHandleRouteに処理が走っていないためみたいだが、根が深そうなので他の方法を探すことにした。

phalconForumから発見

https://forum.phalconphp.com/discussion/443/enable-cross-origin-resource-sharing

どうやら、プリフライト時と、その後どちらのリクエストでも、Access-Control-Allow-XXXXXをつける必要があるらしいので以下の様にした。

optionsの範囲とかヘッダーパラメータの付け方とかガバガバなのはスルーしてください :smirk:

$app->options('/(.*)', function () use ($app) {
    $app->response->sendHeaders();
});

app.php(diまわり)▼

$di->setShared('response', function () {
    $response = new Response();
    if (getenv("MODE") == "development") {
        $response->setHeader('Access-Control-Allow-Origin', '*')
            ->setHeader(
                'Access-Control-Allow-Methods',
                'GET,PUT,POST,DELETE,OPTIONS'
            )
            ->setHeader(
                'Access-Control-Allow-Headers',
                'Origin, X-Requested-With, Content-Range, ' .
                'Content-Disposition, Content-Type, Authorization'
            )
            ->setHeader('Access-Control-Allow-Credentials', 'true');
    }
    return $response;
});

f:id:Kouchannel55:20180516225621p:plain

OPTIONSとPOSTが正常系で帰ってる!

完成!!ここまで来るのに4時間はかかった。。。

所感

  • CORSのめんどくささを体感した
  • proxyServerがなぜ効かないのかは謎。誰か教えて :crying_cat_face:
  • ただ一つ不満を言うなら、以下の様にaxios側でURLをdev環境とprod環境で分ける必要があること
    • baseのaxiosクラスをつくれば一元管理できるので、なんとか納得できる

dev環境▼

    return axios.post('http://localhost:8111/session/login', json, {
        headers: { 'Content-Type': 'application/json' }
      })

prod環境▼

    return axios.post('/session/login', json, {
        headers: { 'Content-Type': 'application/json' }
      })

GitlabのCIでnpm+docker+git+awscliの実行環境構築

追記

ecs_deploy.shの実行権限がなかったので、chmod 777 ./ci/ecs_deploy.shを追記しました

背景

趣味のwebアプリ開発環境を一通り整えたが、最後のCIの部分を全く実装していかなった

GitHubを使っていたらCodePipelineを使って楽々ECSにデプロイできたのだが、今回はGitlabで作成してしまった

そしてGitHubに移す気力はない

対象読者

  • ECS/ECRでの継続デプロイを理解している人

目的

理想は、masterにプッシュしたら、自動でgitlab runnerが走って自動でecrを更新しecsのサービスも更新してほしい(贅沢)

今回の僕のweb環境は以下のようになっています。

RESTfulなので、フロントエンドとAPIサイドに分かれています。

├── Dockerfile
├── README.md
├── docker-compose.yml
├── front     <=フロントエンド側の開発ディレクトリ
├── nginx.conf
└── phalcon     <= webアプリケーションのディレクトリ
    ├── (略)
    ├── public   <= nginxのrootディレクトリ

これを踏まえて、CIサーバーでやらせたいことを掘り下げると、、、

  • dockerコマンドで、ビルドしたイメージをECRにプッシュしたい
  • awsコマンドで、ECSの操作を行いたい
  • frontディレクトリ内で、webpackのビルドを行い、その出力ファイル(index.htmlとその他staticファイル)をphalcon(webアプリケーション)ディレクトリ内のpublicディレクトリ内にブッコミたい

これを実現するには、以下の課題をクリアする必要がある

  • npmのインストール
  • dockerのインストール
  • awsコマンドのインストール

調査

gitlabCIについて一つ勘違いしていた。てっきりprivateなサーバーでのみ動かすことしかできないと思っていたが、どうやらGitlabが用意しているCIサーバーも制限があるが存在しているようである。自前サーバーで動かすのをSpecific Runnersというのに対しShared Runnersというらしい。1ヶ月1グループあたり2000回まで無料のようである。太っ腹。

GitLab.com settings | GitLab

さらにさらに、どうやらimageを指定して、その中で動作するらしい。Dockerは新世界の神ではないか。 細かいCIの設定は.gitlab-ci.ymlに以下のようなフォーマットでかく。以下は最小構成である、

image: python:2.7

stages:
  - deploy

deploy_job:
  stage: deploy
  script:
    - XXXXXXXXXXXX

awsコマンド

先ほどの、npm,docker,awsの中でインストールがめんどくさそうなのはawsという直感を元にpython:2.7イメージを使用。デフォルトでawsコマンドが使えるので。python:2.7はDebianのjessieがベースになっているとのこと。

ここで、みんな大好きAWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY環境変数に登録する。.gitlab-ci.ymlに書くと、gitに追跡されてしまうので、ここはgitlabの設定であらかじめ登録しておくこと

f:id:Kouchannel55:20180515005519p:plain

ここまでで.gitlab-ci.ymlは以下のようになった

image: python:2.7

stages:
  - deploy

deploy_job:
  stage: deploy
  script:
    - pip install --upgrade pip
    - pip install --upgrade awscli

npm

フロントエンドには、vueを使っているが、webpackでビルドする必要がある。

無論python:2.7に入っていないので自力で導入。

以下を、install_npm.shとして作成

curl -sL https://deb.nodesource.com/setup_10.x |  bash -
apt-get install -y nodejs

これを踏まえて、gitlab-ci.ymlは以下のようになる

image: python:2.7

stages:
  - deploy

deploy_job:
  stage: deploy
  script:
    - pip install --upgrade pip
    - pip install --upgrade awscli
    - sh ./ci/install_npm.sh
    - cd ./front
    - npm install
    - npm run build

docker

ECSで動かすタスクで動くDockerイメージを更新するには、ECSというdockerリポジトリに、新たにプッシュする必要がある。

ぐぐるとよく出てくる方法はめっちゃ長かった。

  1. 古いdockerをアンインストール
  2. 周辺パッケージをインストール
  3. GPG鍵の取得
  4. .......以降省略

ダメ元でdocker公式読んだら、もっと簡単な方法があった。やはり公式が正義

docs.docker.com

curl -fsSL get.docker.com -o get-docker.sh
sh get-docker.sh

これだけでいいとのこと。あっさり。

しかし、これだけでは、プロセスが停止したままなので、service docker startを加えて以下が最終形。install_docker.shとしてファイル作成

curl -fsSL get.docker.com -o get-docker.sh
sh get-docker.sh
service docker start

これでdockerがすぐに動かせる状況になった

ECRにプッシュ

プッシュするタグ名は、複数箇所で使用するので、以下のように環境変数として定義。これはシークレットレベルは低いので、.gitlab-ci.ymlに定義

deploy_job:
  stage: deploy
  variables:
    TAG_NAME: XXXXXXXXXXXX
  script:
(略)
    - aws_var=`aws ecr get-login --no-include-email --region ap-northeast-1 `
    - $aws_var
    - docker build -t kyujin .
    - docker tag kyujin:latest $TAG_NAME
    - docker push $TAG_NAME

ECSを更新

最後にして最難関。愚直にやろうと思うと以下の手順でやる必要がある

  1. タスク定義を先ほどプッシュしたイメージを含んで更新(リビジョン)
  2. クラスター内のサービスを更新

非常にめんどくさい。CodePipelineならGUIでサクッとできたのでこちらは地獄のように感じる

以下のスクリプトを使わせてもらった。

github.com

ecs_deploy.shとしてファイルを作成し、以下で実行。

sh ./ci/ecs_deploy.sh -r $REGION -c $CLUSTER -n $SERVICE -i $TAG_NAME -t 1800

これだけで12も自動でやってくれる。幸せ。

しかし、shでやってると怒られた

./ci/ecs_deploy.sh: Syntax error: "(" unexpected

どうやら、Debianでshをすると、dashが起動する影響らしい。微修正

./ci/ecs_deploy.sh -r $REGION -c $CLUSTER -n $SERVICE -i $TAG_NAME -t 1800

jsonのパーサーのjqも必要なのでサクッとインストール

apt-get install jq

結論

最終形は以下のようになった。めでたしめでたし。

image: python:2.7

stages:
  - deploy

deploy_job:
  stage: deploy
  variables:
    TAG_NAME: XXXXXXXXX
    REGION: YYYYYYYY
    CLUSTER: ZZZZZZZZZZZZZZZZ
    SERVICE: WWWWWWWWWWWWW
  script:
    - pip install --upgrade pip
    - pip install --upgrade awscli
    - sh ./ci/install_docker.sh
    - sh ./ci/install_npm.sh
    - cd ./front
    - npm install
    - npm run build
    - cd ./../
    - apt-get install jq
    - aws_var=`aws ecr get-login --no-include-email --region ap-northeast-1 `
    - $aws_var
    - docker build -t kyujin .
    - docker tag kyujin:latest $TAG_NAME
    - docker push $TAG_NAME
    - chmod 777 ./ci/ecs_deploy.sh
    - ./ci/ecs_deploy.sh -r $REGION -c $CLUSTER -n $SERVICE -i $TAG_NAME -t 1800

所管

  • 日曜の午前3時には、masterプッシュして30秒後にはCIが走ってたが、月曜の今現在、masterプッシュして30分経っても走らずにPending状態。。。。やっぱり共有している以上、しかたないのか。。、。
  • 趣味でやるぶんにはCI用意してくれるだけで御の字
  • gitlabのドキュメント、日本語対応してくれんかなあ

Mysql8.0+Docker+SequelProではまったこと

Mysql8.0+Docker+SequelProではまったこと

mysql8.0が今年になってリリースされました。 噂では速度が2倍速くなったらしいですね。てなことでせっかくなのでDockerで使ってみました。その知見メモ

mysql:8でイメージ取得取得した

version: '2'
services:
  # mysql
  db:
    image: mysql:8
........省略

mysqlに接続できない

どうやら、デフォルトのパスワード認証方法が変わったらしい。

my.confに以下の設定を追加した

[mysqld]
default_authentication_plugin= mysql_native_password

接続できた!

SequelProが反応しない

アプリ側でmigration書いて、runさせて、、sequelProで確認っと、、

おおおお!?どのテーブルも開くとクラッシュする!!

f:id:Kouchannel55:20180513221439p:plain

以下、吐かれたエラーログ▼

NSInvalidArgumentException

-[_NSInlineData isEqualToString:]: unrecognized selector sent to instance 0x60000062d180

(
    0   CoreFoundation                      0x00007fffbd2932cb __exceptionPreprocess + 171
    1   libobjc.A.dylib                     0x00007fffd20ac48d objc_exception_throw + 48
    2   CoreFoundation                      0x00007fffbd314f04 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
    3   CoreFoundation                      0x00007fffbd205755 ___forwarding___ + 1061
    4   CoreFoundation                      0x00007fffbd2052a8 _CF_forwarding_prep_0 + 120
    5   Sequel Pro                          0x00000001000587cd -[SPTablesList updateTables:] + 1338
    6   Sequel Pro                          0x00000001000469c5 -[SPDatabaseDocument _selectDatabaseAndItem:] + 454
    7   Foundation                          0x00007fffbec2cb3d __NSThread__start__ + 1243
    8   libsystem_pthread.dylib             0x00007fffd2baa93b _pthread_body + 180
    9   libsystem_pthread.dylib             0x00007fffd2baa887 _pthread_body + 0
    10  libsystem_pthread.dylib             0x00007fffd2baa08d thread_start + 13
)

どうやらDBがクラッシュしたわけではないようで、書き込み読み込み等はアプリ側からは正常にできているみたい。。

んー、migration前はsequelProでテーブル作成・データ追加もうまくいってたはずなのに。。。。

実験でDataGripを使ってみると問題なくテーブルが見れたし編集もできた。あっけない解決

www.jetbrains.com

推測ですが、sequelPro側で対応できていないためかな??

さすがのJetBrainさん一生ついていきます 🙇

結論

  • パスワード認証方法を忘れずに変更しましょう
  • DataGrip最強

関西Node学園 1限目

概要

nodejs.connpass.com

内容個人的まとめ&所感

春からはじめる新しいNode.js - Node.js v10

  • ecmascriptとは?ってレベルから詳しく掘り下げて話してくれていた。
  • 時期バージョンのリリースタイミングや、策定に関するあれこれなど技術的ではないが興味を引かれた
  • stageは0から4まである
  • 新規プロポーサルを出すには.mdに追加するだけでよい
  • javascriptを実行するエンジンでメジャーなのがGoogleが作ったOSSv8engine。他のエンジンと違い、中間コードを挟まないので早い。
  • 最新版は6.2.XX

nodejs

  • stableは偶数バージョン(4,6,8,10..)
  • 最新版は奇数バージョン(5,7,9..)
  • 現在のstableは8
  • 今月末にNodejs10がリリースされる

https://raw.githubusercontent.com/nodejs/Release/master/schedule.pngこちらより拝借

nodejsの新機能

こちらから確認できる▼ Node.js ES2015/ES6, ES2016 and ES2017 support

Typescript + Express

環境構築に関してはexpressのeの字も知らなかったが、だいぶ勉強になった

vue-cliとかtypescript-starterとかでjs周り簡単に複雑な環境が簡単に手に入るのでそれに甘んじていたがその反面一つ一つの役割・機能がわからなくなりがちなのでこのレベルでやってくれるのはわかりやすかった。

やっていたことは

  1. express-generatorを使ってscaffold作成
  2. typescriptとかlintとかtsconfigとかの設定
  3. jsの拡張子をtsに変更
  4. jsの中身をtsに簡単に変更
  5. tsconfigのoutputDirをオリジナルDirに修正
  6. tscでts=>jsにコンパイル
  7. サーバー起動

家に帰って試したが、非常に簡単だった。

手順はこちら

疑問

backendをexpressで、frontendもjsでがっつりやっている場合、npmとかそういうライブラリ管理って分離したほうがいいのかなあ 🤔

babel入門

babelは全く無知だったので理解が進まなかった。反省

Javascirptユニットテスト入門

ユニットテストのやり方云々ではなく、それにいたるまでのステップアップの方法を解説していた

テストをやる基準

  • コスパに優れているか
  • UIと分離させれるか
  • モジュールに切り出せるか

mocha

mochaというNodejsのテストツールがあるらしい。 https://mochajs.org/

テストは誰がする?

質疑のときに飛んできた質問。 コードを書いた人がテストコードを書くべきと明言していたが、コードを書いた人は必然的に視野が狭くなる(コードを書いているから、それに引っ張られたテストコードになるような)ので他人がテストコードを書くことも有用だと思った。

Flowtype入門

全く聞いたことなかった。Facebookが作ったjsの型定義ができるコンパイラ?のようだ

jsの変数・関数にアノテーションをつけてFlowTypeにチェックさせるとエラーが出て来るという仕組みみたい。 エラーがなかった場合はアノテーションが削除されて出力される??ちょっと未確認だ。

はじめは無理に細かく型付けをしない 感動した。既存コードをリファクタリングする事例ではTSだとJS/TSが混在すると管理が大変そうだが、FlowTypeはわかる型から書いていけばいいのでシンプルで有効に使えそう

弱点はTSの方定義ライブラリ(@types)と比べるとFlowTypeの型定義ライブラリ(flow-typed)が貧弱であるということですね(約1/6だったはず) また、FaceBookなので、React以外だと微妙なことがちょいちょいあるみたい。

typescriptとflowtypeの差が書いてあった面白い記事を紹介していた。読んだけどなんとなくわかったような気持ちになった qiita.com

僕は雰囲気でシェルを使っている

背景

  • ラズパイやりたい
  • よくエラーが出る。出た場合に、スマートに原因確認したい
    • 人口少ないのでググっても解決されないこと多々ある
  • シェル読めばいいのでは?
  • 雰囲気でしか読めない
  • シェルの知識を一回洗いたい
  • ということで改めて雰囲気でやってたところお勉強

注意

筆者はターミナルでzsh使ってますが、クウォーティングがbashと異なるせいか、ターミナルで思ったように動かないことが多発しました。そんなときはきちんとファイルにして、shebang/bin/bashを設定して遊んで見ましょう。zshなんか個人の問題だしシェルスクリプトzshで書くのはやめようね。色々困るので

そもそも入力されたコマンドってどうなってるの?

雰囲気というか、全く勉強したことなかった笑 どうやらLinuxでは以下の順番でシステムコールというものが実行されています、

  • fork = プロセスをコピーして子プロセスを作成する
  • exec = コマンドを実行する
  • wait = 親プロセスが子プロセスを監視する
  • exit = 実行結果を返す

  • forkによって現在のコマンドラインのプロセス(以降,X)から子プロセス(以降,x)をコピーして作成

  • Xはwaitによって、子プロセスを監視
  • x上でexecによってコマンドが実行され、exitで結果が返される
  • waitが感知してコマンドを終了する

shebang

シェルスクリプト先頭にあるこういう表現。

#!/bin/bash
#!/bin/sh

ただのコメントやん。と思ってしまいますが、実はコメントではないので、なんでも書いていいわけではないようです。

実は、先ほど書いたexecシステムコールに関係します execシステムコールは、バイナリ形式のファイルを読み込む仕組みです しかし、ファイルの先頭が#!となっている場合のみ、#!から改行までをコマンド、それ以降は、そのコマンドにファイルが引数として渡されているとして扱われます。

なので、例えば

#!/usr/bin/env php
<?php  
    echo "Hello,world";  
?>

これを、こうすると動くんです

$ ./sample_php.sh

便利やんと思う反面、shebangを扱う上で弱点があるようです。 ファイルに必ず実行権限がないと実行できないようです

$ chmod -x sample.sh
$ ./sample.sh
zsh: permission denied: ./sample.sh
$ chmod +x sample.sh
$ ./sample.sh
Hello,world

この問題は、直接shebangコマンドラインで指定すると解決されます。

$ chmod -x sample.sh
$ /bin/bash sample.sh
Hello,world

環境構築しているときに、たまにこういう場面きますよね、こういう理由だったみたいです。

sourceコマンド

よく、.bashrcを修正した後、その修正を反映させるにはsource ~/.bashrcをしろっていうのをみますね、 これに使われるsourceコマンドですが実はスクリプトファイルを1行ずつ読んでいっているだけなんです。これを聞くと普通のshと同じようです。唯一違う点は、sourceは子プロセスで実行しないんです。なので変数などの設定が現在のプロセスに全て引き継がれます。だから追加したばかりのaliasが使えたりするんですね、

ブレース展開

パス名展開(*.shとかsample.[ch]とかのパス名を展開する機能)はみなさん知っていると思いますが、ブレース展開というものもあります

めっちゃ便利そうです。

mkdir {src,test} // srcディレクトリ,testディレクトリを作成
mkdir dir_{1..100}  //ディレクトリを`dir_1`から`dir_100`まで作成
mkdir sample-{a..d} // sample-aからsample-dまで作成
mkdir sample-{4..11..2} //4から2ずつ増えて11まで展開する。従って `4,6,8,10`として展開され作成

やばい、めっちゃ便利。

パラメータ展開

${変数:-値}

たまにみますよねこの表現、変数に値が設定されていない場合はを使うみたいです。

コマンドの引数が設定されていない場合のデフォルト値を設定する際によく使われてます

#!/bin/bash
echo ${1:-デフォルトパラメータだよ}
$ ./myecho.sh
デフォルトパラメータだよ
$ ./myecho.sh 引数1
引数1

これと似た表現で、以下のようなものもありますね、

${変数:=値}

これはどうやら変数が空文字または未定義だった場合にを代入して展開します

さらに似たような表現で

${変数:?値}

もちょくちょく見ます。

これは、変数が未定義だった場合は標準エラーにを出力します。

$ echo ${file:?Please set file}
zsh: file: Please set file

使いどころありすぎてイイ!

if文

以下のように[]があったりなかったりと3パターン存在しているのがすごい気になってた。

if コマンド; then
    :
fi

if [条件っぽいもの]; then
    :
fi

if [[条件っぽいもの]]; then
    :
fi

ちなみに、:はヌルコマンドといって、文字どおり何も実行しない。しかしこれを書かないとなんか処理しろやゴルァと言われるので書いている

まず一つ目if コマンド; then ...

コマンドの実行結果は,正常終了なら0,それ以外なら0以外を返す。 if {コマンド}; then ....の場合は、そのコマンドの実行結果が0である場合に条件に合致するということだ

if cd ~/sample; then
    echo 'Dir exists.'
else
    echo 'Not exists.' >&2
fi

この場合、cd ~/sampleで、sampleディレクトリが存在している場合は条件内に入る。

echo 'Not exists.' >&2は、標準出力を標準エラー出力ヘリダイレクトしている。エラーを手っ取り早く表示するにはこの方法がいいらしい

次にif [条件っぽいもの]; then ...

独特の記法がされている。これはtestコマンドのエイリアスらしい。

testコマンドとは、引数に独自の演算子を渡すと、文字列のチェックやファイルの権限確認などを行なってくれる

演算子 0(true)を返す場合
-a x xが存在する場合
-e x xが存在する場合
-d x xがディレクトリの場合
-f x xがファイルの場合
-L x xがシンボリックリンクの場合
-h x xがシンボリックリンクの場合
-z x xが空文字列の場合
-n x xが空文字列ではない場合
x === y xとyが等しい場合(数値でも文字列でも可能)
x == y xとyが等しい場合(数値でも文字列でも可能)
x != y xとyが等しくない場合(数値でも文字列でも可能)
x < y xがyよりも辞書順で前である場合(数値でも文字列でも可能)
x > y xがyよりも辞書順で後ろである場合(数値でも文字列でも可能)
$ test 0 == 1
$ echo $?
1    // falseとして1が返る

$ [ 0 == 1 ]
$ echo $?
0     // trueとして0が返る

$ [ 'hoge' \< 'fuga' ]  // クウォーティング必須。 '<'でも可
$ echo $?
1     // falseとして1が返る

ちなみに、$?は直前のコマンドの実行結果を取得するコマンド。

また、これらを複雑に条件づけるために、以下の演算子も存在する

演算子 処理
式a -a 式b aかつb。よくある(a&b)
式a -o 式b aまたはb。よくある(a|b)

最後にif [[条件っぽいもの]]; then ...

これは、testコマンドをさらに拡張したものみたいです

簡単にいうと、先ほどの-aや大なり小なりのクウォーティングが不要になるんです。

testコマンド時▼

$ [ 'hoge' \< 'fuga' -a  1 == 0]
$ echo $?
1

[[]]時▼

$ [ 'hoge' < 'fuga' && 1 == 0]
$ echo $?
1

後者の方がわかりやすいですねえ。こんな書き方ができるのは、[[]]がコマンドではないためらしいです。

しかも、[[]]の場合には、&&の前者で1(false)だった場合は後者の処理は行わないので使いやすいですね。なので先ほどの処理では実は1 == 0の処理は行われていないんです!

まとめ

方針決定

所感

  • 匿名でやってたアカウントのブログを捨ててこちらで書くことにした
  • アウトプットしないマンだったのを反省
  • 書くことで整理もできるかも
  • 自己紹介とかキモいので省略
    • 前のブログではつらつら書いてた、キモい
    • 気になるんなら本人に聞け

方針

  • 目標は読者数10人 (某先輩のブログの水準)
  • PVはわりとどうでもいい
  • 強いて言うなら月間100人でいいかなあ
  • ジャンルはWeb系
    • いろんな技術に触りがち
    • 触った備忘録を貯めていく予定
    • 深く詰めるというより新しいことをガンガンやる予定
  • 頑張ったらMediumでやりたいなあ(白目
  • 仕事のことは忘れて書く
  • 1記事/1ヶ月のペース
  • Qiitaとこれを両立する予定

デザイン

  • アフィっぽいのは嫌
  • テックキャンプ上がりのクソ雑魚のブログみたいなのも嫌
    • はてブロのテーマのcssいじれねえのかよ
    • 結局デフォルトのでいいや
  • Markdownで書ける。有能