以下の内容はhttps://let.blog.jp/tag/PowerShellより取得しました。


PowerShell に特殊な null がある
PowerShell を使ってたときに奇妙な動きをするところがありました
null の動作が一部だけ違います

代入してない変数は null で -eq $null すると True になります
-match するとマッチしないので False です

PS C:\> echo ($val -eq $null)
True
PS C:\> echo ($val -match "^A")
False

$null を代入した変数でも同じです

PS C:\> $v = $null
PS C:\> echo ($v -eq $null)
True
PS C:\> echo ($v -match "^A")
False

しかしコマンドの結果の場合は

PS C:\> $x = (node -e "null")
PS C:\> echo ($x -eq $null)
True
PS C:\> echo ($x -match "^A")
PS C:\>

-match のときに False が出ません
コマンドの実行結果なので空配列だったりするのかと思いましたが $x.gettype() は null のメソッド呼び出しとしてエラーになります
$x を表示しても空ですし -eq $null が True なので null のはずなのに

node コマンドは何も出力しないように -e "null" を指定しています
null を評価するだけでそれを出力しないので null が返ってくるわけではなく 標準出力に何も書き込まれないコマンドです

($x -match "^A") も null になっているのかと思って gettype() を呼び出してみるとなんと Object[] でした

PS C:\> ($x -match "^A").gettype()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array

意味がわからないのですがググっていると PowerShell の仕様として特殊な null が存在するようでした
https://learn.microsoft.com/ja-jp/powershell/scripting/learn/deep-dives/everything-about-null?view=powershell-7.4#empty-null

実行ファイルに限らず PowerShell 上の関数でも 何も返さないものの結果である null は特殊な null で「Empty null」と呼ばれるものみたいです
実体は System.Management.Automation.Internal.AutomationNull.Value の値らしいです
この値は null と比較したり値の評価が行われるときは null として扱われるようです
なので

PS C:\> [System.Management.Automation.Internal.AutomationNull]::Value -eq $null
True

ですし 通常の null か判断できません

判断するには特殊な方法で配列に入れて Count を見ればいいようです

PS C:\> @($v).count
1
PS C:\> @($x).count
0

普通の null なら 1 で Empty null なら 0 になります

すごく特殊ですし こういうのはやめてほしいです
変なことしていて無理やり都合を合わせてる感
PHP みを感じます
もっと一貫性があって例外的なものが少ないようしてほしいです
PowerShell でコマンドや変数の文字色を変える
ターミナルの黒画面に飽きて色を変えてみてますが コマンドの出力に色がついてるところだと 色が被ったり影響が出るのですよね
Windows Terminal だと設定の中にテーマを設定するところがあって そこで文字色や背景色を設定できます

wt-color

通常の文字色と背景色以外にも 黒や赤などの色も設定できます
背景色の上に表示してはっきりと文字が見えるように調整できます
色名は参考のものでしかないので 赤なのに実際の色は青みたいなこともできます
アプリケーション側で赤色って指定されたときに画面に出す色を設定する感じです
これのお陰で色を変えてもきれいに見れるように調整できます

これだけでもいいのですが PowerShell 側で色を付けてる部分は PowerShell の設定で変更できます
ユーザーが入力する部分で コマンドは黄色で変数は緑色みたいなのです

ps-color1

ここでどの色を出すかは PowerShell 側に設定があって Get-PSReadLineOption で取得できます

PS C:\Users\WDAGUtilityAccount> Get-PSReadLineOption

EditMode : Windows
AddToHistoryHandler :
HistoryNoDuplicates : True
HistorySavePath : C:\Users\WDAGUtilityAccount\AppData\Roaming\Microsoft\Windows\PowerShell\PSRea
dLine\ConsoleHost_history.txt
HistorySaveStyle : SaveIncrementally
HistorySearchCaseSensitive : False
HistorySearchCursorMovesToEnd : False
MaximumHistoryCount : 4096
ContinuationPrompt : >>
ExtraPromptLineCount : 0
PromptText : >
BellStyle : Audible
DingDuration : 50
DingTone : 1221
CommandsToValidateScriptBlockArguments : {ForEach-Object, %, Invoke-Command, icm...}
CommandValidationHandler :
CompletionQueryItems : 100
MaximumKillRingCount : 10
ShowToolTips : True
ViModeIndicator : None
WordDelimiters : ;:,.[]{}()/\|^&*-=+'"–—―
CommandColor : "$([char]0x1b)[93m"
CommentColor : "$([char]0x1b)[32m"
ContinuationPromptColor : "$([char]0x1b)[37m"
DefaultTokenColor : "$([char]0x1b)[37m"
EmphasisColor : "$([char]0x1b)[96m"
ErrorColor : "$([char]0x1b)[91m"
KeywordColor : "$([char]0x1b)[92m"
MemberColor : "$([char]0x1b)[97m"
NumberColor : "$([char]0x1b)[97m"
OperatorColor : "$([char]0x1b)[90m"
ParameterColor : "$([char]0x1b)[90m"
SelectionColor : "$([char]0x1b)[30;47m"
StringColor : "$([char]0x1b)[36m"
TypeColor : "$([char]0x1b)[37m"
VariableColor : "$([char]0x1b)[92m"

◯◯Color になってるところは実際の色で表示されています
コマンドの色を変更してみます

Set-PSReadLineOption -Colors @{ "Command"="$([char]0x1b)[33m" }

ps-color2

エスケープとして出力する文字が変わるものなので 過去ログは変わらず変更後の行だけ変わります
33 や 93 の数字が色に対応してます
エスケープの詳細は wikipedia 参照です
https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters

38 や 48 を使うと 8bit/24bit で色を絶対値指定できますが これを使うと色名を通さないのでユーザーが変更できないです
そうなると背景色等のカスタマイズと相性が悪いのであまり使わないほうが良さそうです



ちなみに上のは標準の PowerShell でしたが 新しい PowerShell だと少しプロパティが追加されていて エスケープの表記方法も変わっていました
「$([char]0x1b)」 → 「`e」 とシンプルになってます

PowerShell 7.4.1
PS C:\Users\WDAGUtilityAccount> Get-PSReadLineOption

EditMode : Windows
AddToHistoryHandler : System.Func`2[System.String,System.Object]
HistoryNoDuplicates : True
HistorySavePath : C:\Users\WDAGUtilityAccount\AppData\Roaming\Microsoft\Windows\PowerShell\PSRea
dLine\ConsoleHost_history.txt
HistorySaveStyle : SaveIncrementally
HistorySearchCaseSensitive : False
HistorySearchCursorMovesToEnd : False
MaximumHistoryCount : 4096
ContinuationPrompt : >>
ExtraPromptLineCount : 0
PromptText : {> }
BellStyle : Audible
DingDuration : 50
DingTone : 1221
CommandsToValidateScriptBlockArguments : {ForEach-Object, %, Invoke-Command, icm…}
CommandValidationHandler :
CompletionQueryItems : 100
MaximumKillRingCount : 10
ShowToolTips : True
ViModeIndicator : None
WordDelimiters : ;:,.[]{}()/\|!?^&*-=+'"–—―
AnsiEscapeTimeout : 100
PredictionSource : HistoryAndPlugin
PredictionViewStyle : InlineView
TerminateOrphanedConsoleApps : False
CommandColor : "`e[93m"
CommentColor : "`e[32m"
ContinuationPromptColor : "`e[37m"
DefaultTokenColor : "`e[37m"
EmphasisColor : "`e[96m"
ErrorColor : "`e[91m"
InlinePredictionColor : "`e[97;2;3m"
KeywordColor : "`e[92m"
ListPredictionColor : "`e[33m"
ListPredictionSelectedColor : "`e[48;5;238m"
ListPredictionTooltipColor : "`e[97;2;3m"
MemberColor : "`e[37m"
NumberColor : "`e[97m"
OperatorColor : "`e[90m"
ParameterColor : "`e[90m"
SelectionColor : "`e[30;47m"
StringColor : "`e[36m"
TypeColor : "`e[37m"
VariableColor : "`e[92m"

またスタイルに関連する情報が入っている変数 $PSStyle が存在します

PS C:\Users\WDAGUtilityAccount> $psstyle

Reset : `e[0m
BlinkOff : `e[25m
Blink : `e[5m
BoldOff : `e[22m
Bold : `e[1m
DimOff : `e[22m
Dim : `e[2m
Hidden : `e[8m
HiddenOff : `e[28m
Reverse : `e[7m
ReverseOff : `e[27m
ItalicOff : `e[23m
Italic : `e[3m
UnderlineOff : `e[24m
Underline : `e[4m
StrikethroughOff : `e[29m
Strikethrough : `e[9m
OutputRendering : Host
Formatting.FormatAccent : `e[32;1m
Formatting.ErrorAccent : `e[36;1m
Formatting.Error : `e[31;1m
Formatting.Warning : `e[33;1m
Formatting.Verbose : `e[33;1m
Formatting.Debug : `e[33;1m
Formatting.TableHeader : `e[32;1m
Formatting.CustomTableHeaderLabel : `e[32;1;3m
Formatting.FeedbackName : `e[33m
Formatting.FeedbackText : `e[96m
Formatting.FeedbackAction : `e[97m
Progress.Style : `e[33;1m
Progress.MaxWidth : 120
Progress.View : Minimal
Progress.UseOSCIndicator : False
FileInfo.Directory : `e[44;1m
FileInfo.SymbolicLink : `e[36;1m
FileInfo.Executable : `e[32;1m
FileInfo.Extension : .zip,.tgz,.gz,.tar,.nupkg,.cab,.7z,.ps1,.psd1,.psm1,.ps1xml
Foreground.Black : `e[30m
Foreground.BrightBlack : `e[90m
Foreground.White : `e[37m
Foreground.BrightWhite : `e[97m
Foreground.Red : `e[31m
Foreground.BrightRed : `e[91m
Foreground.Magenta : `e[35m
Foreground.BrightMagenta : `e[95m
Foreground.Blue : `e[34m
Foreground.BrightBlue : `e[94m
Foreground.Cyan : `e[36m
Foreground.BrightCyan : `e[96m
Foreground.Green : `e[32m
Foreground.BrightGreen : `e[92m
Foreground.Yellow : `e[33m
Foreground.BrightYellow : `e[93m
Background.Black : `e[40m
Background.BrightBlack : `e[100m
Background.White : `e[47m
Background.BrightWhite : `e[107m
Background.Red : `e[41m
Background.BrightRed : `e[101m
Background.Magenta : `e[45m
Background.BrightMagenta : `e[105m
Background.Blue : `e[44m
Background.BrightBlue : `e[104m
Background.Cyan : `e[46m
Background.BrightCyan : `e[106m
Background.Green : `e[42m
Background.BrightGreen : `e[102m
Background.Yellow : `e[43m
Background.BrightYellow : `e[103m

Set-PSReadLineOption で色を変更するときに色に対応する数字を覚えなくてもここから参照できて便利です

ここの変数を直接更新できないかなと試しましたが ReadOnly プロパティと言われてダメでした

PS C:\Users\WDAGUtilityAccount> $PSStyle.Background.Red = $PSStyle.Foreground.Red
InvalidOperation: 'Red' is a ReadOnly property.
PS C:\Users\WDAGUtilityAccount> $PSStyle | Get-Member background

TypeName: System.Management.Automation.PSStyle

Name MemberType Definition
---- ---------- ----------
Background Property System.Management.Automation.PSStyle+BackgroundColor Background {get;}

PS C:\Users\WDAGUtilityAccount> $PSStyle.Background | Get-Member black

TypeName: System.Management.Automation.PSStyle+BackgroundColor

Name MemberType Definition
---- ---------- ----------
Black Property string Black {get;}

getter しか用意されてないですね
PowerShell 起動時に実行されるスクリプト
これまで気にしたこと無かったですが PowerShell でも bash の .bashrc などのように起動時に実行するスクリプトが登録できるようです

ファイルの場所は決まっていて $profile 変数に入ってます

PS C:\Users\WDAGUtilityAccount> $PROFILE | select *

AllUsersAllHosts : C:\Program Files\PowerShell\7\profile.ps1
AllUsersCurrentHost : C:\Program Files\PowerShell\7\Microsoft.PowerShell_profile.ps1
CurrentUserAllHosts : C:\Users\WDAGUtilityAccount\Documents\PowerShell\profile.ps1
CurrentUserCurrentHost : C:\Users\WDAGUtilityAccount\Documents\PowerShell\Microsoft.PowerShell_profile.ps1
Length : 81

AllUsers だと PowerShell の実行ファイルと同じ場所で CurrentUser だとそのユーザーのドキュメントフォルダの中みたいです
ユーザーは Sandbox ユーザーのものです
ユーザーフォルダと違ってドキュメントフォルダというのが少し微妙な感じがします

インストールせずに zip 版を使うと AllUsers は展開先になります

PS C:\> $PROFILE | select *

AllUsersAllHosts : C:\Users\WDAGUtilityAccount\Desktop\PowerShell-7.4.1-win-x64\profile.ps1
AllUsersCurrentHost : C:\Users\WDAGUtilityAccount\Desktop\PowerShell-7.4.1-win-x64\Microsoft.PowerShell_profile.ps1
CurrentUserAllHosts : C:\Users\WDAGUtilityAccount\Documents\PowerShell\profile.ps1
CurrentUserCurrentHost : C:\Users\WDAGUtilityAccount\Documents\PowerShell\Microsoft.PowerShell_profile.ps1
Length : 81

デフォルトで入ってる古い PowerShell の場合はこうでした

PS C:\Users\WDAGUtilityAccount> $PROFILE | select *

AllUsersAllHosts : C:\Windows\System32\WindowsPowerShell\v1.0\profile.ps1
AllUsersCurrentHost : C:\Windows\System32\WindowsPowerShell\v1.0\Microsoft.PowerShell_profile.ps1
CurrentUserAllHosts : C:\Users\WDAGUtilityAccount\Documents\WindowsPowerShell\profile.ps1
CurrentUserCurrentHost : C:\Users\WDAGUtilityAccount\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
Length : 88

CurrentUser の方がドキュメントフォルダの中なのは同じですが 最近の方だと PowerShell だったところが WindowsPowerShell になっていて別フォルダでした
Windows の VBScript のサポートが終わるらしい
自分には関係ないかなと思ってましたが そういえば使ってるところがありました
ちょっとしたツールでダイアログで入力するために使ってました

bat ファイルはこんな感じで書いておきます

wscript xxx.vbs

xxx.vbs の方はこういう感じです

Dim shell, txt
txt = InputBox("テキスト")

IF Len(Trim(txt)) Then
Set shell = WScript.CreateObject("WScript.Shell")
shell.Run "cmd.exe /c some-command --option " & txt & " & pause"
End If

これで入力用ダイアログが出て ユーザーの入力を受け取ってコマンドに埋め込めます
地味に便利なんですよね
コマンドラインを使えない人向けにもできますし

ただ VBScript が将来的に消えるなら PowerShell に置き換えたほうがいいのかもしれません
たぶんこんなので同じ動きになりそう

bat ファイル

powershell -NoProfile -ExecutionPolicy Unrestricted xxx.ps1

xxx.ps1

Add-Type -AssemblyName "Microsoft.VisualBasic"

$txt = [Microsoft.VisualBasic.Interaction]::InputBox("テキスト")
if ($txt.trim().length) {
some-command --option $txt
pause
}

VBScript より読みやすいしこっちでいいかな
PowerShell で◯◯したい
たまにしか使わないので PowerShell で◯◯したいってなったときに覚えれなくて毎回調べてるのでメモ

▶ コマンドを探したい

Get-Command

PS C:\Users\WDAGUtilityAccount> get-command

CommandType Name Version Source
----------- ---- ------- ------
Alias Add-AppPackage 2.0.1.0 Appx
Alias Add-AppPackageVolume 2.0.1.0 Appx
Alias Add-AppProvisionedPackage 3.0 Dism
...
...

Alias, Function, Cmdlet, Application と実行できるコマンドは種類問わず探せる

引数で検索ワードを指定できる
* や ? でワイルドカード指定もできる

PS C:\Users\WDAGUtilityAccount> get-command select*

CommandType Name Version Source
----------- ---- ------- ------
Alias select -> Select-Object
Cmdlet Select-Object 7.0.0.0 Microsoft.PowerShell.Utility
Cmdlet Select-String 7.0.0.0 Microsoft.PowerShell.Utility
Cmdlet Select-Xml 7.0.0.0 Microsoft.PowerShell.Utility

PS C:\Users\WDAGUtilityAccount> get-command i?x

CommandType Name Version Source
----------- ---- ------- ------
Alias iex -> Invoke-Expression

▶ alias を探したい

エイリアス名から実行されるコマンドを探すなら Get-Command でいいけどその反対
コマンドからそのコマンドを実行するエイリアスを探す
直接的なコマンドはなさそうなので Get-Command の結果から ResolvedCommand の Name が目的のに一致する行を探す

PS C:\Users\WDAGUtilityAccount> (get-command *)| ? { $_.ResolvedCommand.Name -eq "where-object" }

CommandType Name Version Source
----------- ---- ------- ------
Alias ? -> Where-Object
Alias where -> Where-Object

PS C:\Users\WDAGUtilityAccount> (get-command *)| ? { $_.ResolvedCommand.Name -eq "set-location" }

CommandType Name Version Source
----------- ---- ------- ------
Alias cd -> Set-Location
Alias chdir -> Set-Location
Alias sl -> Set-Location

データとして使える形式でなくて良ければ help を見れば書いてる

PS C:\Users\WDAGUtilityAccount> help cd

NAME
Set-Location

...
...

ALIASES
sl
cd
chdir

...
...

▶ コマンドの実行結果を grep したい

Select-String でテキストをフィルタできる

PS C:\Users\WDAGUtilityAccount> echo "a1`nb1`na2`nb2" > text.txt
PS C:\Users\WDAGUtilityAccount> cat text.txt
a1
b1
a2
b2
PS C:\Users\WDAGUtilityAccount> cat text.txt | select-string a

a1
a2

新しい PowerShell だとマッチしてる部分の色が変わる

PowerShell ではパイプで受け取るものはオブジェクトの配列
文字列以外のオブジェクトになってることもある
Select-String は文字列として検索するので文字列化されたものが対象になる

PS C:\Users\WDAGUtilityAccount> dir

Directory: C:\Users\WDAGUtilityAccount

Mode LastWriteTime Length Name
---- ------------- ------ ----
d-r--- 11/5/2022 7:18 AM 3D Objects
d-r--- 11/5/2022 7:18 AM Contacts
d-r--- 11/5/2022 7:18 AM Desktop
d-r--- 11/5/2022 7:18 AM Documents
d-r--- 12/10/2022 4:37 PM Downloads
d-r--- 11/5/2022 7:18 AM Favorites
d-r--- 11/5/2022 7:18 AM Links
d-r--- 11/5/2022 7:18 AM Music
d-r--- 11/5/2022 7:18 AM Pictures
d-r--- 11/5/2022 7:18 AM Saved Games
d-r--- 11/5/2022 7:19 AM Searches
d-r--- 11/5/2022 7:18 AM Videos
-a---- 12/10/2022 5:16 PM 13 text.txt

PS C:\Users\WDAGUtilityAccount> dir | select-string Do

Documents
Downloads

PS C:\Users\WDAGUtilityAccount> dir | select-string 2022

dir で得られるオブジェクトは DirectoryInfo 型か FileInfo 型
DirectoryInfo 型は文字列化すると Name プロパティの値になる

PS C:\Users\WDAGUtilityAccount> (dir)[0].name
3D Objects
PS C:\Users\WDAGUtilityAccount> (dir)[0].tostring()
3D Objects

Do で検索すると Name プロパティに Do を含む Documents はマッチするけど 2022 で検索しても日付は検索対象にならないのでマッチしない

FileInfo 型は特殊で通常の文字列化ではなく ファイルの中身を読み取って検索する

PS C:\Users\WDAGUtilityAccount> dir | select-string b

3D Objects
text.txt:2:b1
text.txt:4:b2

ファイルの中身を検索した場合は ファイル名とマッチした行数も表示される

Get-Alias では文字列化するとエイリアスの名前になる
Name として表示されてる「(エイリアス名) -> (参照先)」は DisplayName プロパティ
文字列化した内容にエイリアスの参照先は含まれないので参照先では検索できない

PS C:\Users\WDAGUtilityAccount> get-alias

CommandType Name Version Source
----------- ---- ------- ------
Alias % -> ForEach-Object
Alias ? -> Where-Object
Alias ac -> Add-Content
...
...

PS C:\Users\WDAGUtilityAccount> get-alias | select-string Whe

where

「?」 がでなくてエイリアスの名前の方に whe を含む where がマッチする

▶ ファイルをダウンロードする

Invoke-WebRequest

古い PowerShell だと wget や curl がエイリアスに設定されていたけど新しい PowerShell だと iwr のみで wget などは使えない
最近は標準で curl.exe が存在するので紛らわしいからだと思う

Invoke-WebRequest http://localhost/test.zip -OutFile test.zip

ダウンロードする場合はファイル名が必須
自動でつけてくれない
curl で -O を使えば自動でつけてくれるのでこっちのほうがいいかも

PowerShell で curl を使う場合は名前をつけて保存するときにリダイレクトを使わないように注意が必要
PowerShell のリダイレクトは文字列化する変換処理が入るのでバイナリを保存するときに使うと壊れる

古い PowerShell だと Invoke-WebRequest が IE 設定を参照するらしくてエラーになることがある
IE を参照しないよう -UseBasicParsing オプションをつけると回避できる
curl を使うでもいいけどデフォルトでは curl は Invoke-WebRequest にエイリアス設定されてるので curl ではなく curl.exe にしないと Invoke-WebRequest が実行されてしまう

▶ コマンドの実行時間を測る

Measure-Command

ブロックの中に計測したいコマンドを書く
コマンドの出力はされない

PS C:\Users\WDAGUtilityAccount> function fib ($a) { if ($a -lt 2) { $a } else { (fib ($a - 1)) + (fib ($a - 2)) } }
PS C:\Users\WDAGUtilityAccount> Measure-Command { fib 30 }

Days : 0
Hours : 0
Minutes : 3
Seconds : 19
Milliseconds : 24
Ticks : 1990247069
TotalDays : 0.00230352670023148
TotalHours : 0.0552846408055556
TotalMinutes : 3.31707844833333
TotalSeconds : 199.0247069
TotalMilliseconds : 199024.7069

ブロックの中で Write-Host にパイプするとコマンドの出力も見れる

▶ select と where

Select-Object と Where-Object へのエイリアス

SQL や LINQ と同じ感じで where は行をフィルタして SELECT は列をフィルタする

PS C:\Users\WDAGUtilityAccount> $obj1 = [PSCustomObject]@{ foo = 1; bar = 2 }
PS C:\Users\WDAGUtilityAccount> $obj2 = [PSCustomObject]@{ foo = 3; bar = 4 }
PS C:\Users\WDAGUtilityAccount> $obj1, $obj2

foo bar
--- ---
1 2
3 4

PS C:\Users\WDAGUtilityAccount> $obj1, $obj2 | where foo -eq 1

foo bar
--- ---
1 2

PS C:\Users\WDAGUtilityAccount> $obj1, $obj2 | where bar -eq 4

foo bar
--- ---
3 4

PS C:\Users\WDAGUtilityAccount> $obj1, $obj2 | select foo

foo
---
1
3

PS C:\Users\WDAGUtilityAccount> $obj1, $obj2 | select bar

bar
---
2
4

PS C:\Users\WDAGUtilityAccount> $obj1, $obj2 | select bar, foo

bar foo
--- ---
2 1
4 3

多くのオブジェクトは普通に表示しても全部のプロパティを表示してくれない
select で * を指定して全部を表示できる

PS C:\Users\WDAGUtilityAccount> dir | where name -eq Desktop

Directory: C:\Users\WDAGUtilityAccount

Mode LastWriteTime Length Name
---- ------------- ------ ----
d-r-- 11/5/2022 7:18 AM Desktop

PS C:\Users\WDAGUtilityAccount> dir | where name -eq Desktop | select *

PSPath : Microsoft.PowerShell.Core\FileSystem::C:\Users\WDAGUtilityAccount\Desktop
PSParentPath : Microsoft.PowerShell.Core\FileSystem::C:\Users\WDAGUtilityAccount
PSChildName : Desktop
PSDrive : C
PSProvider : Microsoft.PowerShell.Core\FileSystem
PSIsContainer : True
Mode : d-r--
ModeWithoutHardLink : d-r--
BaseName : Desktop
ResolvedTarget : C:\Users\WDAGUtilityAccount\Desktop
Target :
LinkType :
Parent : C:\Users\WDAGUtilityAccount
Root : C:\
FullName : C:\Users\WDAGUtilityAccount\Desktop
Extension :
Name : Desktop
Exists : True
CreationTime : 11/5/2022 7:18:11 AM
CreationTimeUtc : 11/4/2022 10:18:11 PM
LastAccessTime : 12/10/2022 7:28:05 PM
LastAccessTimeUtc : 12/10/2022 10:28:05 AM
LastWriteTime : 11/5/2022 7:18:14 AM
LastWriteTimeUtc : 11/4/2022 10:18:14 PM
LinkTarget :
UnixFileMode : -1
Attributes : ReadOnly, Directory

ブロックを使って各行に対して任意の式を適用することもできる

PS C:\Users\WDAGUtilityAccount> 1,2,3 | select { $_ + 10 }, { $_ * $_ }

$_ + 10 $_ * $_
--------- ---------
11 1
12 4
13 9

PS C:\Users\WDAGUtilityAccount> 1..10 | select { $_ }, { fib $_ }

$_ fib $_
---- --------
1 1
2 1
3 2
4 3
5 5
6 8
7 13
8 21
9 34
10 55

▶ 要素数を数える

Measure-Object

PS C:\Users\WDAGUtilityAccount> dir Do*

Directory: C:\Users\WDAGUtilityAccount

Mode LastWriteTime Length Name
---- ------------- ------ ----
d-r-- 11/5/2022 7:18 AM Documents
d-r-- 12/10/2022 4:37 PM Downloads

PS C:\Users\WDAGUtilityAccount> dir Do* | Measure-Object

Count : 2
Average :
Sum :
Maximum :
Minimum :
StandardDeviation :
Property :

PS C:\Users\WDAGUtilityAccount> gcm * | Measure-Object

Count : 2515
Average :
Sum :
Maximum :
Minimum :
StandardDeviation :
Property :
Windows のプロダクトキー取得
よく見る方法
コマンドプロンプトでこのコマンドを入力

wmic path SoftwareLicensingService get OA3xOriginalProductKey

時々これで取得できない環境がある

他の方法を探すと PowerShell で遠回しに↑と同じ処理を実行してるものだったり
レジストリを参照してるけどプロダクトキーじゃなくてプロダクト ID の取得方法が書かれていたり
ID は桁数が違うからみればわかるはず

使える方法だとソフトのインストールが必要になったりであまりやりたくない方法
スクリプトを探すとこういうのがあったけど VBS
https://gist.github.com/craigtp/dda7d0fce891a087a962d29be960f1da

実行するだけなら VBS で困らない気はするけど気持ち的にイヤなのと読みづらいのでなにしてるかわかりづらいのがなんかイヤだったので PowerShell のスクリプトにした

$dpid = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion").DigitalProductId
$is_win8 = [Math]::Truncate($dpid[66] / 6) -band 1
$dpid[66] = ($dpid[66] -band 0xF7) -bor (($is_win8 -band 2) * 4)

$keyoffset = 52
$maps = "BCDFGHJKMPQRTVWXY2346789"
$key = ""
$last = 0

for ($i = 24; $i -ge 0; $i--) {
$current = 0
for ($j = 14; $j -ge 0; $j--) {
$current = $current * 256
$index = $j + $keyoffset
$current = $dpid[$index] + $current
$dpid[$index] = [Math]::Truncate($current / 24)
$current = $current % 24
}
$key = $maps[$current] + $key
$last = $current
}

$key = $key.Substring(1).Insert($last, "N")
$key = ($key -split "(.{5})" | ? { $_ }) -join "-"
echo $key

wmic コマンドでキーを取得できる環境でこのスクリプトを動かして同じ結果になることを確認したのでたぶん大丈夫なはず
wmic コマンドで取得できない環境でこの方法で取得できるかは未確認
PowerShell に移行しつつある
これまではちょっとなにかしたいというときはとりあえず Node.js だった
だけど今でも Node.js は標準機能だけだと簡単なことでも不便なところがある
例えば http(s) リクエストとか

package.json を用意して色々インストールする前提だと困らないレベルだけど ちょっとしたスクリプトだけでパッケージのインストールが必要になるのはなんかイヤ
パッケージ内であっても メインの処理ではない極稀に使うことがある便利ツールくらいなもののために追加パッケージを入れるのも同じく避けたい

そういうのだとグローバルインストールがありかもしれないけど グローバルインストールはコマンド以外は避けたくもある
パッケージ側でインストール漏れてても動いてしまうせいで あとになって必要なパッケージの記載漏れに気づくとかあるし 初めて実行する環境だと事前準備でインストールが必要だし

Node.js にこだわる必要もないので他のツールを探すと PowerShell が良さそう
デフォルトで色々できるし Windows だと標準で使える
ちょっとしたスクリプトを動かしたい環境って基本は開発環境の Windows であって サーバ側で動かすようなものならちゃんとパッケージに含めた機能になるはず

PowerShell だとコマンド呼び出して結果のこの部分取り出して別のコマンドに渡して その結果によって次のコマンドを変えたり とかそういうのがすごくやりやすい
zx が出てきた頃は それに移行を考えてたけど Node.js な分 標準機能だと不足機能がある問題があるし .NET が Linux でも動くので PowerShell に揃えていくのもありかなって思った
PowerShell は ps1 ファイルを実行したときに同じプロセスで実行される
Linux のシェル風に考えていて それぞれ別プロセスだと思ってたのに同じだった

$pid

を a.ps1 に保存して PowerShell で実行する

PS C:\Users\user9\Desktop> $pid
16640
PS C:\Users\user9\Desktop> .\a.ps1
16640
PS C:\Users\user9\Desktop> & .\a.ps1
16640
PS C:\Users\user9\Desktop> powershell .\a.ps1
7948

最初のが実行元の PID で直接 ps1 ファイルを指定したり & を使ったりしても同じ PID が表示される
コマンドに powershell を指定すれば PowerShell プロセスが起動するので別の PID

変数のスコープが終わってるし 困ることないかと思ってたけど GC されないことで Excel プロセスが残るとか カレントディレクトリのずれが引き継がれるとかあった

echo "pw cwd" (Get-Location).path
echo ".net cwd" ([IO.Directory]::GetCurrentDirectory())

cd でカレントディレクトリ移動後に↑のスクリプトを実行すると

pw cwd
C:\Users\user9\Desktop\dir1\dir2
.net cwd
C:\Users\user9\Desktop

スクリプト内では cd してないから PowerShell と .NET のカレントディレクトリが一致してると思いきや 同じプロセスなのでスクリプトを実行してる環境のずれが引き継がれてる

こういうのがあるし 基本は別プロセスで実行するようにしたほうがよさそう
PowerShell でフォルダ・ファイルの属性変更
PowerShell の dir (Get-ChildItem) で取得できるのは FileInfo か DirectoryInfo の配列
フォルダかファイルで変わる

PS C:\tmp\ps> mkdir aa
PS C:\tmp\ps> dir

ディレクトリ: C:\tmp\ps

Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2021/03/01 22:56 aa

PS C:\tmp\ps> $aa=(dir)[0]
PS C:\tmp\ps> $aa.gettype()

IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True DirectoryInfo System.IO.FileSystemInfo

このインスタンスのプロパティの setter を使って属性や更新日時を変更できる

更新日時を変更

PS C:\tmp\ps> $aa.lastwritetime="2020/01/01"
PS C:\tmp\ps> dir

ディレクトリ: C:\tmp\ps

Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2020/01/01 0:00 aa

隠しファイルにする
(隠しファイルは dir に -Force 付けないと取得できない)

PS C:\tmp\ps> $aa.attributes="h"
PS C:\tmp\ps> dir
PS C:\tmp\ps> dir -force

ディレクトリ: C:\tmp\ps

Mode LastWriteTime Length Name
---- ------------- ------ ----
d--h-- 2020/01/01 0:00 aa
PowerShell でコマンドログが残ってた
コマンドプロンプトと一緒で一度ウィンドウを閉じると↑キーを押しても過去のコマンド履歴は見れないものと思っていたのですが 押して見ると見れました
ローカルファイルではここに保存されてました

C:\Users\<username>\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt

Windows のファイル共有の接続状況を確認する
PowerShell のコマンド

get-smbsession
その PC で共有してるフォルダへ接続してる PC の IP アドレスなどがわかる
ClientUserName は接続するときにログインしたユーザ

get-smbconnection
その PC が接続してる共有フォルダの一覧がわかる
ServerName はサーバ名 \\fs01 なら fs01
ShareName はサーバ内に出てくる共有フォルダのルートのこと
UserName は接続してるユーザ名
Dialect は smb のバージョン
両方が使える最大のバージョンが使われる (Win7 は 2.1, Win 8.1 は 3.0.2)

get-smbshare
その PC が共有してるフォルダ一覧
共有設定で指定した名前とそのフォルダのパスがわかる


コマンドは管理者権限で PowerShell で実行する必要あり

C:\Users\win>powershell start-process powershell -verb runas

別 Window で PowerShell が開くのでそこで実行

Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

PS C:\WINDOWS\system32> Get-SmbSession

SessionId ClientComputerName ClientUserName NumOpens
--------- ------------------ -------------- --------
154618822713 192.168.1.109 DESKTOP\PUBLIC 0
154618822727 192.168.1.113 DESKTOP\PUBLIC 0


PS C:\WINDOWS\system32> Get-SmbConnection

ServerName ShareName UserName Credential Dialect NumOpens
---------- --------- -------- ---------- ------- --------
fs01 common DESKTOP\win DESKTOP\win 3.0.2 1
fs01 programs DESKTOP\win DESKTOP\win 3.0.2 4


PS C:\WINDOWS\system32> Get-SmbShare

Name ScopeName Path Description
---- --------- ---- -----------
ADMIN$ * C:\WINDOWS Remote Admin
C$ * C:\ Default share
share001 * C:\Data\share-dir\public\tmp9999\share001
D$ * D:\ Default share
icxss * C:\dev2\code\icx\ss
IPC$ * Remote IPC
print$ * C:\windows\system32\spool\drivers プリンター ドライバー
public * C:\Data\others\public
scanner * C:\Data\share-dir\scanner
web * D:\test\web\



以上の内容はhttps://let.blog.jp/tag/PowerShellより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14