tag:crieit.net,2005:https://crieit.net/tags/PowerShell/feed 「PowerShell」の記事 - Crieit Crieitでタグ「PowerShell」に投稿された最近の記事 2023-12-01T01:13:10+09:00 https://crieit.net/tags/PowerShell/feed tag:crieit.net,2005:PublicArticle/18667 2023-12-01T01:13:10+09:00 2023-12-01T01:13:10+09:00 https://crieit.net/posts/PowerShell-6568b49675a81 後ほどミュートを解除しておく PowerShell スクリプト <p>本記事は <a target="_blank" rel="nofollow noopener" href="https://qiita.com/advent-calendar/2023/shell">シェルスクリプト&PowerShell Advent Calendar 2023</a> の1日目の記事だ。<br /> 如何にハードルを下げるかを考え、しょーもない内容となることを意識している…と言い訳しておこう。</p> <h2 id="何故ミュートを解除したいのか"><a href="#%E4%BD%95%E6%95%85%E3%83%9F%E3%83%A5%E3%83%BC%E3%83%88%E3%82%92%E8%A7%A3%E9%99%A4%E3%81%97%E3%81%9F%E3%81%84%E3%81%AE%E3%81%8B">何故ミュートを解除したいのか</a></h2> <p>さて、一時的に PC をミュートにしておきたい事がある。</p> <p>コロナ禍を経て、 Cisco や Polycom といったテレビ会議システムに替わって、 Zoom などのWeb会議ソフトが普及した。<br /> しかし、近年出社を強要されるのが一般化してくると、結局以前のように会議室に集まって、離れた拠点の相手とビデオ会議を行うことになる。<br /> このとき、全員各々のPCでビデオ会議には参加するものの、音声は卓上マイク&スピーカー使う事があり、ハウリングを避ける為に卓上マイクを使う人以外はスピーカー・マイクをミュートにするだろう。</p> <p>ところが、会議終了後ミュートしっぱなしにしてしまい、通知等に気づきにくくなることがあるので、一定時間後にミュートを簡単に解除できるようにしておきたい。</p> <p>それだけのために、スクリプト経由で MMDevice API 呼ぶのも仰々しいなぁと思っていた所、よく考えたらキーボードにあるミュート状態をトグルキーを仮想的に押せばいいじゃないかと気づいた。</p> <p>ということで、早速 PowerShell でキーボードのマルチメディアキーを押すスクリプトを書いてみる。</p> <h2 id="早速コード例"><a href="#%E6%97%A9%E9%80%9F%E3%82%B3%E3%83%BC%E3%83%89%E4%BE%8B">早速コード例</a></h2> <pre><code class="powershell">#Requires -Version 5 $addTypeOptions = if ($PSVersionTable.PSVersion -lt [version]'6.0') { @{ Language = 'CSharpVersion3' }; } else { @{}; }; $typeKeyboard = Add-Type -PassThru @addTypeOptions -TypeDefinition @' using System; using System.Runtime.InteropServices; namespace D8B35E79FC964E69854B61A2C9FB3C08 { public static class Keyboard { [StructLayout(LayoutKind.Sequential)] struct MOUSEINPUT { public Int32 dx; public Int32 dy; public UInt32 mouseData; public UInt32 dwFlags; public UInt32 time; public IntPtr dwExtraInfo; } [StructLayout(LayoutKind.Sequential)] struct KEYBDINPUT { public UInt16 wVk; public UInt16 wScan; public UInt32 dwFlags; public UInt32 time; public IntPtr dwExtraInfo; } // Define the unused MOUSE structure due to the UNION_INPUT structure size determination. [StructLayout(LayoutKind.Explicit)] struct UNION_INPUT { [FieldOffset(0)] public MOUSEINPUT mi; [FieldOffset(0)] public KEYBDINPUT ki; } [StructLayout(LayoutKind.Sequential)] struct INPUT { public int type; public UNION_INPUT ui; } [DllImport("user32.dll", SetLastError = true)] extern static uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize); public static void TypeKey(UInt16 vk) { var inputs = new INPUT[2]; inputs[1].type = inputs[0].type = 1; // INPUT_KEYBOARD inputs[1].ui.ki.wVk = inputs[0].ui.ki.wVk = vk; inputs[0].ui.ki.dwFlags = 0x0000; // KEYEVENTF_KEYDOWN inputs[1].ui.ki.dwFlags = 0x0002; // KEYEVENTF_KEYUP var uSent = SendInput(2, inputs, Marshal.SizeOf(inputs[0])); if (uSent != 2) { throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error()); } } } } '@ | Where-Object Name -EQ Keyboard; $typeKeyboard::TypeKey(<# VK_VOLUME_MUTE #> 0xAD); </code></pre> <p>これを <code>Push-MuteKey.ps1</code> 等の名前で保存しておいて、</p> <pre><code>Start-Sleep -Seconds ([datetime]'11:01' - [datetime]::Now).TotalSeconds; .\Push-MuteKey.ps1; </code></pre> <p>などと指定して実行すれば、指定時間にミュートキーが押されてミュートが解除される。</p> <p>Win32 API C# 経由で叩く場合、 32bit か 64bit どちらのプロセスでも問題なく動くよう気をつける必要があったり、 PowerShell のバージョン (5 か 7以降か) でコンパイル時の挙動が異なるので、そこらへんも気をつけて実装した。<br /> うーん、 Win32 API 叩くために色々構造体を定義する必要もあって、結局仰々しいなぁ。。。</p> <h2 id="おわりに"><a href="#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB">おわりに</a></h2> <p><a target="_blank" rel="nofollow noopener" href="https://qiita.com/advent-calendar/2023/shell">シェルスクリプト&PowerShell Advent Calendar 2023</a> は、執筆者募集中です!<br /> こんな割としょうも無い内容で構わないので、久々になんか書いてみませんか!?</p> advanceboy tag:crieit.net,2005:PublicArticle/18588 2023-10-03T09:07:43+09:00 2023-10-03T23:51:18+09:00 https://crieit.net/posts/Pester-v3-v5-PowerShell Pester v3 & v5 互換のテストコードを書く (PowerShell) <p>PowerShell のテストフレームワークである Pester は、単体テストや自動テストなどを手軽に書けるので、スクリプトやコードの品質を保つのに役立つ。</p> <p>しかし、 Pester は v3 から v5 にかけてかなり大きな破壊的変更がある。</p> <p>この記事では、 Pester v3 と v5 の両方に互換があるテストコードの書き方について紹介したい。</p> <h2 id="何故 v3 と v5 で互換を取りたいか"><a href="#%E4%BD%95%E6%95%85+v3+%E3%81%A8+v5+%E3%81%A7%E4%BA%92%E6%8F%9B%E3%82%92%E5%8F%96%E3%82%8A%E3%81%9F%E3%81%84%E3%81%8B">何故 v3 と v5 で互換を取りたいか</a></h2> <p>Windows 10 や 11 では、デフォルトで Windows PowerShell 5.1 がインストールされており、更にそこには Pester モジュールの v3.4.0 もインストールされている。<br /> Windows 上の PowerShell モジュールディレクトリはシステム全体で共通のため、たとえ別途 PowerShell 7.3 LTS などをインストールしていたとしても、既定では Pester v3.4.0 が読み込まれるわけだ。</p> <p>一方、 Linux 等のそれ以外のシステムで PowerShell をインストールした場合は、 Pester が自動的にインストールされることはないため、 Pester モジュールを追加でインストールすることになる。<br /> このとき、普通は最新版の v5 が入るだろう。</p> <p>Windows PowerShell の Pester を v5 に更新させたり、 Linux 等のシステムでインストールする Pester モジュールを v3.4 に抑えさせたりできればよいが、まぁなかなかそうも行かない時がある。</p> <p>そんなのっぴきならん状況だと、多少苦労してでもテストコード側を Pester v3 と v5 どちらにも互換を取ってしまったほうが手っ取り早いかも…なんて状況が地球上の何処かには存在するかもしれない。</p> <h2 id="テストコードの書き方"><a href="#%E3%83%86%E3%82%B9%E3%83%88%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E6%9B%B8%E3%81%8D%E6%96%B9">テストコードの書き方</a></h2> <p>v3 と v4 の間の破壊的変更で一番デカいのは、 <code>Should</code> 構文の変更で互換切りを行っている部分だ。</p> <p><a target="_blank" rel="nofollow noopener" href="https://pester.dev/docs/migrations/breaking-changes-in-v5">https://pester.dev/docs/migrations/breaking-changes-in-v5</a></p> <blockquote> <ul> <li>Legacy syntax <code>Should Be</code> (without <code>-</code>) is removed, see <a target="_blank" rel="nofollow noopener" href="https://pester.dev/docs/migrations/v3-to-v4">Migrating from Pester v3 to v4</a></li> </ul> </blockquote> <p>構文自体の変更自体は v4 で行われたものだが、このときは以前の構文も互換性が残されていた。<br /> ところが、 v5 で早速この互換が切られてしまっている。</p> <p>コレのせいで、ほぼすべてのテストコードが、そのままでは v3 と v5 の間で互換を取ることが不可能になっている。<br /> 何考えてんだ……</p> <p>位置指定パラメータからスイッチパラメータに変わっているため、このどちらにも渡せるようにするためには <a target="_blank" rel="nofollow noopener" href="https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_splatting">Splatting 表記</a> に頼ることにする。<br /> <a target="_blank" rel="nofollow noopener" href="https://github.com/pester/Pester/wiki/Should-v3">v3 で使える <code>Should</code></a> のオペレーターそれぞれについて、 v3 以下なら位置指定パラメータで渡す方法で、 v4 以上ならスイッチパラメータで渡す方法で、それぞれ <code>$BeForCompat</code> のような形で <code>$*ForCompat</code>, <code>$Not*ForCompat</code> の形式の変数をテストコード開始時に定義する。<br /> そして、その変数を <code>Should</code> で <code>@BeForCompat</code> のように Splatting 表記にて使用する。</p> <p>また、 v4 では <code>Contain</code>/<code>ContainExactly</code> オペレーターが <a target="_blank" rel="nofollow noopener" href="https://pester.dev/docs/migrations/v3-to-v4#update-to-new-names"><code>-FileContentMatch</code>/<code>-FileContentMatchExactly</code></a> にリネームされていたり、<br /> v5 では <code>*.Tests.ps1</code> <a target="_blank" rel="nofollow noopener" href="https://pester.dev/docs/migrations/v4-to-v5#put-setup-in-beforeall">ファイル先頭や、 <code>Context</code>, <code>Describe</code> 直下にセットアップコードが記載できなくなっている</a>ので、 全て <code>BeforeAll</code> ブロック内に記述する必要がある。</p> <p>これらのマイグレーションをまとめると、 v3/v5 互換の Pester テストコードは以下のようになる。<br /> (<code>v3-to-v5-Migrations.Tests.ps1</code> というファイル名で保存されているものとする)</p> <pre><code class="powershell">#Requires -Version 5 #Requires -Modules @{ ModuleName="Pester"; ModuleVersion="3.0" } trap { break; } $GlobalBeforeAll = { # Write here the code that needs to be executed at the start of the test $shouldParams = 'Be','BeExactly','BeGreaterThan','BeLessThan','BeLike','BeLikeExactly','BeNullOrEmpty','BeOfType','Exist','Match','MatchExactly','Throw'; if ((Get-Module Pester).Version -ge [version]'4.0') { $shouldParams | ForEach-Object { Set-Variable "${_}ForCompat" @{ $_ = $true }; Set-Variable "Not${_}ForCompat" @{ Not = $true; $_ = $true }; }; $FileContentMatchForCompat = @{ FileContentMatch = $true }; $FileContentMatchExactlyForCompat = @{ FileContentMatchExactly = $true }; $NotFileContentMatchForCompat = @{ Not = $true; FileContentMatch = $true }; $NotFileContentMatchExactlyForCompat = @{ Not = $true; FileContentMatchExactly = $true }; } else { $shouldParams | ForEach-Object { Set-Variable "${_}ForCompat" @($_); Set-Variable "Not${_}ForCompat" @('Not', $_); }; $FileContentMatchForCompat = @('Contain'); $FileContentMatchExactlyForCompat = @('ContainExactly'); $NotFileContentMatchForCompat = @('Not','Contain'); $NotFileContentMatchExactlyForCompat = @('Not','ContainExactly'); } } if ((Get-Module Pester).Version -ge [version]'5.0') { BeforeAll $GlobalBeforeAll; } else { . $GlobalBeforeAll; } Describe 'Pester v3, v5 compatibility notation test' { BeforeAll { # DON'T use $MyInvocation.MyCommand.Path $filePath = '.\v3-to-v5-Migrations.Tests.ps1'; } It "Pattern: (<A>, <B>)" -TestCases @( @{A=1; B=2}; @{A=3; B=4}; ) { param ($A, $B); ($A / $A) | Should @BeForCompat 1; ($B * $A) | Should @NotBeForCompat 0; $filePath | Should @FileContentMatchExactlyForCompat 'BeforeAll'; } } </code></pre> <pre><code class="console">PS > # Pester 3.4.0 の場合 PS > Get-Module Pester ModuleType Version PreRelease Name ---------- ------- ---------- ---- Script 3.4.0 Pester PS > Invoke-Pester Describing Pester v3, v5 compatibility notation test [+] Pattern: (1, 2) 74ms [+] Pattern: (3, 4) 5ms Tests completed in 53ms Passed: 2 Failed: 0 Skipped: 0 Pending: 0 Inconclusive: 0 PS > </code></pre> <pre><code class="console">PS > # Pester 5.5.0 の場合 PS /> Get-Module Pester ModuleType Version PreRelease Name ---------- ------- ---------- ---- Script 5.5.0 Pester PS > Invoke-Pester Starting discovery in 1 files. Discovery found 2 tests in 21ms. Running tests. [+] .../v3-to-v5-Migrations.Tests.ps1 115ms (82ms|13ms) Tests completed in 117ms Tests Passed: 2, Failed: 0, Skipped: 0 NotRun: 0 PS > </code></pre> <p>うーん、面倒くさいね。</p> <h2 id="Tips"><a href="#Tips">Tips</a></h2> <p>こんなアホなことはせずに、Pester v3 なら v3 のコードで、 v5 なら v5 のコードで記述する場合、せめてテストコードの先頭で <a target="_blank" rel="nofollow noopener" href="https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_requires"><code>#Requires</code> 構文</a> を書いて、どのバージョンの Pester をターゲットにしているかしっかり明示しよう。</p> <pre><code class="powershell"># Pester v3, v4 をターゲットにする場合 #Requires -Modules @{ ModuleName="Pester"; ModuleVersion="3.0"; MaximumVersion="4.99" } (Get-Module Pester).Version; Describe 'desc' { It 'it' { 1 | Should Be 1; }; }; </code></pre> <pre><code class="powershell"># Pester v5 以降をターゲットにする場合 #Requires -Modules @{ ModuleName="Pester"; ModuleVersion="5.0" } BeforeAll { (Get-Module Pester).Version; }; Describe 'desc' { It 'it' { 1 | Should -Be 1; }; }; </code></pre> <p>本来 <code>#Requires</code> 構文には、必要なモジュールが現在のセッションにない場合自動でインポートする機能があり、例えば複数のバージョンのモジュールがサイドバイサイドでインストールされていた場合だと、条件に一致するバージョンのものがインポートされる便利な機能がある。<br /> しかし Pester の場合は、必ずしも良い感じには機能してくれるとは限らない。</p> <p>テストコードが読み込まれる前 <code>Invoke-Pester</code> した時点で Pester モジュールのインポートが走ってしまうことや、複数のバージョンのモジュールがインポートされた状態になると、いずれか一つのモジュールが条件を満たせば <code>#Requires</code> の検証をパスしてしまうのに、インポートされたもののうち最も古いバージョンのモジュールで実行されてしまうような挙動をとるためだ。</p> <p>それでも、 Pester モジュールがひとつしかインストールされていないような多くの状況では、バージョンを明示しておくことでエラー原因がわかりやすくはなるはずだ。</p> advanceboy tag:crieit.net,2005:PublicArticle/18409 2023-03-19T17:06:25+09:00 2023-03-19T17:06:25+09:00 https://crieit.net/posts/219f6ded47497fc5dc0f3415074011b8 エクスプローラーのキャッシュを削除 <p>■ExplorerCashDel.vbs</p> <pre><code>Option Explicit Const cmd = "Remove-Item C:\Users\xxxx\AppData\Roaming\Microsoft\Windows\Recent\AutomaticDestinations/*-ms" Dim processName : processName = "エクスプローラーキャッシュ削除処理" PeriodicExecution cmd,processName '========================================================= '機能名 : Sub PeriodicExecution '引数 : pm_cmd PowerShellコマンド ' : pm_processName 処理名 '処理 : PowerShell定期実行用 '========================================================= Sub PeriodicExecution(pm_cmd ,pm_processName) Dim objFS Dim x Dim strName,strPath 'ファイルシステムオブジェクト作成 Set objFS = CreateObject("Scripting.FileSystemObject") 'ファイル名を取得 strName = objFS.GetFileName(strPath) x = Msgbox ( pm_processName & "を実行しますか?" & strName , vbOkCancel , pm_processName ) If x = vbOk then ' WSHオブジェクトの作成 Dim objShell Set objShell = CreateObject("WScript.Shell") ' PowerShellコマンドの実行 objShell.Run "powershell -ExecutionPolicy RemoteSigned -Command " & pm_cmd, 0 Msgbox "完了しました" , , pm_processName End If ' WSHオブジェクトの破棄 Set objShell = Nothing End Sub </code></pre> speasmen88 tag:crieit.net,2005:PublicArticle/18402 2023-03-07T15:07:01+09:00 2023-03-07T15:07:01+09:00 https://crieit.net/posts/M365-PowerShell-6406d4854c02c 【M365】PowerShellで複数のユーザーアカウントのパスワードを変更する <p>初めてPowerShellで操作する場合は、<a href="https://crieit.net/posts/PowerShell-M365-Teams">こちら</a>を参考にM365の管理モジュールをインストールすること</p> <h2 id="Azure Active Directory への接続を開始"><a href="#Azure+Active+Directory+%E3%81%B8%E3%81%AE%E6%8E%A5%E7%B6%9A%E3%82%92%E9%96%8B%E5%A7%8B">Azure Active Directory への接続を開始</a></h2> <pre><code class="PowerShell">> Connect-MsolService </code></pre> <h2 id="複数ユーザーアカウントのパスワード一括変更に使用するCSVの作成例"><a href="#%E8%A4%87%E6%95%B0%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%81%AE%E3%83%91%E3%82%B9%E3%83%AF%E3%83%BC%E3%83%89%E4%B8%80%E6%8B%AC%E5%A4%89%E6%9B%B4%E3%81%AB%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8BCSV%E3%81%AE%E4%BD%9C%E6%88%90%E4%BE%8B">複数ユーザーアカウントのパスワード一括変更に使用するCSVの作成例</a></h2> <div class="table-responsive"><table> <thead> <tr> <th>UserPrincipalName</th> <th>Password</th> </tr> </thead> <tbody> <tr> <td>[email protected]</td> <td>hogehogepassword1</td> </tr> <tr> <td>[email protected]</td> <td>hogehogepassword2</td> </tr> </tbody> </table></div> <h2 id="複数ユーザーアカウントのパスワード一括変更(CSVインポート)"><a href="#%E8%A4%87%E6%95%B0%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%81%AE%E3%83%91%E3%82%B9%E3%83%AF%E3%83%BC%E3%83%89%E4%B8%80%E6%8B%AC%E5%A4%89%E6%9B%B4%28CSV%E3%82%A4%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%88%29">複数ユーザーアカウントのパスワード一括変更(CSVインポート)</a></h2> <p>インポートするCSVファイル(NewAccounts.csv)がC:\M365tools\2023にある場合の例</p> <pre><code class="PowerShell">> Import-Csv -Path "C:\M365tools\2023\ChangePW.csv" | foreach {Set-MsolUserPassword -NewPassword $_.Password -UserPrincipalName $_.UserPrincipalName -ForceChangePassword $False} </code></pre> arohajiro tag:crieit.net,2005:PublicArticle/18401 2023-03-07T15:03:40+09:00 2023-03-07T15:03:40+09:00 https://crieit.net/posts/M365-PowerShell-6406d3bca65e3 【M365】PowerShellで複数のユーザーアカウントの表示名を変更する <p>初めてPowerShellで操作する場合は、<a href="https://crieit.net/posts/PowerShell-M365-Teams">こちら</a>を参考にM365の管理モジュールをインストールすること</p> <h2 id="Azure Active Directory への接続を開始"><a href="#Azure+Active+Directory+%E3%81%B8%E3%81%AE%E6%8E%A5%E7%B6%9A%E3%82%92%E9%96%8B%E5%A7%8B">Azure Active Directory への接続を開始</a></h2> <pre><code class="PowerShell">> Connect-MsolService </code></pre> <h2 id="複数ユーザーアカウントの表示名一括変更に使用するCSVの作成例"><a href="#%E8%A4%87%E6%95%B0%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%81%AE%E8%A1%A8%E7%A4%BA%E5%90%8D%E4%B8%80%E6%8B%AC%E5%A4%89%E6%9B%B4%E3%81%AB%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8BCSV%E3%81%AE%E4%BD%9C%E6%88%90%E4%BE%8B">複数ユーザーアカウントの表示名一括変更に使用するCSVの作成例</a></h2> <div class="table-responsive"><table> <thead> <tr> <th>UserPrincipalName</th> <th>DisplayName</th> </tr> </thead> <tbody> <tr> <td>[email protected]</td> <td>user1(AbeT)</td> </tr> <tr> <td>[email protected]</td> <td>user2(SatoK)</td> </tr> </tbody> </table></div> <h2 id="複数ユーザーアカウントの表示名一括変更(CSVインポート)"><a href="#%E8%A4%87%E6%95%B0%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%81%AE%E8%A1%A8%E7%A4%BA%E5%90%8D%E4%B8%80%E6%8B%AC%E5%A4%89%E6%9B%B4%28CSV%E3%82%A4%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%88%29">複数ユーザーアカウントの表示名一括変更(CSVインポート)</a></h2> <p>インポートするCSVファイル(NewAccounts.csv)がC:\M365tools\2023にある場合の例</p> <pre><code class="PowerShell">> Import-Csv -Path "C:\M365tools\2023\ChangeDN.csv" | foreach {Set-MsolUser -DisplayName $_.DisplayName -UserPrincipalName $_.UserPrincipalName} </code></pre> arohajiro tag:crieit.net,2005:PublicArticle/18400 2023-03-07T14:11:02+09:00 2023-03-07T15:04:58+09:00 https://crieit.net/posts/M365-PowerShell 【M365】PowerShellで複数のユーザーアカウントを作成する <p>初めてPowerShellで操作する場合は、<a href="https://crieit.net/posts/PowerShell-M365-Teams">こちら</a>を参考にM365の管理モジュールをインストールすること</p> <h2 id="Azure Active Directory への接続を開始"><a href="#Azure+Active+Directory+%E3%81%B8%E3%81%AE%E6%8E%A5%E7%B6%9A%E3%82%92%E9%96%8B%E5%A7%8B">Azure Active Directory への接続を開始</a></h2> <pre><code class="PowerShell">> Connect-MsolService </code></pre> <h2 id="ライセンスの状態を確認"><a href="#%E3%83%A9%E3%82%A4%E3%82%BB%E3%83%B3%E3%82%B9%E3%81%AE%E7%8A%B6%E6%85%8B%E3%82%92%E7%A2%BA%E8%AA%8D">ライセンスの状態を確認</a></h2> <p>ユーザーに割り当てるライセンスのID(以下CSVのAccountSkuId)を確認し、インポートするCSVに入力する</p> <pre><code class="PowerShell">> Get-MsolAccountSku </code></pre> <h2 id="新規ユーザーの一括作成に使用するCSVの作成例"><a href="#%E6%96%B0%E8%A6%8F%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%81%AE%E4%B8%80%E6%8B%AC%E4%BD%9C%E6%88%90%E3%81%AB%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8BCSV%E3%81%AE%E4%BD%9C%E6%88%90%E4%BE%8B">新規ユーザーの一括作成に使用するCSVの作成例</a></h2> <div class="table-responsive"><table> <thead> <tr> <th>UserPrincipalName</th> <th>FirstName</th> <th>LastName</th> <th>DisplayName</th> <th>Password</th> <th>UsageLocation</th> <th>AccountSkuId</th> </tr> </thead> <tbody> <tr> <td>[email protected]</td> <td>Taro</td> <td>Abe</td> <td>user1(Abe)</td> <td>hogepassword1</td> <td>JP</td> <td>hoge:O365_BUSINESS_PREMIUM</td> </tr> <tr> <td>[email protected]</td> <td>Ken</td> <td>Sato</td> <td>user2(Sato)</td> <td>hogepassword2</td> <td>JP</td> <td>hoge:O365_BUSINESS_PREMIUM</td> </tr> </tbody> </table></div> <h2 id="新規ユーザーの一括作成(CSVインポート)"><a href="#%E6%96%B0%E8%A6%8F%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%81%AE%E4%B8%80%E6%8B%AC%E4%BD%9C%E6%88%90%28CSV%E3%82%A4%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%88%29">新規ユーザーの一括作成(CSVインポート)</a></h2> <p>インポートするCSVファイル(NewAccounts.csv)がC:\M365tools\2023にある場合の例</p> <pre><code class="PowerShell">> Import-Csv -Path "C:\M365tools\2023\NewAccounts.csv" | foreach {New-MsolUser -DisplayName $_.DisplayName -FirstName $_.FirstName -LastName $_.LastName -Password $_.Password -UserPrincipalName $_.UserPrincipalName -UsageLocation $_.UsageLocation -LicenseAssignment $_.AccountSkuId} | Export-Csv -Path "C:\M365tools\2023\NewAccountResults.csv" </code></pre> arohajiro tag:crieit.net,2005:PublicArticle/18390 2023-02-20T07:35:41+09:00 2023-02-20T07:35:41+09:00 https://crieit.net/posts/PowerShell-Powershell バッチファイルからPowerShellを呼び出し、Powershellコマンドでリネーム <p><strong>■バッチファイル側</strong><br /> ps_call.bat</p> <pre><code>@echo off echo PowerShell呼び出し中です。 powershell -NoProfile -ExecutionPolicy Unrestricted .\rename.ps1 echo 完了しました pause > nul exit </code></pre> <p><strong>■powershell側</strong><br /> rename.ps1</p> <pre><code>Set-Location C:\XXXXXX Get-ChildItem *.txt| Rename-Item -NewName {$_.Name -replace '^([0-9])月','0$1月'} Get-ChildItem *.txt| Rename-Item -NewName {$_.Name -replace '月([0-9])日','月0$1日'} Get-ChildItem *.txt| Rename-Item -NewName {$_.Name -replace '([0-9][0-9])月([0-9][0-9])日','$1$2'} </code></pre> speasmen88 tag:crieit.net,2005:PublicArticle/18387 2023-02-14T12:41:19+09:00 2023-02-15T00:14:13+09:00 https://crieit.net/posts/PowerShell-63eb02df4b3e2 PowerShell で中身がランダムな大きめのファイルを高速に作る <p>色々テストをしていると、ユニークなランダムの中身で、任意サイズのファイルが欲しくなることがままある。</p> <p>デバイスを全てファイルとして扱う Unix や Linux なら、 <code>/dev/urandom</code> 擬似デバイスを使って <code>dd if=/dev/urandom of=out.bin bs=1M count=64</code> みたいに任意サイズでファイルをコピーして作れば良い。</p> <p>しかし API ベースの Windows だとそういうわけにいかないので、何らかのスクリプトから API を呼んでやる必要がある。</p> <p>呼ばせるスクリプトは、 Windows に標準で搭載されていて追加でインストール不要な、毎度おなじみ PowerShell が良いだろう。<br /> 作りたいランダムなファイルのサイズが小さければ、 PowerShell 上で以下のような雑なコードを実行すればよい。</p> <pre><code class="powershell">$Size = 16KB; [byte[]]$bin = &{foreach($i in 1..$Size) { Get-Random -Maximum 255 <span>}</span><span>}</span>; [System.IO.File]::WriteAllBytes((Join-Path (Get-Location) .\out.bin), $bin); </code></pre> <p>しかし、 MiB オーダーを越えてくると、上記のコードでは速度が実用的ではなくなる。</p> <p>と言うことで、出力速度も気にしながら、大きめなランダムなファイルを作成するコードを考えてみる。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <pre><code class="powershell">function Make-RandomFile ([Parameter(Mandatory=$true)][string]$OutPath, [Parameter(Mandatory=$true)][long]$FileSize, [int]$ChunkSize = 16MB) { $bin1 = [byte[]]::new($ChunkSize); $bin2 = [byte[]]::new($ChunkSize); $r = [System.Random]::new(); [long]$progress = 0; [int]$nextLen = 0; $fs = [System.IO.File]::Create([System.IO.Path]::Combine($PWD, $OutPath)); try { do { $task = $fs.WriteAsync($bin1, 0, $nextLen); $r.NextBytes($bin2); $task.Wait(); $bin1, $bin2 = $bin2, $bin1; $progress += $nextLen; $nextLen = [System.Math]::Min([long]$ChunkSize, $FileSize - $progress); } while ($nextLen -gt 0); } finally { $fs.Dispose(); } } </code></pre> <p>上記のような関数をコンソールに貼り付けて定義し、あとは以下のように実行すれば良い。</p> <pre><code class="powershell">Make-RandomFile .\out.bin -FileSize 1GB; </code></pre> <h2 id="解説"><a href="#%E8%A7%A3%E8%AA%AC">解説</a></h2> <p>大きなファイルを作る際、最初に挙げたような雑なコードだと、一旦全てのデータをメモリ (RAM) に置いてしまうため、メモリ不足になったり動作が遅くなったりしかねない。<br /> そこで、適当なチャンクサイズ (上記コードだと標準では 16MiB) 毎にランダムなデータを作って、それを書き込んでいる。</p> <p>また、ランダムな配列を作るのも、その配列をファイルに書き込むのも、それぞれ時間がかかるため、それらを同期的に逐次実行すると効率が悪い。<br /> そこでバッファを2つ用意して、片方のバッファでファイルを非同期的に書き込んでいる間に、もう一方のバッファでランダムなデータを生成することで、効率を上げている。</p> <h2 id="補足"><a href="#%E8%A3%9C%E8%B6%B3">補足</a></h2> <p>ちなみに、 .NET 5 以前の <a target="_blank" rel="nofollow noopener" href="https://learn.microsoft.com/ja-jp/dotnet/api/system.random">System.Random</a> の擬似乱数生成器は、あまり乱数精度が高くないと言われているので、セキュリティ的に精度の高いランダム性であることが重要な場合は、より乱数精度の高い別の API を使った方が良いかもしれない。</p> <p>とりあえず、ちょっとしたテストファイルでランダムなデータが欲しいだけなら、 System.Random で十分だと思うが。</p> <p>また、ストレージの速度が十分に速い状況でより速く実行させたいなら、 Windows 10/11 標準の Windows PowerShell 5.1 より <a target="_blank" rel="nofollow noopener" href="https://www.microsoft.com/store/apps/9MZ1SNWT0N5D">PowerShell 7.2 以降</a>で実行したほうが、圧倒的に速いはずだ。 <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p> <div class="footnotes" role="doc-endnotes"> <hr /> <ol> <li id="fn:1" role="doc-endnote"> <p>Windows PowerShell 5.1 では .NET Framework 4.8 が、 PowerShell 7.2 LTS では .NET 6 が、それぞれ使われているが、 その .NET 6 以降では、擬似乱数生成器が性能の良いものに切り替わった為 (<a target="_blank" rel="nofollow noopener" href="https://andantesoft.hatenablog.com/entry/2021/03/28/231632">.NET 6 (Preview) における System.Random の実装変更 - 屋根裏工房改</a>) <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p> </li> </ol> </div> advanceboy tag:crieit.net,2005:PublicArticle/18198 2022-05-26T20:41:56+09:00 2022-05-26T20:41:56+09:00 https://crieit.net/posts/aka-tool-batlauncher PowerShell:指定フォルダのbatを実行するLauncherを作ってみた <h1 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h1> <p>お仕事とかでテスト用batとか作ってしょっちゅうたたくけどいちいちコマンドプロンプトに打つのも面倒なので画面のはじっこにでもbat実行用のLauncherでも置いとけばいいかなと思って作ってみた。<br /> 最初は適当にexe作ろうと思ったけどたまにはPowerShellでForm作るの復習がてらやってみようってことで昼休みに作ってたらまんまと罠にはまって一旦諦め。<br /> 解決はしたものの悔しさと絶対覚えてられない自身があったので記事に残す。</p> <h1 id="やりたいこと"><a href="#%E3%82%84%E3%82%8A%E3%81%9F%E3%81%84%E3%81%93%E3%81%A8">やりたいこと</a></h1> <ul> <li>引数で渡されたPathにある.batファイルをそれぞれボタンコントロールにして一列に並べる。</li> <li>ボタンをクリックすると対応するbatを実行する</li> <li>後ろに隠れると不便だからTopMost</li> <li>batファイルがなければエラーメッセージでもだして終了</li> </ul> <h1 id="単純にFormの話"><a href="#%E5%8D%98%E7%B4%94%E3%81%ABForm%E3%81%AE%E8%A9%B1">単純にFormの話</a></h1> <p>最初のおまじないさえ知っておけばそんなに難しくない</p> <p>こういうの(アセンブリ)使うよって教えてあげて</p> <pre><code>Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing </code></pre> <p>Form(外側)作ってあげて</p> <pre><code>$Form = New-Object System.Windows.Forms.Form $Form.Text = "BatToolLauncher" $Form.Width = 180 $Form.Height = $BatList.Count*30 + 50 $Form.TopMost = $true </code></pre> <p>ボタンを配置する</p> <pre><code>$Button = New-Object System.Windows.Forms.Button $Button.Location = New-Object System.Drawing.Point(0,0) $Button.Top = $Button.Top + $i * 30 $Button.Size = New-Object System.Drawing.Size(160,30) $Button.Text = $x.BaseName $Form.Controls.Add($Button) </code></pre> <p>※$BatListはbatの一覧(Get-ChildItemの結果)<br /> ※$xは$BatListをforeachで回した時の一時変数</p> <h1 id="ボタンクリック時の挙動を設定する(今回の鬼門)"><a href="#%E3%83%9C%E3%82%BF%E3%83%B3%E3%82%AF%E3%83%AA%E3%83%83%E3%82%AF%E6%99%82%E3%81%AE%E6%8C%99%E5%8B%95%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B%28%E4%BB%8A%E5%9B%9E%E3%81%AE%E9%AC%BC%E9%96%80%29">ボタンクリック時の挙動を設定する(今回の鬼門)</a></h1> <p>ボタンクリック時の挙動を書くことだけは簡単。<br /> 「Add_イベント」の形で定義してあげればいい。クリックイベントなら下の{}の中に書けばいい。</p> <pre><code>$Button.Add_Click({}) </code></pre> <p>簡単。なはずだった。<br /> とにもかくにも失敗例↓</p> <pre><code>$Button.Add_Click({Start-Process -WindowStyle Hidden $x.FullName}) </code></pre> <p>これは$x.FullNameでbatのフルパスが入るからそいつをStart-Processするだけ簡単だよね、と思って頭を抱えたもの。<br /> 何が起こるかというと、実際のクリック時の$xを参照してくれる。foreachでぐるぐる回していたので、$xが指すのは最後のbat。つまり、どのボタンを押しても最後のbatファイルが実行される。<br /> というわけで半日かけて試行錯誤・調査して解決策↓</p> <pre><code>$Button.Add_Click({Start-Process -WindowStyle Hidden $x.FullName}.GetNewClosure()) </code></pre> <p>GetNewClosure()<br /> を書くことでこの{}で囲まれた部分はスクリプトブロックじゃなくてクロージャだよ、となるらしい。今回の話に沿わせるとこの行を書いた時点の変数の内容でFixしてくれるらしい。<br /> ちょっと説明の言葉が怪しい自覚はある。</p> <h1 id="スクリプト全体"><a href="#%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E5%85%A8%E4%BD%93">スクリプト全体</a></h1> <p>結果的にこんな感じ。</p> <pre><code>Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing if($args.Length -gt 0) { $TargetDir = $args[0] } else { $MyPath = $MyInvocation.MyCommand.Path $TargetDir = Split-Path -Parent $MyPath } # batファイルのリスト作成 $BatList = Get-ChildItem $TargetDir -Filter "*.bat" if($BatList -eq $null) { [System.Windows.Forms.MessageBox]::Show("no bat files", "Error") return } # Form作成 $Form = New-Object System.Windows.Forms.Form $Form.Text = "BatToolLauncher" $Form.Width = 180 $Form.Height = $BatList.Count*30 + 50 $Form.TopMost = $true # ボタン作成 $i = 0 foreach($x in $BatList) { $Button = New-Object System.Windows.Forms.Button $Button.Location = New-Object System.Drawing.Point(0,0) $Button.Top = $Button.Top + $i * 30 $Button.Size = New-Object System.Drawing.Size(160,30) $Button.Text = $x.BaseName # GetNewClosure様をたたえる! $Button.Add_Click({Start-Process -WindowStyle Hidden $x.FullName}.GetNewClosure()) $Form.Controls.Add($Button) $i++ } # 表示 $Form.ShowDialog() </code></pre> <h1 id="おまけ"><a href="#%E3%81%8A%E3%81%BE%E3%81%91">おまけ</a></h1> <p>ps1ファイルってそのままたたけないのでいつも汎用性持たせて作ってるbat</p> <pre><code>powershell -ExecutionPolicy RemoteSigned %~dpn0.ps1 %* </code></pre> <p>同じ場所にある同名ps1を実行するbat<br /> いつもの使いまわしファイルがどこかにいったので久しぶりに手書きしたけどだいたいこんなだったはず。</p> RedCol tag:crieit.net,2005:PublicArticle/18165 2022-04-11T12:19:26+09:00 2022-04-11T12:19:52+09:00 https://crieit.net/posts/Teams Teamsの管理コマンド備忘メモ <h2 id="チームの作成"><a href="#%E3%83%81%E3%83%BC%E3%83%A0%E3%81%AE%E4%BD%9C%E6%88%90">チームの作成</a></h2> <pre><code class="PowerShell">> New-Team -DisplayName "表示名" -MailNickName "MailNickName属性値" -Owner "所有者のUPN" -Visibility "public or private" -Description "説明" </code></pre> <h2 id="チームの一括ユーザー登録"><a href="#%E3%83%81%E3%83%BC%E3%83%A0%E3%81%AE%E4%B8%80%E6%8B%AC%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E7%99%BB%E9%8C%B2">チームの一括ユーザー登録</a></h2> <pre><code class="PowerShell">> Import-Csv -Path ".\teamusers.csv" | foreach{Add-TeamUser -GroupId $_.grpID -user $_.userPN -Role $_.role} </code></pre> <h3 id="teamusers.csv (CSVの作成例)"><a href="#teamusers.csv+%28CSV%E3%81%AE%E4%BD%9C%E6%88%90%E4%BE%8B%29">teamusers.csv (CSVの作成例)</a></h3> <p>grpIDはGet-Teamで確認したチームのGroupIdを入れる<br /> userPNはユーザーのメールアドレスを入れる<br /> roleは所有者ならownerでメンバーはmember</p> <div class="table-responsive"><table> <thead> <tr> <th>grpID</th> <th>userPN</th> <th>role</th> </tr> </thead> <tbody> <tr> <td>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</td> <td>[email protected]</td> <td>member</td> </tr> <tr> <td>xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</td> <td>[email protected]</td> <td>owner</td> </tr> </tbody> </table></div> <h2 id="チャンネルの作成"><a href="#%E3%83%81%E3%83%A3%E3%83%B3%E3%83%8D%E3%83%AB%E3%81%AE%E4%BD%9C%E6%88%90">チャンネルの作成</a></h2> <p>標準チャンネルで作成されます</p> <pre><code class="PowerShell">> New-TeamChannel -GroupId "Get-Teamで確認したチームのGroupId" -DisplayName "表示名" </code></pre> arohajiro tag:crieit.net,2005:PublicArticle/18164 2022-04-11T11:41:03+09:00 2022-11-10T10:10:37+09:00 https://crieit.net/posts/PowerShell-M365-Teams PowerShellにM365やTeamsの管理モジュールをインストールする <p>M365のユーザーアカウントやTeamsを管理するコマンドを実行するために、事前にインストールが必要</p> <h2 id="AzureADモジュールのインストール"><a href="#AzureAD%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">AzureADモジュールのインストール</a></h2> <pre><code class="PowerShell">> Install-Module -Name AzureAD </code></pre> <h2 id="M365モジュールのインストール"><a href="#M365%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">M365モジュールのインストール</a></h2> <pre><code class="PowerShell">> Install-Module -Name MSOnline </code></pre> <h2 id="Microsoft Teamsモジュールのインストール"><a href="#Microsoft+Teams%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">Microsoft Teamsモジュールのインストール</a></h2> <pre><code class="PowerShell">> Install-Module -Name MicrosoftTeams </code></pre> <h3 id="Microsoft Teamsに接続"><a href="#Microsoft+Teams%E3%81%AB%E6%8E%A5%E7%B6%9A">Microsoft Teamsに接続</a></h3> <p>コマンド実行後に、ブラウザが起動してM365のサインイン画面が出てきますので、管理権限のあるユーザーでサインインします。</p> <pre><code class="PowerShell">> Connect-MicrosoftTeams </code></pre> <h4 id="チーム一覧を出力"><a href="#%E3%83%81%E3%83%BC%E3%83%A0%E4%B8%80%E8%A6%A7%E3%82%92%E5%87%BA%E5%8A%9B">チーム一覧を出力</a></h4> <pre><code class="PowerShell">> Get-Team </code></pre> arohajiro tag:crieit.net,2005:PublicArticle/18163 2022-04-11T11:27:58+09:00 2022-04-11T11:28:39+09:00 https://crieit.net/posts/macOS-PowerShell macOSにPowerShellをインストールする <p>MacでM365やTeamsをコマンドで管理する時に便利</p> <h2 id="PowerShellインストール"><a href="#PowerShell%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">PowerShellインストール</a></h2> <pre><code class="bash">$ brew install --cask powershell </code></pre> <h2 id="PowerShellの起動"><a href="#PowerShell%E3%81%AE%E8%B5%B7%E5%8B%95">PowerShellの起動</a></h2> <pre><code class="bash">$ pwsh </code></pre> arohajiro tag:crieit.net,2005:PublicArticle/17958 2022-01-28T18:32:09+09:00 2022-01-28T18:32:09+09:00 https://crieit.net/posts/aka-win-sysconf-1 OSの設定をPowershellで変える(言語編) <h1 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h1> <p>多国語の対応とかしてると確認のためにいちいちシステム言語を切り替えて云々が発生してうっとおしいのでバッチにしてしまえ、たぶんPowerShellでできるはずだ、ということで調べた結果を垂れ流す。<br /> なお、システム設定いじくるので管理者として実行してあげる必要がある。<br /> それも面倒なので権限昇格するようにバッチで行う。(そのうち別記事で書く)</p> <h1 id="サポート言語の追加"><a href="#%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88%E8%A8%80%E8%AA%9E%E3%81%AE%E8%BF%BD%E5%8A%A0">サポート言語の追加</a></h1> <p>[Windows設定]→[時刻と言語]→[言語]のページの下の方の[言語の追加]。</p> <h2 id="Get-WinUserLanguageList/Set-WinUserLanguageList"><a href="#Get-WinUserLanguageList%2FSet-WinUserLanguageList">Get-WinUserLanguageList/Set-WinUserLanguageList</a></h2> <p>Get~で現在のサポート言語のリストをとってくる<br /> Set~でサポート言語リストをセットする<br /> ので<br /> 1.Getする<br /> 2.追加する(言語コードをAdd)<br /> 3.Setする<br /> でいける</p> <p>例えばドイツ語なら</p> <pre><code>$CurrList = Get-WinUserLanguageList $CurrList.Add("de-DE") Set-WinUserLanguageList $CurrList -Force </code></pre> <p>-Forceは自動化したいから。ないと確認メッセージが出ます。</p> <h1 id="地域設定(形式)"><a href="#%E5%9C%B0%E5%9F%9F%E8%A8%AD%E5%AE%9A%28%E5%BD%A2%E5%BC%8F%29">地域設定(形式)</a></h1> <p>[Windows設定]→[時刻と言語]→[地域]のページの2こめくらい。</p> <h2 id="Get-Culture/Set-Culture"><a href="#Get-Culture%2FSet-Culture">Get-Culture/Set-Culture</a></h2> <p>ただ言語デフォルトに変更したいだけならSet-Cultureで言語コードを渡せばいい。<br /> だけど言語デフォルトでありがちな罠の小数点(たまにカンマの国がある)を変更したいなら<br /> 1.Set 言語デフォルト<br /> 2.Get<br /> 3.編集<br /> 4.Set 編集したの<br /> という手順になるが、どうも1の直後に2を呼ぶと反映されてなかったのでps1ファイルを分けた。</p> <pre><code># ChangeCulture_1.ps1 Set-Culture "de-DE" </code></pre> <pre><code># ChangeCulture_2.ps1 $Cult = Get-Culture $Cult.NumberFormat.NumberDecimalSeparator = "." Set-Culture $Cult </code></pre> <h1 id="システムロケール"><a href="#%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%83%AD%E3%82%B1%E3%83%BC%E3%83%AB">システムロケール</a></h1> <p>[Windows設定]→[時刻と言語]→[地域]→[関連設定:日付、時刻、地域の追加設定]→[地域]→[管理]タブ→[Unicode対応ではないプログラムの言語]<br /> ※遠い、正直コントロールパネル出した方が早く着く</p> <h2 id="Get-WinSystemLocale/Set-WinSystemLocale"><a href="#Get-WinSystemLocale%2FSet-WinSystemLocale">Get-WinSystemLocale/Set-WinSystemLocale</a></h2> <p>確認したいならGetで。Setで言語コードを入れれば終わり。</p> <pre><code>Set-WinSystemLocale "de-DE" </code></pre> <p>再起動は必須なのでお好みに合わせて<br /> Restart-System<br /> をスクリプトに含めるなり、そこは手でやるなりご自由に。</p> <p>カンタン!</p> RedCol tag:crieit.net,2005:PublicArticle/17892 2021-12-24T23:56:32+09:00 2021-12-24T23:56:32+09:00 https://crieit.net/posts/oneliner-open-windows-hosts-20211224 Windows の hosts ファイルを管理者権限のメモ帳で開くワンライナー <h2 id="経緯"><a href="#%E7%B5%8C%E7%B7%AF">経緯</a></h2> <p><code>hosts</code> を毎度毎度手動で開く (管理者権限でメモ帳を開いて、メモ帳から <code>%windows%\system32\drivers\etc\hosts</code> を開く) のは面倒になってきたので、ワンライナーを作ることにしました。</p> <h2 id="コード"><a href="#%E3%82%B3%E3%83%BC%E3%83%89">コード</a></h2> <pre><code class="bat">cd /d %~dp0 PowerShell -NoProfile -ExecutionPolicy unrestricted -Command "start notepad C:\Windows\System32\drivers\etc\hosts -verb runas" </code></pre> <p>最初の一行はなくても良いので実質ワンライナー。</p> <p>これを bat ファイルとして保存すれば、ダブルクリックで <code>hosts</code> を開くことができます。</p> <p>……ただ、 bat はスタート画面にピン止めすることはできないのですね。</p> <h2 id="スタート画面にピン止め"><a href="#%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%88%E7%94%BB%E9%9D%A2%E3%81%AB%E3%83%94%E3%83%B3%E6%AD%A2%E3%82%81">スタート画面にピン止め</a></h2> <p>bat そのものはピン止めできませんが、 <code>cmd.exe</code> (コマンドプロンプト) はできます。</p> <p>そこで、ショートカットで「コマンドプロンプトを開きつつ、実行するコマンドとして上述ワンライナーを指定する」ことでピン止めすることにしました。</p> <ol> <li>bat ファイルのショートカットを作成</li> <li>1.のショートカットを右クリック→「プロパティ」でプロパティ画面を開く</li> <li>ショートカットのリンク先の先頭に、 <code>cmd /c</code>を挿入する</li> </ol> <p>これで、上述の「コマンドプロンプトを開きつつ、実行するコマンドとして上述ワンライナーを指定する」を実現しました。</p> <p>これでスタート画面にもピン止めできて、ダブルクリックで <code>hosts</code> を開くことができるようになりました。便利。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <h3 id="hosts を開く"><a href="#hosts+%E3%82%92%E9%96%8B%E3%81%8F">hosts を開く</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/tiibun/items/5719869552ee5b713d83">管理者権限でメモ帳を開いてhostsを編集するコマンド - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://qiita.com/syui/items/7d42d7e691010ee212fb">WindowsのPowerShellを管理者権限で起動する方法 - Qiita</a></li> <li><a target="_blank" rel="nofollow noopener" href="https://i-think-it.net/windows10-notepad-administrator-open/">Windows10でメモ帳を管理者権限で一瞬で開く方法 | あいしんくいっと</a></li> </ul> <h3 id="ピン止め"><a href="#%E3%83%94%E3%83%B3%E6%AD%A2%E3%82%81">ピン止め</a></h3> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://donadona.hatenablog.jp/entry/2016/08/22/182751">Windows10でバッチファイルをスタート画面にピン留めする - ドナドナされるプログラマのメモ</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/17635 2021-09-05T21:34:35+09:00 2021-09-05T21:41:26+09:00 https://crieit.net/posts/PowerShell-6134b95b1325f PowerShellで正規表現で対象行を抽出する方法 <p>PowerShellで特定の行を抽出する方法</p> <ul> <li>小数点以下n桁以上の値を持つ行を出力するコマンド</li> </ul> <pre><code>$re ='[0-9]\.[0-9]{n,}' select-string -path .\powershell.csv -pattern $re </code></pre> <p>出力結果は下記のようになる。<br /> powershell.csv:8:1.0 110.0 10.0 1.899999999<br /> powershell.csv:11:1.0 110.0 10.0 1.899999999</p> <hr /> <ul> <li>出力結果に含まれるファイル名部分を削除して、抽出した行のみ出力する方法</li> </ul> <pre><code>select-string powershell.csv -pattern $re | foreach-object { $($_ -split ":" )[3] } </code></pre> <p>出力結果は下記のようになる。<br /> 1.0 110.0 10.0 1.899999999<br /> 1.0 110.0 10.0 1.899999999</p> ao-iro tag:crieit.net,2005:PublicArticle/16690 2021-02-19T02:27:51+09:00 2021-02-19T02:27:51+09:00 https://crieit.net/posts/PowerShell-602ea39702454 PowerShell でインストール済プログラム一覧を取得する <p>PowerShell を Windows の管理ツールとしてみたときに、割と昔から意味がわからないと思っていることが 1点 ある。<br /> <strong>Windows のインストール済みプログラム一覧を取得するコマンドがない</strong>ことだ。</p> <p>もう少し具体的に言うと、<code>appwiz.cpl</code> で開ける、コントロールパネルのプログラムと機能の一覧や、 設定の「アプリと機能」の一覧を取得するコマンドレットがない。<br /> コマンドレットがないだけじゃなく、コードを書いて取得する確立して手段すら存在しないのだ。</p> <p>とりあえず、それに近い情報がとれる代替方法として、レジストリのアンインストーラー一覧を取得する方法が知られている。<br /> ただ、ググってもあまり PowerShell らしい綺麗 (?) なコードで書かれているサンプルが少なかったので、ワンライナーで書いてみる。</p> <pre><code class="powershell">Get-ChildItem -LiteralPath (('HKLM:', 'HKCU:' | %{ "${_}\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" }) + 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall') | Get-ItemProperty -Name DisplayName, DisplayVersion, Publisher, InstallDate -ErrorAction Ignore; </code></pre> <p>これで取得した内容を <code>| Sort-Object DisplayName, DisplayVersion</code> や <code>| Select-Object DisplayName, DisplayVersion</code> などにパイプしてから使うと良いだろう。</p> <hr /> <p>ちなみに WMI の Win32_Product クラスを使って取得する方法 (<code>Get-CimInstance -ClassName Win32_Product</code>) は、</p> <ul> <li>MSI でインストールしたアプリしか取得できない</li> <li>コンピューターはMSIがインストールされているすべてのアプリケーションを再検証するので、負荷がかかるし遅い</li> </ul> <p>と言うことから使うべきではないとされている。<br /> <a target="_blank" rel="nofollow noopener" href="https://community.idera.com/database-tools/powershell/ask_the_experts/f/powershell_for_windows-12/22674/get-wmiobject--class-win32_product-doesn-t-list-all-installed-applications">Get-WmiObject -class win32_product doesn't list all installed Applications - PowerShell for Windows - Ask the Experts - IDERA Community</a></p> advanceboy tag:crieit.net,2005:PublicArticle/16582 2021-01-12T23:48:54+09:00 2021-01-12T23:48:54+09:00 https://crieit.net/posts/powershell-access-key-and-value-in-psobject-20210112 (PowerShell) PSCustomObject をループさせつつキーと値の両方にアクセスする <p>引き続き PowerShell のお話。今度は PSCustomObject をループさせつつキーと値の両方にアクセスする方法が分からず躓きました。</p> <h2 id="現象"><a href="#%E7%8F%BE%E8%B1%A1">現象</a></h2> <pre><code class="json">{ "common": { "config": { "user.name": "testuser", "user.id": 12345, "user.email": "[email protected]" } } } </code></pre> <p>例えばこんな <code>config.json</code> があるとします。</p> <p>この JSONファイル を読み込んでループさせつつ、キーと値の双方にアクセスしたい。</p> <p>そこで <code>foreach($confgi in $configData.GetEnumerator()){}</code> としたり、 <code>foreach($confgi in $configData.Keys){}</code> としたりしましたが一向に中身が取得できず四苦八苦。</p> <h2 id="対処"><a href="#%E5%AF%BE%E5%87%A6">対処</a></h2> <pre><code class="powershell"># JSONファイル 読み込み [String]$configPath = Join-Path ( Convert-Path . ) 'config.json' $configData = Get-Content -Path $configPath -Raw -Encoding UTF8 | ConvertFrom-JSON # ループ foreach( $key in $configData.common.config.psobject.properties.name ) { Write-Host $key ' = ' $configData.common.config.$key } </code></pre> <p>最終的に、 <code>$Object.psobject.properties.name</code> という全然異なるアクセス方法だったことが分かりました。今回の場合はキーが階層で入り組んでいるのでさらに <code>.</code> (ピリオド(ドット)) の個数が増えています。</p> <pre><code class="bash">> PowerShell -ExecutionPolicy RemoteSigned .\test.ps1 user.name = testuser user.id = 12345 user.email = [email protected] </code></pre> <p>やっとキーと値の両方が取得できました……。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://docs.microsoft.com/ja-jp/powershell/scripting/learn/deep-dives/everything-about-pscustomobject?view=powershell-7.1">PSCustomObject について知りたかったことのすべて - PowerShell | Microsoft Docs</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/16579 2021-01-11T22:59:05+09:00 2021-01-11T22:59:05+09:00 https://crieit.net/posts/powershell-access-key-with-piriod-in-psobject-20210111 (PowerShell) PSCustomObject のピリオド(ドット)入りのキー名にアクセスする <p>Powershell で JSON から読み込んだオブジェクトにピリオド(ドット)入りのキー名があり、これにどうやってアクセスしようか悩みました。</p> <h2 id="現象"><a href="#%E7%8F%BE%E8%B1%A1">現象</a></h2> <pre><code class="json">{ "user": { "user.name": "testuser", "user.email": "[email protected]" } } </code></pre> <p>例えばこんな <code>config.json</code> があるとします。</p> <p>この JSONファイル を読み込み、ユーザの入力を受け付けて設定を上書きするスクリプトを書きました。</p> <pre><code class="powershell"># JSONファイル 読み込み [String]$configPath = Join-Path ( Convert-Path . ) 'config.json' $configData = Get-Content -Path $configPath -Raw -Encoding UTF8 | ConvertFrom-JSON # 一度出力 Write-Host $configData.user['user.name'] # 入力受付 [String]$username = Read-Host 'ユーザー名 を入力してください (半角英数字と一部記号 (-, _) )。' if (-not ($username -match "^[a-zA-z0-9\-_]+$") ) { Write-Host 'ERROR: ユーザ名 に使用できない文字が含まれています。' -BackgroundColor DarkRed Write-Host `r`n exit } $configData.user['user.name'] = $username # 再度出力 Write-Host $configData.user['user.name'] </code></pre> <p>ここで問題となったのが <code>user.name</code> という <code>.</code> (ピリオド(ドット))入りのキー名。オブジェクトで <code>.</code> でキー名を連結できるのが JavaScript に似ていたので、つい JavaScript のように <code>$configData.user['user.name']</code> と書いてしましました。</p> <p>すると</p> <pre><code class="bash">> PowerShell -ExecutionPolicy RemoteSigned .\test.ps1 ユーザー名 を入力してください (半角英数字と一部記号 (-, _) )。: aaa 型 System.Management.Automation.PSObject のオブジェクトにインデックスを付けることはできません。 発生場所 PATH\TO\PROJECT\test.ps1:16 文字:1 + $configData.user['user.name'] = $username + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) []、RuntimeException + FullyQualifiedErrorId : CannotIndex </code></pre> <p>怒られました。しかも、初出の位置ではなく変数で上書きする部分で。初出の <code>Write-Host</code> による標準出力はブランクを出力してスルーされたようです。</p> <p>以上より、</p> <ul> <li>変数の値を読み込む場合(出力など): 存在しないキーとしてブランク出力してそのままスルー</li> <li>変数の値を書き込む場合: エラー</li> </ul> <p>となるようです。困った。</p> <h2 id="対処"><a href="#%E5%AF%BE%E5%87%A6">対処</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://docs.microsoft.com/ja-jp/powershell/scripting/learn/deep-dives/everything-about-pscustomobject?view=powershell-7.1">PSCustomObject について知りたかったことのすべて - PowerShell | Microsoft Docs</a></li> </ul> <blockquote> <p>プロパティ名に文字列を使用しても、やはり機能します。</p> <p><code>$myObject.'Name'</code></p> </blockquote> <p>なん……だと……。</p> <p>ということで上記スクリプトを書き換えます。</p> <pre><code class="powershell"># JSONファイル 読み込み [String]$configPath = Join-Path ( Convert-Path . ) 'config.json' $configData = Get-Content -Path $configPath -Raw -Encoding UTF8 | ConvertFrom-JSON # 一度出力 Write-Host $configData.user.'user.name' # 入力受付 [String]$username = Read-Host 'ユーザー名 を入力してください (半角英数字と一部記号 (-, _) )。' if (-not ($username -match "^[a-zA-z0-9\-_]+$") ) { Write-Host 'ERROR: ユーザ名 に使用できない文字が含まれています。' -BackgroundColor DarkRed Write-Host `r`n exit } $configData.user.'user.name' = $username # 再度出力 Write-Host $configData.user.'user.name' </code></pre> <p>肝は <code>$configData.user.'user.name'</code> 。 <code>$configData.user['user.name']</code> の角括弧 (<code>[]</code>)を外してキー名の文字列をそのまま <code>.</code> (ピリオド(ドット)) で繋げました。</p> <pre><code class="bash">> PowerShell -ExecutionPolicy RemoteSigned .\test.ps1 testuser ユーザー名 を入力してください (半角英数字と一部記号 (-, _) )。: abc abc </code></pre> <p>上手く行きました。</p> <h2 id="参考"><a href="#%E5%8F%82%E8%80%83">参考</a></h2> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://docs.microsoft.com/ja-jp/powershell/scripting/learn/deep-dives/everything-about-pscustomobject?view=powershell-7.1">PSCustomObject について知りたかったことのすべて - PowerShell | Microsoft Docs</a></li> </ul> arm-band tag:crieit.net,2005:PublicArticle/16315 2020-12-09T02:33:59+09:00 2020-12-09T12:41:39+09:00 https://crieit.net/posts/NET-Interactive-C-PowerShell .NET Interactive で C# と PowerShell を股に掛ける <p>この記事は、「<a target="_blank" rel="nofollow noopener" href="https://qiita.com/advent-calendar/2020/csharplang">C# Advent Calendar 2020</a>」 9日目の記事、かつ 「<a target="_blank" rel="nofollow noopener" href="https://qiita.com/advent-calendar/2020/powershell">PowerShell Advent Calendar 2020</a>」 9日目の記事だ。</p> <p>スミマセン。横着した。</p> <p>この時期になれば .NET Interactive の Preview 4 が出てるだろうしその記事でも書こうかな~ などと目論んでいたものの、 残念ながら登場しなかった。</p> <p>このため、 .NET Interactive .NET 5 対応版の導入と、 Variable sharing の内容でお茶を濁そうと思う。</p> <p>.NET Interactive のなんたるかについては、以前の以下の記事で取り扱っているので、そちらを参照願いたい。<br /> <a target="_blank" rel="nofollow noopener" href="https://aquasoftware.net/blog/?p=1379">.NET Interactive の C# REPL を Jupyter で | Aqua Ware つぶやきブログ</a></p> <h2 id=".NET Interactive を .NET 5 で動かしてみる"><a href="#.NET+Interactive+%E3%82%92+.NET+5+%E3%81%A7%E5%8B%95%E3%81%8B%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B">.NET Interactive を .NET 5 で動かしてみる</a></h2> <p>いきなり本題とは関係ない話になるが、 せっかく .NET 5 がリリースされたところなので、 .NET Interactive を .NET 5 で動かしてみよう。</p> <p>12/8 現在、NuGet で公式にリリースされている .NET Interactive は、 9月にリリースされた <a target="_blank" rel="nofollow noopener" href="https://devblogs.microsoft.com/dotnet/net-interactive-preview-3-vs-code-insiders-and-polyglot-notebooks/">Preview 3</a> に少し更新が加えられた、 <a target="_blank" rel="nofollow noopener" href="https://www.nuget.org/packages/Microsoft.dotnet-interactive/1.0.155302">1.0.155302</a> だ。<br /> これは、 .NET Core 3.1 上に構築されている。</p> <p>一方で 11/27 頃のビルド (<a target="_blank" rel="nofollow noopener" href="https://github.com/dotnet/interactive/commit/61d231529ce8657172c65b389e3524ae72cd30b7">dotnet/interactive@61d2315</a>) あたりでは、既に .NET 5 対応が済まされている。</p> <p>更に、 何故か <a target="_blank" rel="nofollow noopener" href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode&ssr=false#version-history">Visual Studio Code の .NET Interactive Notebooks 拡張では、 .NET 5 に対応した 1.0.160204</a> がリリースされている。</p> <p>このため、 現時点で .NET Interactive で .NET 5 を扱う主な方法は、以下の通り。</p> <ul> <li><a target="_blank" rel="nofollow noopener" href="https://mybinder.org/v2/gh/dotnet/interactive/main?urlpath=lab"><img src="https://static.mybinder.org/badge_logo.svg" alt="launch binder" /></a> main ブランチの binder コンテナにアクセス</li> <li>Azure DevOps の CI ビルドの NuGet feed (<a target="_blank" rel="nofollow noopener" href="https://dev.azure.com/dnceng/public/_packaging?_a=feed&feed=dotnet-tools">dotnet-tools</a>) から、 <a target="_blank" rel="nofollow noopener" href="https://dev.azure.com/dnceng/public/_packaging?_a=package&feed=dotnet-tools&package=Microsoft.dotnet-interactive&protocolType=NuGet&version=1.0.160204&view=overview">Glogal Tool</a> を DL <ul> <li>実行例:<br /> <code>powershell dotnet tool install -g Microsoft.dotnet-interactive --version 1.0.160204 --add-source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json</code></li> </ul></li> <li>VS Code Insider を起動し、 <a target="_blank" rel="nofollow noopener" href="https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode&ssr=false#version-history">.NET Interactive Notebooks 拡張の Preview</a> をインストール</li> </ul> <p>残念ながら、 .NET Interactive インストール済み Jupyter の docker イメージのようなものは 公式では用意されておらず、 コンテナを使いたかったら自分でビルドするしかない。<br /> (そのせいで、 上記 binder の起動は遅い)</p> <p>vscode 拡張だと、 コード補完が ignore case で効くのでとても書きやすい。<br /> 以下、 vscode の .NET Interactive Notebooks 拡張の バージョン 1.0.160204 での実行を前提とするが、 他の環境でも同様に動作するはずだ。</p> <p>なお、 起動した Notebook やコンソールの .NET CLR のバージョンを知りたければ、 System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription を実行すれば良い。<br /> .NET 5.x.x と表示されれば、 .NET 5 で起動していることが確認できる。</p> <p><a href="https://crieit.now.sh/upload_images/601ee4e8bdda70c90dbb844c3f41b00a5fcfb8394c961.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/601ee4e8bdda70c90dbb844c3f41b00a5fcfb8394c961.png?mw=700" alt="dotnet-interactive-variable-sharing-00.png" /></a></p> <h2 id="Variable sharing とは"><a href="#Variable+sharing+%E3%81%A8%E3%81%AF">Variable sharing とは</a></h2> <p>さて、ようやく本題。</p> <p>.NET Interactive が面白いのは、 ひとつの Notebook 内で、 C#, PowerShell, F# の言語間の変数を共有できる点だ。<br /> 悪いなあ、 <a target="_blank" rel="nofollow noopener" href="https://docs.microsoft.com/dotnet/visual-basic/">VB.NET</a>。 このツールは3人用なんだ。</p> <p>この変数を共有できる機能を、 variable sharing という。</p> <p><a href="https://crieit.now.sh/upload_images/6c0880bb18fa55b7ca72b70edf5ea8c45fcfb85010ba9.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/6c0880bb18fa55b7ca72b70edf5ea8c45fcfb85010ba9.png?mw=700" alt="dotnet-interactive-variable-sharing-01.png" /></a></p> <p>使い方はとても簡単で、 <code>#!share --from</code> マジックコマンドで、 参照する .NET カーネルと変数名を指定するだけだ。</p> <p>但し、 読み込まれている型の空間は共有されない点には注意だ。<br /> 例えば、 C# で定義したクラスのインスタンスを PowerShell で読み込もうとしたり、<br /> PowerShell でロードされているモジュールで定義された型のインスタンスを C# で読もうとすると、エラーになってしまう。<br /> 詳しくは、 <a target="_blank" rel="nofollow noopener" href="https://github.com/dotnet/interactive/blob/main/docs/variable-sharing.md">interactive/variable-sharing.md · dotnet/interactive</a> のドキュメントを参照。</p> <p><a href="https://crieit.now.sh/upload_images/d2e56ff085416a06d06b9561ffd485035fcfb859aa4d5.png" target="_blank" rel="nofollow noopener"><img src="https://crieit.now.sh/upload_images/d2e56ff085416a06d06b9561ffd485035fcfb859aa4d5.png?mw=700" alt="dotnet-interactive-variable-sharing-02.png" /></a></p> <p>但し、 PowerShell でよく使われる <code>PSCustomObject</code> 型については特別対応が入っているらしく、いい感じに <code>Dictionary<string, object></code> に変換されて C# でも扱える。<br /> なにそれ。 PowerShell 内部でも欲しい機能なんだけど。</p> <h2 id="Variable sharing の使いどころ"><a href="#Variable+sharing+%E3%81%AE%E4%BD%BF%E3%81%84%E3%81%A9%E3%81%93%E3%82%8D">Variable sharing の使いどころ</a></h2> <p>例えば…<br /> 例えば……<br /> ………</p> <p>うーん、よい例が思いつかない…</p> <p>そもそも、 C# でできることはだいたい PowerShell でできるし、 その逆もまた然りなんだよな。<br /> 加えて PowerShell では、 <code>Add-Type</code> で手軽に C# のコードを埋め込めるし…</p> <p>強いて挙げるなら、 PowerShell は、付属のコマンドレットや追加モジュールによって、 I/O 操作やリソースの読み込みにが強い一方で、 パイプを交えたデータの処理はだいぶ遅いところだろうか。<br /> 例えば、 PowerShell で ファイルの読み込みなどを行いつつ、 大量のデータの処理は C# で行うといったような使い方はアリかもしれない。</p> <pre><code class="powershell">#!pwsh $data1 = Get-ChildItem *.json -Recurse | Get-Content -Raw | ConvertFrom-Json; $data2 = Get-Clipboard; </code></pre> <pre><code class="csharp">#!csharp #!share --from pwsh data1, data2 data1.Where(kv => kv.Key == "tools") </code></pre> <pre><code class="csharp">#!csharp data2.First() </code></pre> <p>この程度のデータ処理なら PowerShell で全く問題ないレベルだが、 処理するデータの数が増えてくれば C# で実行するメリットが出てくるだろう。</p> advanceboy tag:crieit.net,2005:PublicArticle/16109 2020-10-04T23:29:37+09:00 2020-10-04T23:44:28+09:00 https://crieit.net/posts/PowerShell-Git-reset-stash PowerShell で Git の reset/stash などを使う際に履歴を指定する方法 <p>本記事は、 Qrunch からの移転記事です。</p> <p>移転元: <a target="_blank" rel="nofollow noopener" href="https://morichan.qrunch.io/entries/gDTuDEwe0dme8qkN">PowerShell で Git の reset/stash などを使う際に履歴を指定する方法 - 雑木林</a></p> <h1 id="TL; DR"><a href="#TL%3B+DR">TL; DR</a></h1> <p>中括弧で表記する文字列は <code>'</code> で囲みましょう。</p> <h1 id="はじめに"><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a></h1> <p>みなさんおなじみ、履歴を戻すコマンドは、下記の通りです。</p> <p>gitで履歴を戻すコマンド(実行不可)</p> <pre><code class="powershell">git reset HEAD@{0} </code></pre> <p>また、スタッシュを適用するコマンドは、下記の通りです。</p> <p>gitでスタッシュを適用するコマンド(実行不可)</p> <pre><code class="powershell">git stash pop stash@{0} </code></pre> <p>しかし、 PowerShell で上記を実行しようとすると、次のエラーが発生し、履歴リセット/スタッシュ適用できません。</p> <p>PowerShellで履歴を戻そうとした際のエラー</p> <pre><code class="powershell">error: unknown switch `e' </code></pre> <h1 id="解決策"><a href="#%E8%A7%A3%E6%B1%BA%E7%AD%96">解決策</a></h1> <p>PoserShell では、中括弧に特別な意味があるため(スクリプトブロックと呼ぶらしい)、中括弧内の文字列をコマンドとして実行してしまうからみたいです。</p> <p><a target="_blank" rel="nofollow noopener" href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_script_blocks?view=powershell-7">about_Script_Blocks - PowerShell | Microsoft Docs</a></p> <p><code>'</code> で囲み文字列化することで、実行できます。</p> <p>gitで履歴を戻すコマンド(文字列化)</p> <pre><code class="powershell">git reset 'HEAD@{0}' </code></pre> <p>gitでスタッシュを適用するコマンド</p> <pre><code class="powershell">git stash pop 'stash@{0}' </code></pre> <p>または、中括弧エスケープ処理をすることでも可能です。<br /> PowerShell のエスケープシーケンスは <code>`</code> ですよね。</p> <p>gitで履歴を戻すコマンド(エスケープ処理)</p> <pre><code class="powershell">git reset HEAD@`{0`} </code></pre> <p><a target="_blank" rel="nofollow noopener" href="https://github.com/dahlbyk/posh-git/issues/106">git reset HEAD@{1} results in error · Issue #106 · dahlbyk/posh-git</a></p> <h1 id="おわりに"><a href="#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB">おわりに</a></h1> <p>エラー文だけでは理解できず、地味に苦労しました。<br /> PowerShell は <del>めんどくさい</del> 奥が深いですね。</p> Morichan