ベスパリブ

プログラミングを主とした日記・備忘録です。ベスパ持ってないです。

Raspberry Pi のSDカードを縮小する方法

32GBから16GBのSDカードに縮小したかった。
たくさん記事はあり色々試しましたが、結局以下の方法でうまくいきました。

raspberry piのイメージファイルを小さな容量のメモリーカードにコピーする方法

EaseUS ToDo Backup Freeを使う方法も試しましたが、不良セクタの書き込み失敗エラーが解消できませんでした。

・2019/02/23追記
ラズパイでgpartedを実行、"e2fsck cannot continue aborting is mounted"エラー - ベスパリブ

Gitコマンド備忘録

この記事は都度更新します。
[最終更新]2019/12/09

Gitの基本知識

fetch, pull, pushの挙動

【Git】リモートからの取得とリモートへの反映で行っていること(fetch,pull,push) - Qiita

fetch, mergeの挙動とかを忘れたらコーヒー飲みながら上の記事を読む。

blame

ファイルの各行がいつ変更されたかを知ることができる

git blame [ファイル名]

・例

$ git blame hoge.py
c885c163 (committerName 2017-11-16 11:14:59 +0900  1) # -*- coding: utf-8 -*-
c885c163 (committerName  2017-11-16 11:14:59 +0900  2) def task1():
c885c163 (committerName 2017-11-16 11:14:59 +0900  3)     print("task1")
d9340aa0 (committerName 2017-11-17 12:33:44 +0900  4) def task2():
d9340aa0 (committerName 2017-11-17 12:33:44 +0900  5)     print("task2")

各行のコミット番号、コミッタ、日付時刻、行番号が確認できる。

コミット番号がどういうコミット内容だったかを知るには、showコマンドを使う。

branch

ブランチを確認する
*が付いているものが現在のブランチ

ブランチの確認

git branch
* master
  branchA
  branchB

clone

git clone [リポジトリのURL]

で、リポジトリのフォルダをダウンロードできる。
cloneとダウンロードって何が違うの?というと、cloneはそのプロジェクトのすべてのファイルのすべての歴史が含まれている。つまりコミット履歴などが含まれている。リポジトリからダウンロードするとそれらの歴史が含まれていないので、大抵はcloneする。

conflicts

競合ファイル一覧を表示する。本来の git コマンドには存在しない。

以下のコマンドを実行し、設定ファイルにconflictsコマンドのエイリアスを作成する。

$ git config --global alias.conflicts '!git ls-files -u | cut -f 2 | sort -u'

するとconflictsコマンドが使えるようになるので、実行したら競合ファイルの一覧が表示される。

$ git conflicts
src/mods/a.py
src/mods/b.py
web/main.py

checkout

checkoutでできることは大きく2つ。
・ブランチの切り替え
・ファイルを特定の状態に戻す
上2つがなんで同じコマンドなのかは不明。Gitのむかつきどころ。

ブランチの切り替え系

ブランチを切り替える
git checkout [ブランチ名]

ブランチ名の確認の仕方は、branchコマンドの項目を参照。

・例1

git checkout branchA

ブランチをbranchAに切り替える

・例2

git checkout -f branchA

ブランチを強制的にbranchAに切り替える
作業ツリーとインデックスの変更は破棄される

ブランチ作成と切り替えを同時に行う
git checkout -b branchC

あんまり使ったことない

ファイルを特定の状態に戻す系

ファイルを特定のcommitの状態に戻す

https://qiita.com/ritukiii/items/5bc8f74dbf4dc5d1384c

git checkout [コミット番号] [ファイルパス]

この操作は非常に使える。
例えばバグが発生したとき、どこのコミットからバグが発生したか調べるときに、ひとつひとつコミット番号を戻しながら使ったりする。

・コミット番号の調べ方
GitHubのページでファイルを開けばコミット番号が書いてある。
あるいはlogコマンドの項目を参照。

・例1

git checkout [コミット番号] hoge.txt

hoge.txtが[コミット番号]のときの状態に戻る

・例2

git checkout [コミット番号] .

全てのファイルが[コミット番号]のときの状態に戻る

・ 例3

git checkout HEAD -- hoge.txt

hoge.txtの現在の変更を破棄して、最新状態(HEAD)に戻す。

--の意味は「--以降の文字列をオプションとして扱わない」で、これにより-が混ざったファイル名を扱えるようになる。

・例4

git checkout HEAD^ -- hoge.txt

hoge.txtを1つ前のコミット(HEAD^)の状態に戻す。

log

コミット履歴を確認する

> git log
commit c4adc034f65a53d85b3fa8270e0e8239e4d45518
...

commit 8601edcfa95c72bf10c3b4479b59f1ffe488c5a0
...

ファイルのコミット履歴を確認する

$ git log --oneline hoge.txt
13aed9b (HEAD -> master, origin/master) updated
c4adc03 fix: 
8601edc fix: coding: utf-8

checkoutでファイルを特定のコミット状態に戻すときに便利

全体のコミット履歴を確認する

$ git log --oneline
13aed9b (HEAD -> master, origin/master) updated
c4adc03 fix:
ce5dd9d Update

reset

ステージを取り消す(addしたものを取り消す)

$ git reset HEAD [ファイル名]
$ git reset HEAD hoge.py

特定コミット状態に戻す

動作確認のためなど、過去のコミット状態を再現したいときは以下のようにする

git log  # コミットのハッシュ値を確認
git reset --hard 昔のコミットのハッシュ値

特定コミット状態から最新の状態に戻るには、

git reset --hard ORIG_HEAD

show

・ファイルの内容を見る
・コミットの差分を見る
上の2つの操作が同時にできる

そのコミット番号のときの変更を表示する

git show [コミット番号]

バグが発生したコミットを指定することで、ソースのどこが原因か追跡するのに便利。

$ git show c885c163
commit c885c1630f1a9f68ddf20c2583691eb999999999
Author: committerName <committer@mail.com>
Date:   Thu Nov 16 11:14:59 2017 +0900

    2017/11/16
    * Fix: センサ変更によるInput Errorを修正.

diff --git a/project/src/mymod/hoge/hoge.py b/project/src/mymod/hoge/hoge.py
index eeea885..71daae6 100755
--- a/src/updater.py
+++ b/src/updater.py
@@ -8,7 +8,6 @@ from mymod.sensor.am2320    import AM2320
-import time
 import datetime

最初にコミットメッセージが表示され、次にそのコミットによる変更のdiffがファイル毎に表示される。

ここでコミットメッセージに変更理由をきちんと書いていれば何を意図して変更したのかよくわかり、バグフィックスが容易になる。(コミットメッセージはちゃんと書こう)

特定のファイルのみshowしたいとき

$ git show [コミット番号]:[ファイル名]

・例

$ git show c8a5c163:src/mymod/sensor/am2320.py
import sys
sys.path.append("/project/src")
from mymod.const                import const
from mymod.helper               import helper
import datetime

class AM2320():
    """ センサAM2320のクラス """

そのコミット番号のときのファイルの内容が表示される。

stash

作業ツリー(とインデックス)の変更を、コミットせずに一時的に保存する。
スタッシュと呼ばれる領域に一時的に保存する。

作業ツリーとインデックスを一時的に保存する

$ git stash

作業ツリーを一時的に保存する(インデックスは保存しない)

$ git stash --no-keep-index

メッセージ付きで一時的に保存する

$ git stash save "message"

$ git stash save "fix: Change version"

スタッシュに保存したものの一覧

$ git stash list
stash@{0}: On master: fix: Change version
stash@{1}: WIP on master: 37c38ad fix: バグを修正

上に行くほど直近のスタッシュ。

スタッシュに保存した内容を作業ツリーに戻す

$ git stash apply <stash>
()
$ git stash apply stash@{0} 

スタッシュを指定しない場合、直近のスタッシュに戻る
技術評論社の ポケットリファレンス には「スタッシュを指定しない場合、直近のスタッシュが削除される」と書いてあるが、実際にはそのような動作はしなかった)

スタッシュに保存した内容を作業ツリーに戻した後、そのスタッシュを削除する

$ git stash pop <stash>
()
$ git stash pop stash@{0} 

使い方はapplyと同様。どちらか覚えれば良い。

スタッシュの削除

$ git stash drop <stash>
()
$ git stash drop stash@{0}

スタッシュを指定しない場合、直近のものが削除される

スタッシュの全削除

$ git stash clear

参考

  • Git ポケットリファレンス[改訂新版]

ジョギング始めました

ソフトウェアエンジニアの人たちって、筋トレとかジョギングとかロードバイクとかやってますよね。なんか体を鍛えるとエンジニア力も上がるみたいな定説ありますよね。

やる気出すだめにスポーツウェアを買いました。
スポーツウェアがやる気スイッチになって、今の所三日坊主は回避できました。
ジョギングの効果が出るのは3ヶ月後らしいので、3ヶ月後にまた何か書きます。
書かなかった場合は察してください。

ポインタの値渡しを理解する(あるいはポインタのポインタの使い所)

以下のようなコードがあったわけです。

#include <stdio.h>
char data[16] = "abcdefg";

int main(void){
    // Your code here!
    char* pData = NULL;
    
    pData = data;

    printf("%s\n", pData); // abcdefg
}

以下のように関数にしたかったわけです。

#include <stdio.h>
char data[16] = "abcdefg";

void func(char* pData){
    pData = data;
}

int main(void){
    // Your code here!
    char* pData = NULL;
    
    func(pData);

    printf("%s\n", pData);  // Runtime error(Exit status:139(Invalid memory reference))
}

エラーが出るわけです。
それぞれのアドレスを表示してみます。

#include <stdio.h>
char data[16] = "abcdefg";

void func(char *pData){
    pData = data;
    printf("func-in: %d\n", pData);  // func-in: 6295616
}

int main(void){
    // Your code here!
    char* pData = NULL;
    
    func(pData);
    printf("func-out: %d\n", pData);  // func-out: 0
    
    pData = data;
    printf("%d\n", pData);  // 6295616
    
}

これなんでだろ~ってずっと悩んでいたんですが、小一時間悩んで、このfunc()に渡しているpDataはいわゆる「ポインタの値渡し」というやつでは?と思い至る。
その場合、ポインタのポインタを渡せば解決できるかもと思い修正。func2()を作成します。

#include <stdio.h>
char data[16] = "abcdefg";

void func(char *pData){
    pData = data;
    printf("func-in: %d\n", pData);  // func-in: 6295616
}

void func2(char **pData){
    *pData = data;
    printf("func2-in: %d\n", *pData);  // func2-in: 6295616
}

int main(void){
    // Your code here!
    char* pData = NULL;
    
    func(pData);
    printf("func-out: %d\n", pData);  // func-out: 0
    
    func2(&pData);
    printf("func2-out: %d\n", pData);  // func2-out: 6295616
    printf("%s\n", pData);  // abcdefg
    
    pData = data;
    printf("%d\n", pData);  // 6295616
    
}

オッ、意図したとおりに動作しました。
C言語に参照なし」とは言いますが、こうして見ると、「&」が参照渡しの記号に見えてしょうがない。

WM_IME_CHAR、WM_IME_KEYDOWN、WM_IME_KEYUPの挙動を調べた

WinAPIの話です。

VK_DELETE メッセージを送信したら、"."(ドット)が出力される問題

仮想キーVK_DELETEをSendMessageしたら、なぜか"." (ドット) が出力されてしまう問題に悩んでいました。

原因は単純で、SendMessage(hWnd, WM_IME_CHAR, VK_DELETE, 0)としていたせいでした。

WM_IME_CHARとは何か。
http://www.geocities.jp/katayama_hirofumi_mz/imehackerz/ja/WM_IME_CHAR.html
によると、第三引数のwParamはUnicode文字の値と書いてあります。

VK_DELETEは0x2eです。
WikipediaUnicodeを調べます。
https://ja.wikipedia.org/wiki/Unicode%E4%B8%80%E8%A6%A7_0000-0FFF
なるほど。ドットです。

WM_IME_CHARとは?

WM_IME_CHARの仕様は、

IMEが変換結果の文字を取得するときに、アプリに送られます。ウィンドウは、そのウィンドウプロシージャーを通じてこのメッセージを受け取ります。

らしいですが、ちょっとよくわかりません。

原文のMSDNも読みましたが、やはりちょっとよくわかりません。

よくわかっていないなりの理解なのですが、Unicodeを送信したいときはWM_IME_CHARを使えということでしょう。実際、そういう動作をします。

Call SendMessage(hWnd, WM_IME_CHAR, &H21, 0)  ' !
Call SendMessage(hWnd, WM_IME_CHAR, &H2E, 0)  ' .
Call SendMessage(hWnd, WM_IME_CHAR, &H7E, 0)  ' ~ 

結局、DELETEを送信するには?

やりたいことは「DELETEキーを押したい」と同じです。
ここにきて、"仮想キー"の考えが腑に落ちた気がします。仮想キーは文字通り「仮想的なキーボード」なので、「キーを押して」、「キーを離す」というメッセージを送信するということになります。

Call SendMessage(hWnd, WM_IME_KEYDOWN, VK_DELETE , 0)
Call SendMessage(hWnd, WM_IME_KEYUP, VK_DELETE, 0)

何かがおかしい

Call SendMessage(hWnd_Edit, WM_IME_CHAR , Asc("a"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR , Asc("b"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR , Asc("c"), 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_BACK, 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYUP, VK_BACK, 0)

とすると、"b"と"c"が消えました( VK_BACK はBackSpace)。
WM_IME_KEYDOWN と WM_IME_KEYUPでワンセットかと思いきや、 WM_IME_KEYDOWN と WM_IME_KEYUPで二回VK_BACKを押したことになっているようです(私の環境だけ?)。

ちなみに、「押しっぱなし」がどうなるのかも確かめてみました。

Call SendMessage(hWnd_Edit, WM_IME_CHAR , Asc("a"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR , Asc("b"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR , Asc("c"), 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_BACK, 0)

"c"が消えただけで、"a"と"b"は残りました。どうやら「押しっぱなし」のようなことにはならないようです。

さらに、VK_LEFT(左キー)の挙動を調べました。

Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("a"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("b"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("c"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("d"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("e"), 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_LEFT, 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYUP, VK_LEFT, 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_LEFT, 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYUP, VK_LEFT, 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("X"), 0)
' "abcXde"
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("a"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("b"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("c"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("d"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("e"), 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_LEFT, 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_LEFT, 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("X"), 0)
' "abcXde"
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("a"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("b"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("c"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("d"), 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("e"), 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYUP, VK_LEFT, 0)
Call SendMessage(hWnd_Edit, WM_IME_KEYUP, VK_LEFT, 0)
Call SendMessage(hWnd_Edit, WM_IME_CHAR, Asc("X"), 0)
' "abcdeX"

この辺、挙動を確認しながらプログラム組まないと、よくわからないバグになりそうです。環境による気もしてきました……。 どこかに良い書籍や情報はないでしょうか。

使い分けとか

キーボードでできることは仮想キーにやらせよう(オライリー・ジャパン風)
逆に、 「ʨ」とかの入力はUnicodeでしかできないと思います(入力できるのか知らないけど。私の環境だと?に変換された)

補足

MSDNを辿っていると、知識として入れておいたほうが良さそうなページを見つけました。
キーボード入力

Editコントロールが入力可能になるまで待つ

WinAPIの話です。
Editコントロールのハンドルを取得できても、Editコントロールに入力可能というわけではないようです。
このせいで、ハンドル取得した直後に文字列をSendMessageしても、テキストボックスに文字が入っていない場合があります。

しょうがないので、Editコントロールが入力可能になるまで待つ関数を作ります。
方法としては、
1. Editコントロールに適当な文字をSendMessageし、文字数を取得する。
2. 文字数が1文字以上なら、入力可能なので、文字を消して終了
3. 文字数が0ならば、入力できていないので、1.に戻る
です。単純ですね。

Public Declare Function SendMessage4GetText Lib "user32" Alias "SendMessageW" (ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal iParam As System.Text.StringBuilder) As Integer
Public Const VK_BACK As Integer = &H8  ' BackSpace

Public Sub Wait4Edit(ByVal hWnd As IntPtr)
    Dim buf As New System.Text.StringBuilder()
    While 1
        buf.Length = 2048
        System.Windows.Forms.Application.DoEvents()
        Call SendMessage(hWnd, WM_IME_CHAR, Asc("a"), 0)  ' 適当な文字を送信する
        If SendMessage4GetText (hWnd, WM_GETTEXT, buf.Length, buf) >= 1 Then  ' 入力できていたら、文字数が1文字以上になる
            Exit While
        End If
        System.Threading.Thread.Sleep(100)
    End While
    ' 適当な文字を送っているので、コントロールに入力されている文字を全部消す
    While 1
        buf.Length = 2048
        System.Windows.Forms.Application.DoEvents()
        Call SendMessage(hWnd_Edit, WM_IME_KEYDOWN, VK_BACK, 0)
        Call SendMessage(hWnd_Edit, WM_IME_KEYUP, VK_BACK, 0)
        If SendMessage4GetText (hWnd, WM_GETTEXT, buf.Length, buf) = 0 Then
            Return
        End If
        System.Threading.Thread.Sleep(100)
    End While
End Sub

引数hWndにはEditコントロールのハンドルを与えます。
与えるハンドル間違えたりすると無限ループから返ってこないことに注意。

なぜWhile文を2つに分けているかというと、最初一つにまとめていたのですが、なぜか入力ボックスに文字が残っていたりした("a"が2回送られる? or VK_BACKが反応しない?)ので、「文字を消す」処理を確実に行わせたかったため二つに分けています。この辺の仕様をまだよくわかっていません。でもまあ、動くしいいか(適当)

追記[20180525]

IsWindowVisible()とIsWindowEnabled()という便利な関数を見つけました。これを使えば良さそうです。

Public Declare Function IsWindowEnabled Lib "user32" Alias "IsWindowEnabled" (ByVal hWnd As IntPtr) As Boolean
Public Declare Function IsWindowVisible Lib "user32" Alias "IsWindowVisible" (ByVal hWnd As IntPtr) As Boolean

Public Sub Wait4Edit(ByVal hWnd As IntPtr)
    ' エディットボックスが入力可能になるまで待つ
    While True
        If (IsWindowEnabled(hWnd) = True) And (IsWindowVisible(hWnd) = True) Then
            Exit While
        End If
        System.Threading.Thread.Sleep(10)
    End While
End Sub