Windowsの送る(SendTo)メニューでスラッシュ区切りのパス文字列をクリップボードにコピーする

概要

  • Windowsのパス文字列を他の言語に渡す際、円記号(¥)もといバックスラッシュ(\)を毎回処理するのが面倒
  • エクスプローラなどのGUIメニューで選択するときにスラッシュ(/)を区切り文字とする標準的なパス文字列を取得したい。
  • Powershellスクリプトを作成して、右クリックメニューの「送る(SendTo)」に登録することで簡単に取得できた。

背景

歴史的な経緯もあり*1Windowsのパスは円記号もといバックスラッシュ(\)区切り。

ただ、バックスラッシュはRやPythonといった各種プログラミング言語ではエスケープ文字として使われている。

このため、例えばRで以下のようにバックスラッシュが含まれるWindowsのパス文字列を直接利用できない。

#以下は動かない
> setwd("D:\SandBox\")
 エラー:  ""D:\S" で始まる文字列の中で '\S' は文字列で認識されないエスケープです

#以下のどちらかなら動く
> setwd("D:/SandBox/")
> setwd("D:\\SandBox\\")

大したことではないのだが、毎度バックスラッシュを処理して渡すのはめんどくさいので簡単にしたい。

方法

Windowsエクスプローラには、ファイル・フォルダの右クリックメニューの中に「送る(SendTo)」というメニューがある。

このメニューはSendToフォルダにあるショートカットの集合体であり、実行すると選択したファイル・フォルダを引数としてそのショートカットを起動してくれる。

そこで、エクスプローラ上で簡単にバックスラッシュをスラッシュに変換した文字列を取得するために、パス文字列を変換するスクリプトを作成してそのショートカットをSendToに登録した。
これでファイル・フォルダを右クリックしてショートカットを選択すると、スラッシュで区切られた文字列を取得できるようになる。

以下のようなGet-FilePathWithSlash.ps1を作成する。cf.Powershell上で、パイプで渡すと文字化けする。

$OutputEncoding = [System.Text.Encoding]::GetEncoding('shift-jis')
$args[0] -replace "\\", "/" | clip.exe

追記 2018-07-04

上記だとクリップボードに改行が入っていしまうのでPowershell5.0以上が入っている環境なら以下のようにした方が良い

$OutputEncoding = [System.Text.Encoding]::GetEncoding('shift-jis')
$args[0] -replace "\\", "/" | Set-Clipboard

SendToフォルダの場所を確認する。cf.【PowerShell】SendToフォルダを表示するスクリプト2 | ほそぼそプログラミング日記

#SendToフォルダの場所
[Environment]::GetFolderPath('SendTo')

Path
----
C:\Users\majima_ko16\AppData\Roaming\Microsoft\Windows\SendTo

上記のフォルダにショートカットを任意の名前で作成し、プロパティのリンク先を以下のように書き換える。 cf. 「送る」with PowerShell - なにか作る

C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -WindowStyle Hidden -File "D:\<scriptPath>\Get-FilePathWithSlash.ps1"

その他

この方法を応用するとでCygwin用のパスも作れるだろう(cygpathはあるが)。

Powershellを使ってBasic認証を通してWebアクセスする

概要

  • PowershellInvoke-WebrequestのCredentialパラメータを使えば、簡単にBasic認証のかかったページにwebアクセスできる

実際

基本的に上記の通りCredentialを生成した上で、Invoke-Webreques-Credentialを指定してやればよい。 環境はWindows10で実行して試した、Powerhell 5.0以降であれば動作するはず。

その他

  1. ベーシック認証については以下を参照のこと

  2. Powershellを使って正しく認証プロキシ経由でWebアクセスする - mk_55's diaryでも記載したが、パスワードはSecureString等で事前に暗号化しておくことが望ましい。

Powershellを使って正しく認証プロキシ経由でWebアクセスする

概要

  • プロキシの自動構成スクリプト(.pac)に従って、正しい認証プロキシの認証を通した上で、Powershellでwebアクセスしたい。
  • つまり、アクセスするURLに対応するプロキシのURLを取得して、Invoke-Webrequestに渡してWebアクセスしたい。
  • [System.Net.WebRequest]::GetSystemWebProxy()を使うとOS設定を利用して、URLに対応するプロキシを取得することができる。

実際

OSでプロキシ設定がなされていることを前提としている

# 各種設定
$URL = "http://example.com"

# 認証情報の生成(本来は暗号化して外だししておく)
$ProxyUser = "User"
$ProxyPassword = "PassWord"
$securePassword = ConvertTo-SecureString $ProxyPassword -AsPlainText -Force
$proxyCredntial  = System.Management.Automation.PSCredential ($ProxyUser, $securePassword)

#システムのProxy設定を利用して、URLに対応するProxy経由でアクセス
$SystemProxy = [System.Net.WebRequest]::GetSystemWebProxy()
$proxy = $SystemProxy.GetProxy($URL)
Invoke-Webrequest $URL -Proxy $proxy.AsoluteUri -ProxyCredential $proxyCredntial  Method Get

環境はWindows10で実行しているので、Powerhell 5.0以降であれば動作するはず。

その他

  1. web上にはデフォルトプロキシを設定してしまうという例が散見されるが、汎用性に欠ける。

  2. プロキシの自動構成スクリプト(.pac)、要はJavascriptなので、ScriptControllerで実行してやろうと思った。しかし、ScriptControllerは64bit OSでは廃止されており不可能だった。

  3. 実用を考えるなら、パスワードはスクリプトに記載しせず、事前にSecureStringを利用してファイルに保存しておき、スクリプトの中でCredentialを生成する必要すべき。*1

  4. 例示用のドメインについてはRFC2606を参照のこと。

*1:正直なところ、私はSecureStringについて正しく理解しているとは言い難い。どこかで調べてまとめたい

携帯性だけを追求したBluetoothキーボード「Wekey Pocket PN301」

スマホ向けのキーボードを探していて、非常に薄くて軽いBluetoothキーボード「Wekey Pocket PN301」を購入したのでそのメモ。

良いところ

軽い。重さは95gとなんと100g以下である。

薄い。薄さは約5mmほど。

小さい。サイズはだいたいシステム手帳のバイブルサイズのリフィルぐらい。

上記のように携帯性が非常に良いので、気軽にカバンに入れておける。

また、二つ折りの本体を開くと電源が入り、すぐ使えるところも地味にポイントが高い。

悪いところ

キーボードとしての機能はあまり良くない。 打鍵感が全くないので、タイプの抜けがとても多い。また、ほぼただの板なので、タイピングは非常に固く長時間タイプするとだいぶ疲れそう。

その他

ペアリングはFn+P、キーサウンドはFn+BackSpaceで調整することができる。 キーボード配列が英語なので、気になる人は気になるかもしれない。

まとめ

メインで利用するというよりは、ちょっと外出・旅行するときに、スマホと一緒にお守り代わりに持っておくと重宝するかな、という感じだとおもう。

あと、似たような製品で以下のようなものがあり、重さが2倍になるが、打鍵感はこちらの方がよさそう。

WindowsのVS Codeでtextlintを使ってリアルタイムに文章校正する。

Windows上のVS Codeでtextlintを使うためにやるべきことは以下の通り。

Node.jsとnpmをインストールする

textlintはNode.js製のプログラムなのでまずはNode.jsとNode.js用のパッケージ管理ツールであるnpmをインストールする。

例えば次のようなページが参考になる⇒Node.js / npmをインストールする(for Windows) - Qiita

上記のページに従って、公式サイトにアクセスし、Windows用のバイナリをダウンロードしてインストールする。 インストールに成功していれば以下のコマンドでバージョンを確認できる。

PS D:\> node --version
v8.10.0
PS D:\> npm --version
5.6.0

textlintをインストールする

npmを使ってtextlintをインストールする。ここでは手っ取り早くグローバルインストールする*1

また、ルールを一から設定するのは手間なので、azuさん作の日本語の技術文書向けプリセットであるtextlint-rule-preset-ja-technical-writingをインストールする。 ついでに例外を明示するためにtextlint/textlint-filter-rule-commentstextlint/textlint-filter-rule-whitelistもインストールする。

PS D:\> npm install -global textlint textlint-rule-preset-ja-technical-writing textlint-filter-rule-comments textlint-filter-rule-whitelist

textlintを設定する

インストール後には設定ファイルを作成する必要がある。 textlintの対象にしたいフォルダへ移動してtextlint --initすると設定ファイルである.textlintrcが生成される。

PS D:\> cd D:\hoge
PS D:\hoge> textlint --init
PS D:\hoge> ls


    ディレクトリ: D:\hoge


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----       2018/03/23     22:53             34 .textlintrc
-a----       2018/03/23     22:06           1174 textlintのインストール.md

あとは生成された設定ファイルで、先ほどインストールしたプリセットルールを有効にする。

{
  "filters": {
      "comments": true
  },
    "rules": {
        "preset-ja-technical-writing": true
    }
}

トラブルシューティング

ここまでやって動くと思いきや、エラーでるのでそのトラブルシューティング

textlintが動作しない

[Error - 23:04:48] Server initialization failed.
  Message: Request initialize failed with message: Failed to resolve module: textlint
  Code: -32603

textlintへのパスが通っていないから発生したエラー。VS Codeを再起動して解決。

モジュールが利用できない

PS D:\Users\username\hoge> textlint --config .\.textlintrc .\textlintのインストール.md
× Error
Failed to load textlint's filter rule module: "comments" is not found.
See FAQ: https://github.com/textlint/textlint/blob/master/docs/faq/failed-to-load-textlints-module.md


× Stack trace
ReferenceError: Failed to load textlint's filter rule module: "comments" is not found.
See FAQ: https://github.com/textlint/textlint/blob/master/docs/faq/failed-to-load-textlints-module.md

    at TextLintModuleResolver.resolveFilterRulePackageName (C:\Users\username\AppData\Roaming\npm\node_modules\textlint\lib\textlint\src\engine\textlint-module-resolver.js:116:19)
    at TextLintModuleLoader.loadFilterRule (C:\Users\username\AppData\Roaming\npm\node_modules\textlint\lib\textlint\src\engine\textlint-module-loader.js:221:43)
    at C:\Users\username\AppData\Roaming\npm\node_modules\textlint\lib\textlint\src\engine\textlint-module-loader.js:80:23
    at Array.forEach (<anonymous>)
    at TextLintModuleLoader.loadFromConfig (C:\Users\username\AppData\Roaming\npm\node_modules\textlint\lib\textlint\src\engine\textlint-module-loader.js:79:32)
    at TextLintEngine.AbstractTextLintEngine (C:\Users\username\AppData\Roaming\npm\node_modules\textlint\lib\textlint\src\engine\textlint-engine-core.js:97:27)
    at new TextLintEngine (C:\Users\username\AppData\Roaming\npm\node_modules\textlint\lib\textlint\src\textlint-engine.js:22:47)
    at Object.executeWithOptions (C:\Users\username\AppData\Roaming\npm\node_modules\textlint\lib\textlint\src\cli.js:133:26)
    at Object.execute (C:\Users\username\AppData\Roaming\npm\node_modules\textlint\lib\textlint\src\cli.js:90:25)
    at C:\Users\username\AppData\Roaming\npm\node_modules\textlint\bin\textlint.js:34:20
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
    at Function.Module.runMain (module.js:695:11)
    at startup (bootstrap_node.js:188:16)

commentsモジュールが見つからずに発生したエラー。再度全モジュールをnpm install -globalして解消。

コマンドラインでは動くけど、ファイルを開いても拡張機能が動作しない

textlintのVS Code拡張は以下の条件で動作するので、ファイル単位ではなく、フォルダ(プロジェクト)単位で開く必要がある。フォルダを開いて解決。

既に、textlintの設定をしているプロジェクト(node_modulesにtextlintやルールがインストールされていて、.textlintrcが配置されている場所)なら、VS Codeでそのディレクトリを開くだけで動作します。

VS Codeでtextlintを使って文章をチェックする - Qiita

指定のルールだけ設定を変更したい場合(2018/06/24追記)

‘preset-ja-technical-writing‘で一部のルールを無効化したり、設定を変更したりしたいときは、以下のように入れ子に記載する。

{
    "filters": {
        "comments": true
    },
    "rules": {
        "preset-ja-technical-writing": {
            "sentence-length": {
                "max": 100
            }
        }
    }
}

おわりに

ここまでできれば、リアルタイムにlintしながら文章を書くことができる。 ただ、この時点ではデフォルト設定がのみなので、効果は限定的だ。より効果を出すためには、 .textlintrc をカスタマイズして辞書やフィルタを育てていけばよい。 楽をするために頑張って、退屈なことは機械にやらせよう。

*1:Linuxなどにおいてインストールするときは、パーミッションエラー等になることもあるそうなので、よく考えてローカルモードでやった方がよいだろう

Powershellで特定のフォルダの拡張子の一覧を取得する

特定のフォルダ配下に含まれるファイルの拡張子の一覧を取得するための方法のメモ

Powershellを使ってファイルの拡張子を得るにはいくつか方法があって、例えばSystem.IO.Pathクラスを使う手もあったりするが、それよりはSystem.IO.FileInfoクラスを生成してしまった方が楽だし応用が効くように思う。

PS D:\> Get-ChildItem -Recurse -File | %{$_.Extension} | Sort-Object -CaseSensitive | Get-Unique
.log
.txt

やっていることは以下のような流れ

  • Get-ChildItem -Recurse -Fileでカレントディレクトリのファイルをサブディレクトリ含めて取得
  • FileInfoのExtentionフィールドで拡張子を取得して
  • Sort-Object -CaseSensitiveで大文字小文字を区別してソートしたうえで
  • Get-Uniqueでユニークにする(重複を除く)

なお、Get-ChileItemのFileパラメータはv3からしか使えないので、v2の場合はWhere-Objectの抽出条件でFileInfoのAttributeか、PSIsContainerを使って絞りこむことで代替する。

PS D:\SandBox> Get-ChildItem -Recurse | Where-Object{$_.Attribute -ne "Directory" } | %{$_.Extension} | Sort-Object -CaseSensitive | Get-Unique

PS D:\SandBox> Get-ChildItem -Recurse | Where-Object{$_.PSIsContainer -eq $false } | %{$_.Extension} | Sort-Object -CaseSensitive | Get-Unique

また、特定のファイルの拡張子を取得するときはGet-ItemでFileInfoのオブジェクトを取得すればよい。Alias(gi)を使うと楽に書ける。

PS D:\> $(Get-Item .\random.log).Extension
.log

Powershellスクリプト・モジュールのヘルプトピックの作り方

Powershellスクリプト・モジュールで正しくGet-Helpで読むことができるヘルプトピック(Help topic)を作る方法のメモ。

そもそもヘルプトピックとは?

要するにGet-Helpしたときに表示されるヘルプのこと*1。細かいことは詳しいヘルプ情報の取得 | Microsoft Docsを参照。

例えばGet-HelpコマンドレットをGet-Helpすると以下のようになる

PS D:\SandBox> get-help Get-ChildItem

名前
    Get-ChildItem

概要
    Gets the items and child items in one or more specified locations.

(以下略)

上記のようなヘルプトピックは、簡単にPowershellで自作したスクリプト・モジュールでも表示させることができるので、以降はその方法・記法を記載する*2

スクリプト(.ps1)

スクリプトの場合は、コメントベースヘルプ(Comment_Based_Help)をスクリプト内に記述することで、ヘルプトピックを作成できる。 記載場所は関数の宣言のすぐ上かすぐ下で、以下のようなイメージ。

#関数の前に書く場合
  <#
  .SYNOPSIS
   関数の概要
  .DESCRIPTION
   関数の詳細な説明
  .EXAMPLE
   関数の利用例
  .EXAMPLE
   利用例は複数記載できる
  .PARAMETER foo
  パラメータの説明
  .PARAMETER bar
  パラメータの分だけ記載する
  #>
function Do-Hoge {
   (略)
}

関数の中に書く場合
function Do-Fuga {
  <#
  .SYNOPSIS
   関数の概要
  .DESCRIPTION
   関数の詳細な説明
  .EXAMPLE
   関数の利用例
  .EXAMPLE
   利用例は複数記載できる
  .PARAMETER foo
  パラメータの説明
  .PARAMETER bar
  パラメータの分だけ記載する
  #>
 (略)
}

もっと詳しいことはGet-Help about_Comment_Based_Helpを実行するか、以下のWebページを読むとわかると思う。

モジュール(.psm1)

モジュールの場合は、コメントベースヘルプも使えるが、それ以外にもヘルプを外部ファイルに記述して読み込ませることができる。 ファイルには、モジュール全体に対する説明である「概念説明のヘルプ(Conceptual ("About") Help)」と、個々の関数について記載する「コマンドレット ヘルプファイル(Cmdlet Help File)」の2種類がある。

なお、モジュールに関しては以下のブログ記事がとても参考になる。モジュールの自動読み込みについてとか。

モジュールヘルプの場所

以下のような形でヘルプファイルを配置すると、OSの言語によって自動的に読み込むヘルプファイルを切り替えてくれる。

<ModulePath>
         \SampleModule
               \<en-US>
                     \about_SampleModule.help.txt
                     \SampleModule.extention-help.xml
                     \SampleNestedModule.extention-help.xml
               \<ja>
                     \about_SampleModule.help.txt
                     \SampleModule.extention-help.xml
                     \SampleNestedModule.extention-help.xml

このように多言語対応できるところが、この方式の強みだと思う。逆に言うと、Powershellでモジュールを書いて、かつドメスティックに使うだけならComment Basedで十分な気がする。

ロケール(en-USとか)の一覧はロケール ID (LCID) の一覧VBScriptのレファレンスだが、参考になる。

概念説明のヘルプ(Conceptual ("About") Help)

モジュール全体の説明のことで、Writing Help for Windows PowerShell Modules.aspx)に詳しい。 以下のようなファイル(内容は上記サイトからの引用)を「about_hogehoge.help.txt」という名前で作ると、Get-Help about_hogehogeで参照できる。

TOPIC
    about_<subject or module name>

SHORT DESCRIPTION
    A short, one-line description of the topic contents.

LONG DESCRIPTION
    A detailed, full description of the subject or purpose of the module.

EXAMPLES
    Examples of how to use the module or how the subject feature works in practice.

KEYWORDS
    Terms or titles on which you might expect your users to search for the information in this topic.

SEE ALSO
    Text-only references for further reading. Hyperlinks cannot work in the Windows PowerShell console. 

コマンドレット ヘルプファイル(Cmdlet Help File)

モジュールの中のメンバー(関数など)のヘルプをxmlファイル(SampleModule.extention-help.xml)に記載する。 例としては以下のような感じ(MSのサイトからの引用)。

<command:command
  xmlns:maml="http://schemas.microsoft.com/maml/2004/10"
  xmlns:command="http://schemas.microsoft.com/maml/dev/command/2004/10" 
  xmlns:dev="http://schemas.microsoft.com/maml/dev/2004/10">
  <command:details>
    <!--Add name an synopsis here-->
  </command:details>
  <maml:description>
    <!--Add detailed description here-->
  </maml:description>
  <command:syntax>
    <!--Add syntax information here-->
  </command:syntax>
  <command:parameters>
    <!--Add parameter information here-->
  </command:parameters>
  <command:inputTypes>
    <!--Add input type information here-->
  </command:inputTypes>
  <command:returnValues>
    <!--Add return value information here-->
  </command:returnValues>
  <maml:alertSet>
    <!--Add Note information here-->
  </maml:alertSet>
  <command:examples>
    <!--Add cmdlet examples here-->
  </command:examples>
  <maml:relatedLinks>
    <!--Add links to related content here-->
  </maml:relatedLinks>
</command:command>

とりあえず、参考になりそうなのは以下のサイト。よめば雰囲気でだいたいわかる。特に最後のSAPIEN社の公式ブログの「What do I name the XML help file?」はファイル命名規則について詳しく参考になる。

しかし、多言語対応するにはこのファイルを作ることが必要なのだが、残念なことにコマンドが自動でやってくれていた補完が効かない。 例えばParameterのattributeなど、コードで明示していることも、いちいち自分で書かないといけない。生のxmlを、しかも英語のレファレンスを見ながら書かざるを得ず、とてもつらい。

Git-Hubを見るとコードからヘルプ生成を自動化するプロジェクトも散見されるので、そのうち使って記事にしたいと思う。たとえばPowerShell/platyPSとか。

まとめ

ちゃんとGet-Help対応のヘルプ(Help Topic)を書こう。

*1:Linuxでいうmanコマンドで表示されるmanページのこと

*2:Linuxで例えるとmanのためのgroffマクロの書き方のようなイメージ。manページを書いたことはないけれど