コピペコードで快適生活

明日使えるソースを自分のために

第37回シェル芸勉強会メモ

https://b.ueda.tech/?post=20180901_shellgei_37 にて学んだことメモ。

Q1

FizzBuzzのアニメーション

seq 100 | 
gsed '0~3s/.*/Fizz/;0~5s/.*/Buzz/;0~15s/.*/FizzBuzz/' | # 0~3s みたいな書き方で3行ごとに置換を行う
gawk '{printf("%08s\n", $1)}' |  # 8桁で右揃えにする
while read line; do clear; echo $line; sleep 0.5; done # でもこれだと右揃え表示されない...

Q2

フリーメイソン

echo '   /\' | sed ':a p;s@ /@/  @;b a' | head -n 4 | sed '$a ̄ ̄ ̄ ̄' | sed '3s;/   ;/ 👁 ;'

sedで何やっているのかわからないので、プロに教えていただいた。

# aコマンドで指定行のあとに文字列を差し込める。
# $a だと最終行に差し込む
echo "hoge" | gsed '1a\fuga'
hoge
fuga


# sの後につけた文字がデリミタになる
# @を使った例
echo '//' | gsed 's@/@pp/@'
pp//

# sedで無限ループ
# :aはラベル pは出力
# s@hoge@hogehoge@ で 置換(デリミタを@にしている)
# bはラベルジャンプの意味。なので、b a でaラベルへジャンプ
echo "hoge" | gsed ':a p;s@hoge@hogehoge@;b a' | head -n 5
hoge
hogehoge
hogehogehoge
hogehogehogehoge
hogehogehogehogehoge

# 3sとやると3行目だけ置換する
echo "hoge" | gsed ':a p;s@hoge@hogehoge@;b a' | head -n 5 | gsed "3s/hoge/fuga/g"
hoge
hogehoge
fugafugafuga
hogehogehogehoge
hogehogehogehogehoge

Q3

3桁の中に同じ数がなく、各桁を足すと15になる数字。ゼロは含まない。
これは簡単。

seq -w 999 | grep -v "0" | gawk -v FS="" '($1!=$2 && $2!=$3 && $1 + $2 + $3 == 15){print $1$2$3}'

補足

seq -w 999 # これでゼロパディングできる
gawk -v FS="" {} # これで1文字ごとに区切れる。

ActiveRecordのafter_commitが発火するとき発火しないとき

発火する

・なにも変更せずにsaveしても発火する(select文しかないtransactionでcommitがされるため)
・destroyで発火する。self.destroyed?はtrue, self.reloadでActiveRecord::RecordNotFound
・soft_destroyでも発火する。self.soft_destroyed?はtrue (kakurenbo-puti使用時)
・他のafter_commitがこけても発火する。

発火しない

・update_columnでは発火しない(トランザクションはっていないからcommitもない)
・validationエラー時はひっかからない(commitじゃなくてrollbackするので)
・DBエラー(unique制限)でrollbackしたときは発火しない(commitじゃなくてrollbackするので)

ndenvを使ってNode.jsをインストールする

ずっとnvm使っていたのだけど、プロジェクトディレクトリ移動するたびに手動でバージョン切り替えるのがさすがに辛くなってきたので、ndenvに乗り換えました。

インストール

# clone
git clone https://github.com/riywo/ndenv ~/.ndenv

# パスを通す
続きを読む

Homebrewのコマンドメモ

ちゃんとわかっていない感あったのでメモ。

## インストールされたformulaの一覧
brew list

## アンインストール
brew uninstall FORMULA_NAME

## 更新のあるformulaを見る
brew outdated

## 古いバージョンのformulaを削除
brew cleanup
brew cleanup -n # 何が消されるのか一覧を表示する。

## brew本体のアップデート
brew update

## formulaのアップデート
brew upgrade FORMULA_NAME

## formulaの情報見る
brew info FORMULA_NAME

## tap(追加)したリポジトリの一覧
brew tap

## tapする/外す
brew tap ユーザー名/リポジトリ名
brew untap ユーザー名/リポジトリ名 # このリポジトリでインストールしたformulaは先に削除しておく

第38回҈҈҉҈҈҉シ҈҉ェ҈҉ル҈҉芸҈҉勉҈҉強҈҉会メモ

https://b.ueda.tech/?post=shellgei_38 より学んだことメモ

xxd コマンド

対象を16進数でダンプする/16進数から復元する。
pオプションはpostscript形式でダンプするの意味。

# ダンプする
echo "ほげほげ" | xxd -p

# 復元する 
echo "ほげほげ" | xxd -p | xxd -r -p

# もやもやの文字コード
echo d288 | xxd -r -p
echo d289 | xxd -r -p

grep -o オプション

マッチした行ではなくマッチした箇所のみを返す

# なのでこうすれば縦に並ぶ
echo "あいうえお" | grep -o .

grep -f オプション

検索条件を保存したファイルでフィルターする

cat cond.txt
あ
い

echo "あいうえお" | grep -o . | grep -f cond.txt
あ
い

# リダイレクションと組み合わせる
cat cond.txt
あいうえお

echo "あいうえお" | grep -o . | grep -f <(grep -o . cond.txt)
あ
い

sort -kオプション

# 2項目目以降でソート
cat sample.txt | sort -k 2

# 2項目目だけでソート
cat sample.txt | sort -k 2,2

# 1項目目だけを数値としてソート
cat sample.txt | sort -k 1,1n

uniqで重複を抽出する

重複行の抽出する

# dオプションで重複のみ抽出
# fオプションで重複チェック除外フィールドを指定 (1を指定した場合は2以降で重複チェック)
cat sample.txt | sort -k 2,2 | uniq -d -f 1

trコマンド

# 置き換え(文字数は揃える必要がある)
echo 012 | gtr 012 abc

# 大文字小文字変換
echo ABC | gtr A-Z a-z

# 改行コードに変換
echo "A B C" | gtr ' ' \\n

# 指定文字以外を削除する
echo "1234567890abcdef" | gtr -dc 1234567890

nlコマンド

行数を表示

echo "A B C" | tr ' ' \\n | nl

foldコマンド

指定した幅で改行する

echo "ABCDEFGHIJK" | gfold -b5

ガウス分布な乱数生成

※0-1間の一様乱数を12回足して6引いた値の集合は、標準偏差1のガウス分布に従う。
http://www.geocities.jp/jun930/etc/varianceofrandom.html
https://bellcurve.jp/statistics/blog/17953.html

cat /dev/urandom | gtr -dc 0-9 | gfold -b10 | gsed 's/^/0./' |
gawk '{a+=$1}NR%12==0{print (a-6);a=0}'

指定範囲の文字を全部出力

echo {A..Z}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z

カンマのところで改行を入れる

cat sample.txt | gsed -e "s/,/,\\n/g"

nkf(NetworkKanjiFilter)

文字コード/改行コードの変換を行うコマンド

# -w UTF8に変換
# -Lu LFに変換
# -x 半角カタカナを半角のまま
cat myoho_shinji.txt | nkf -wLux

xargsで、引数の数を指定する

cat hogefuga.txt
hoge
fuga

cat hogefuga.txt | xargs -n 2
hoge fuga

awkの中で、コマンドの実行結果を得る

# コマンド | getline 変数 で、
# 変数にコマンドの実行結果が入る
cat data.txt | gawk '{"hostname" | getline x; print x;}'
suzukake.local
suzukake.local
suzukake.local
suzukake.local

awkでバッファを出力する

fflush();

factorコマンドで素因数分解する

gfactor 12
12: 2 2 3

Q7分解

a^2 + b^2がab + 1で割り切れる正の整数の組合せa,bを生成してください(ランダムで良いです)。また、このとき、(a^2+b^2)/(ab+1)が正の整数の二乗になっていることを確かめてください。

awkを分解して見てみれば怖くない。

cat /dev/urandom | gtr -dc 0-9 | gfold -b2 | gsed 's/^0*//' | xargs -n 2 |
gawk '
($1*$1 + $2*$2) % ($1*$2+1) == 0 { # この条件に該当した場合に{}内を実行する
  a = ($1*$1 + $2*$2)/($1*$2+1); # 変数代入
  "gfactor " a | getline x ; # factorコマンドで素因数分解した結果を変数xに格納する
  print $1,$2,x; # 出力
  fflush() # バッファ出力
}
' | # 4 64 16: 2 2 2 2 のように右辺に素因数分解した結果が出力される
gawk '{
  for(i=4;i<=NF;i++) a[$i]++; # $iでi番目の値が取れる。上記の例でいうと、a[2]に4が入る。
  printf $1" "$2" "$3;
  x="OK";
  for(k in a){
    if(a[k]%2 == 1) x="NG"; # 同じ値が2で割り切れない数繰り返された = 二乗にならない場合
  }
  print x;
}'

Q8分解

数列x_{i+1} = a * x_i * (1 - x_i)について、xの初期値を0.5としてAWKで出力して、百万回以上連続で、これまで出てきていない数字を出力してください。ただし、aは3以上、4未満の小数から選んでください。これに飽き足らない人は限界に挑戦してください。

これも何やっているか分解して考えれば怖くない。

cat /dev/urandom | gtr -dc 0-9 | gfold -b10 | gsed 's/^/0./' | # 0.xの数値をランダムに出す
gawk '{print $1*4}' | gawk '$1>=3' | # 3以上4未満に揃える
while read a ; # 入力がある限り繰り返す
do
  gawk -v a=$a ' # シェル変数をawkの変数に代入
  BEGIN{
    OFMT="%.20f"; # 小数表示20桁とする(デフォだと指数表示になるため)
    x=0.5; # 数列の初期値を設定
    while(1){
      print x; # 数列の値を出力
      x=a*x*(1-x) # 数列の次の値を計算
    }
  }
  ' |
  head -n 1000000 | # 百万行出力
  sort -u | # 重複を削除して並び替え
  wc -l | # それが何行あるか
  gawk -v a=$a '
    $1==1000000 { # 百万行あれば
      print a,$1  # 出力する
    }
  ';
done |
head -n 1

npmでパッケージ公開するまでのコマンド

# npm設定確認
# レジストリのURLも確認できる
npm config list

# レジストリを変える
npm set registry http://npm.example.com
npm set registry https://registry.npmjs.org/

# レジストリにユーザを追加
npm adduser --registry http://npm.example.com

# ログインする
# ~/.npmrc にログイン情報が保持されるので1回だけ叩けばOK
npm login

# パッケージのpublish
npm publish

# まちがえてpublishしたのを削除する
npm unpublish <package-name>@<version>

class_evalを使って、classに自由な名前でメソッドを追加

concernに書くとこんな感じか。

#
# include ::Logica::DateRangeSearchable
# date_range_name_is :validity, :valid
#
# klass.valid
#   -> validity_start_at, validity_end_atが期間内のみ取り出す
# instance.valid?
#   -> validity_start_at, validity_end_atが期間内か
#
module Logica
  module DateRangeSearchable
    extend ActiveSupport::Concern

    class_methods do
      def date_range_name_is(name, method_name = nil)
        method_name = name if method_name.blank?

        validation_name = "#{name}_start_lt_end_validation".to_sym
        validate validation_name

        class_eval <<-EOS
          def #{method_name}?(now = Time.zone.now)
            (self.#{name}_start_at.nil? || self.#{name}_start_at <= now) &&
            (self.#{name}_end_at.nil? || self.#{name}_end_at >= now )
          end

          def #{validation_name}
            if self.#{name}_start_at.nil? || self.#{name}_end_at.nil?
              return true
            end
            if self.#{name}_end_at <= self.#{name}_start_at
              message = I18n.t('errors.messages.less_than', count: self.#{name}_end_at)
              self.errors.add("#{name}_start_at".to_sym, message)
            end
          end

          def self.#{method_name}_conds(now = Time.zone.now)
            a = arel_table[:#{name}_start_at].lteq(now).or(arel_table[:#{name}_start_at].eq(nil))
            b = arel_table[:#{name}_end_at].gteq(now).or(arel_table[:#{name}_end_at].eq(nil))
            a.and(b)
          end
        EOS

        class_eval do
          scope method_name.to_sym, -> (now = Time.zone.now) {
            where(self.try("#{method_name}_conds", now))
          }
        end

      end
    end
  end
end