デフォルトのエイリアスとかぶってるコマンド

コマンドプロンプトで使っていたコマンドを PowerShell で実行しようとすると、時々エイリアスとかぶってるやつがあることに気づく。そういう時は後ろに .exe を付ければ実行できる。PowerShellは高機能だから代わりのコマンドレットもあるのだが、その場でタイプして実行する用途としてはそれほど使いやすくはなかったりする。

sc.exe

サービスを制御するためのコマンドです(TechNet)。

PowerShell では Set-Content のエイリアス sc とかぶってます。

*-Service 系のコマンドで代替できます。

# サービスの状態を確認
sc.exe query sysmain
↓
# PowerShell
Get-Service sysmain | Format-List
# 短縮形
gsv sysmain | fl
# サービスの停止
sc.exe stop spooler
↓
# PowerShell
Stop-Service spooler
# 短縮形
spsv spooler

where.exe

ファイルの検索コマンドです(TechNet)。

PowerShell では Where-Object のエイリアス where とかぶってます。

Get-Command (gcm) と Get-ChildItem (gci) で代替できます。

# パスの通っている場所からコマンドを検索
where.exe notepad
↓
# PowerShell
Get-Command notepad -all
# 短縮形
gcm notepad -all
# 特定のディレクトリ以下からファイルを検索
where.exe /r . *.txt
↓
# PowerShell
Get-ChildItem -Recurse *.txt | Select-Object FullName
# 短縮形
gci -re *.txt | select fullname


とりあえずは以上です。他は見つけ次第追加していく予定。

winreg でレジストリを読み書きするサンプルコード

標準ライブラリに入ってる winreg を使うとレジストリの読み書きができるけど、こいつは Windows API を薄く包んでいるだけなのでリファレンスを見ても使い方がさっぱりわからないという。

いろいろ調べて、おそらくこうするのが正しいと思われるサンプルコードを書いた。

目次

値の読み込み

# Python 3.6
import winreg
path = r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders'
key = winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, path)
data, regtype = winreg.QueryValueEx(key, 'Personal')
print('種類:', regtype)
print('データ:', data)
winreg.CloseKey(key)  # key.Close() と書いても同じ

OpenKey と OpenKeyEx はまったく同じです。好きな方を使ってください。両方とも内部では Windows APIRegOpenKeyEx を使っています。

QueryValue と QueryValueEx は中身が違います。Ex がつかない方は16bit時代の遺物なので使いません。

値の書き込み

import winreg
path = r'Software\7-Zip\FM'
key = winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, path, access=winreg.KEY_WRITE)
winreg.SetValueEx(key, 'PanelPath0', 0, winreg.REG_SZ, 'C:\Program Files')
winreg.CloseKey(key)

値をいじる場合は、access=winreg.KEY_WRITE をつけてください。デフォルトでは KEY_READ なので書き込めません。OpenKeyEx の代わりに後述のCreateKeyExを使ってもいいです。

Ex の付かない SetValue は16bit時代の遺物なので使いません。SetValueEx を使ってください。

コンテキスト・マネージャ

import winreg
with winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, r'Software') as key:
    pass  # key を使った処理をここで行う

PyHKEY 型は with 文と一緒に使うと自動で CloseKey してくれます。

キーの作成

import winreg
newkey = winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, r'Software\__test__')
winreg.CloseKey(newkey)

CreateKeyEx は作ったキーを開いて返すので、閉じるのを忘れないように。

Ex の付かない CreateKey は16bit時代の遺物なので使いません。

OpenKeyEx と CreateKeyEx の違い

OpenKeyEx → これから開こうとするキーは既に存在しているはず。もし存在しなければエラー。値の閲覧用として使いやすい。
CreateKeyEx → これから開こうとするキーは存在しないかもしれない。もし存在しなければ自動で作成する。値の書き込み用として使いやすい。

import winreg

path = r'Software\__test2__'

with winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, path) as key:
    winreg.SetValueEx(key, 'name1', 0, winreg.REG_SZ, 'data1')
    print('キーを開いて値を書き込んだよ')

with winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, path) as key:
    data, _ = winreg.QueryValueEx(key, 'name1')
    print('読み取ったデータ:', data)

値の削除

import winreg
path = r'Software\__test2__'
with winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, path, access=winreg.KEY_SET_VALUE) as key:
    winreg.DeleteValue(key, 'name1')

キーの削除

import winreg
winreg.DeleteKey(winreg.HKEY_CURRENT_USER, r'Software\__test2__')

DeleteKey と DeleteKeyEx は中身が違いますが、オプション機能を使わなければ同じはずです。

参考:32bit プロセス(WOW64 プロセス) から、64bit プロセス用のレジストリキーの参照

値の列挙

2つのやり方があるので、どちらか好きな方を選んでください。

1つ目は、予めキーの数を調べてから取得する方法です。

import winreg
path = r'Control Panel\Cursors'
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, path) as key:
    n_subkeys, n_subvalues, timestamp = winreg.QueryInfoKey(key)
    for i in range(n_subvalues):
        name, data, regtype = winreg.EnumValue(key, i)
        print('{:>25s} → {}'.format(name, data))

2つ目は、エラーになるまで取得を繰り返す方法です。

import winreg
import itertools

ERROR_NO_MORE_ITEMS = 259
path = r'Control Panel\Cursors'

with winreg.OpenKey(winreg.HKEY_CURRENT_USER, path) as key:
    for i in itertools.count():
        try:
            name, data, regtype = winreg.EnumValue(key, i)
        except WindowsError as err:
            if err.winerror == ERROR_NO_MORE_ITEMS:
                break
            raise
        print('{:>25s} → {}'.format(name, data))

キーの列挙

これも2つのやり方があるので、どちらか好きな方を選んでください。

import winreg
path = r'Software\Microsoft\Windows\CurrentVersion\Uninstall'
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, path) as key:
    n_subkeys, n_subvalues, timestamp = winreg.QueryInfoKey(key)
    for i in range(n_subkeys):
        subkey = winreg.EnumKey(key, i)
        print('-', subkey)
import winreg
import itertools

ERROR_NO_MORE_ITEMS = 259
path = r'Software\Microsoft\Windows\CurrentVersion\Uninstall'

with winreg.OpenKey(winreg.HKEY_CURRENT_USER, path) as key:
    for i in itertools.count():
        try:
            subkey = winreg.EnumKey(key, i)
        except WindowsError as err:
            if err.winerror == ERROR_NO_MORE_ITEMS:
                break
            raise
        print('-', subkey)

ちなみに、キーや値を列挙しながら削除していく時は、インデックスを逆順に回します。前からやるとインデックス番号がずれて困ります。

既定値

import winreg

# 読み
with winreg.OpenKeyEx(winreg.HKEY_CLASSES_ROOT, r'Python.File') as key:
    name = ''  # Noneでも大丈夫
    data, regtype = winreg.QueryValueEx(key, name)
    print(data)

# 書き
with winreg.OpenKeyEx(winreg.HKEY_CLASSES_ROOT, r'Python.File', access=winreg.KEY_SET_VALUE) as key:
    name = ''  # Noneでも大丈夫
    winreg.SetValueEx(key, name, 0, winreg.REG_SZ, 'ぱいそん ふぁいる')

名前欄を空にすれば既定値の読み書きができます。

後ろに Ex の付かない QueryValue と SetValue を使っても既定値は操作できますが、この2つは中のAPIが古いので使いません。

中の人のブログによれば、レジストリに既定値(もしくは既定の値)が存在するのは歴史的な理由によるものなんだそうだ。大昔の Windows では、キーと値は1対1に対応していた。しかし32ビットの Windows では1つのキーで複数の値を持てるようにした。それで値同士を識別するのに「値の名前」が作られ、さらに従来のキーだけでアクセスしていた値は「既定値」になったということらしい。PowerShellレジストリの値がプロパティ扱いされるのも、この辺りに理由がありそうな気がする。

REG_EXPAND_SZ

import winreg
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Environment') as key:
    data, regtype = winreg.QueryValueEx(key, 'temp')
    print('展開前:', data)
    if regtype == winreg.REG_EXPAND_SZ:
        data_exp = winreg.ExpandEnvironmentStrings(data)
        print('展開後:', data_exp)

REG_EXPAND_SZ は、環境変数を含むかもしれない文字列です。環境変数の展開は自分でやります。

REG_BINARY

import winreg
path = r'Software\__test__'

# 書き込み
with winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, path) as key:
    winreg.SetValueEx(key, 'name1', 0, winreg.REG_BINARY, b'binary data here')

# 読み込み
with winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, path) as key:
    data, _ = winreg.QueryValueEx(key, 'name1')
    print('読み取ったデータ:', data)

bytes型です。

REG_MULTI_SZ

import winreg
path = r'Software\__test__'

# 読み
with winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, path) as key:
    L = ['foo', 'bar', 'baz']
    winreg.SetValueEx(key, 'name2', 0, winreg.REG_MULTI_SZ, L)

# 書き
with winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, path) as key:
    data, _ = winreg.QueryValueEx(key, 'name2')
    print('読み取ったデータ:', data)

文字列のリストです。空文字列や \0 を含む文字列を混ぜるとバグります。

サブキーを開く

import winreg

k0 = winreg.HKEY_CURRENT_USER
k1 = winreg.OpenKeyEx(k0, 'Software')
k2 = winreg.OpenKeyEx(k1, 'Microsoft')
k3 = winreg.OpenKeyEx(k2, 'Windows')
k4 = winreg.OpenKeyEx(k3, 'CurrentVersion\Run')

for i in k1, k2, k3, k4:
    winreg.CloseKey(i)

このように、あるキーのサブキーを順繰りに開いていくことができます。CreateKeyEx でも同様です。

参考

Out-GridView は選択画面としても使える

Out-GridView を使うとコマンドレットの出力をGUIに表示できるわけだが、PassThru パラメーターを指定することで、オブジェクトの「選択画面」としても機能します。

# 例:カレントディレクトリのファイル一覧を表示して削除するファイルを選ばせる
Get-ChildItem | Out-GridView -PassThru | Remove-Item
# 省略形
gci | ogv -pass | ri

f:id:itasuke:20180103120907p:plain

Ctrl や Shift キーを押しながら行をポチポチ選んでから OK ボタンを押す。すると選んだ項目が Remove-Item に渡されてファイルが削除されるという寸法です。

ちなみに、Windows のリストビューにおいて Ctrl は一行選択、Shift は範囲選択です。

参考

Firefoxで文字化けするよくある理由

時々Firefoxでページが文字化けするサイトがあるけど、原因を調査すると95%くらいは Content-Type の設定が間違ってました。それもHTMLソースの方じゃなくて、HTTPヘッダーの方が。

普通、文字コードを指定するとしたらHTMLでこう書くと思うけど、

<!-- HTML5 -->
<meta charset="utf-8" />

<!-- HTML4 -->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">

HTTPのレスポンスヘッダーでも文字コードは指定できます。

GET /foo.html HTTP/1.1
Host: www.example.com
Connection: close

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8  ←コレ!!

で、もしmetaタグとHTTPヘッダーの文字コード指定が異なる場合は、HTTPヘッダーの方を優先するようです。

HTTPヘッダーによる指定の方が文書内でのmeta指定よりも優先度が高い

https://www.w3.org/International/questions/qa-html-encoding-declarations.ja

WebブラウザがHTML文書の文字コードを判定する際には、このメディアタイプのcharsetパラメータを最優先に参照する

http://www.atmarkit.co.jp/ait/articles/0412/25/news006.html

文字化けに至るまでの、よくありがちな流れはこんな感じです。

  • 以前はShift_JISEUC-JPでHTMLを記述していた。
  • 最近になって新しいコンテンツはUTF-8で書くようにした。
  • サーバーの設定も変えて、charset=utf8 を付けるようにした。
  • 古いコンテンツが文字化けし始めたことに気づかない。

とにかくコンテンツを見れるようにする

閲覧者の方で文字コードを変更する方法ですが、この前Firefoxはメニューの配置を変えたので奥の方に移動されています。探すと「その他」の中に配置されてました。

→ その他 → テキストエンコーディングの中です。

f:id:itasuke:20171218214112p:plain

話は変わるけど、この記号 を「ハンバーガーメニュー」と呼ぶんだが、どう見てもハンバーガーには見えないし、マクドナルドと紛らわしいので、代わりに三つ引き(みつひき)と呼んだらいいと思う。

画像認識と小説スノウ・クラッシュ

図書館でニュートンを立ち読みしてたら、人工知能の特集をやってて、何となくパラパラめくってたら、AIの弱点みたいなページがあった。どう見てもパンダの画像なのに、細工を施したノイズを混ぜることでテナガザルと認識させられるとかいう内容。

ainow.aitogetter.com

それで思い出したのだが、今から25年前に書かれた『スノウ・クラッシュ』っていうSF小説に似たような話が出てくる。VRの世界(今で言うMMORPGみたいなものか?)で、人間に特殊なノイズというかビットマップを見せることで人間の脳みそをクラッシュさせたり操ったりできるとかいう内容。当時も人工知能は流行ってたんだっけ? 確かエキスパートシステムとか何とか世代コンピュータとか聞いたことがある。それから人工知能研究は冬の時代に入り、また最近になって盛り返してきてるわけだ。

もし人間の意識を全てニューラルネットワークで説明できるなら、視覚に依存した生命にも似たような脆弱性があるんじゃないかとふと思ったけど、それはないか。何億年にも渡って進化してきた生命に、そんなわかりやすい脆弱性が残ってるわけがないというか、もしあったとしても錯視ぐらいなものかな。うん。