Windows縛り的な状況のときに便利だけど即忘れるので思い出す脳内コストを削減するために備忘録を。
順不同
helpみる
Get-Help -Name Import-CSV -Full
https://docs.microsoft.com/ja-jp/powershell/scripting/learn/ps101/02-help-system?view=powershell-7.1
モジュールのインポート
Import-Module Az.Accounts
Import-Module Az.Resources
Import-Module Az.Compute
Import-Module Az.Automation
※AuzureAutomationのImportはメニューから別途するしコードに書く必要がないが書いとかないと何使ってたか分からなくなってモジュールアップデートの影響範囲を調べづらくなるので書いたほうがいいかなという感想です。
変数をセットする
# String
$maResourceGroupname = "komitet-rg"
# array
$metric_obj = @()
$metric_obj = @('a','b')
# custom object
$ins_counts = [PSCustomObject]@{ステータス = "完了"; 件数 = "$stat_comp";},
[PSCustomObject]@{ステータス = "受付済"; 件数 = "$stat_l2";},
[PSCustomObject]@{ステータス = "新規"; 件数 = "$stat_new";},
[PSCustomObject]@{ステータス = "対応中"; 件数 = "$stat_wip";}
# json
$ParamsJson = '[
{
"project": "hoge",
"checkServices": [
"AzureFrontDoor.Backend",
"AzureTrafficManager"
]
},
{
"project": "piyo",
"checkServices": [
"AzureActiveDirectoryDomainServices",
"DataFactoryManagement",
"AzureMonitor.Core"
]
}
]'
変数の中身を見る
$metric_obj
Write-output $metric_obj
echo $metric_obj
どれでも出る。
一行だけ見る場合
# select-objectの-First 1オプション
$Error[0] -split "`r`n" | Select-Object -First 1
# 配列なら添え字使う
($Error[0] -split "`r`n")[0]
headとtail風
Get-Process | Select-Object -Property ProcessName, Description, StartTime -First 5
Get-Process | Select-Object -Property ProcessName, Description, StartTime -Last 5
https://qiita.com/miyamiya/items/059430d5499f0fe336ff
データ型を変換する
#関数をつかう
$yesterday = ((Get-Date).AddDays(-1)).ToString("yyyy-MM-dd");
#冒頭に型を足す
$storage_space_free_mb = ([int]$total - [int]$used)
[pscustomobject]@{TIME = $timegenerated; storage_space_free_mb = $storage_space_free_mb; storage_useage_percent = $storage_useage_percent;} | Write-Output
#変換コマンドをつかう
$diffdatafd = $diffresultfd|ConvertTo-Json|ConvertFrom-Json
$msgtm = $diffdatatm.InputObject.value|Out-String
$computer_selected|Select-String $metric |convertfrom-csv -Header $CsvHeader|Export-Csv -NoTypeInformation $outfile
日付を扱った変数
$yesterday = ((Get-Date).AddDays(-1)).ToString("yyyy-MM-dd");
$yesterday_start = (((Get-Date).AddDays(-1)).ToString("yyyy-MM-dd") + "T00:00:00+09:00");
$yesterday_end = (((Get-Date).AddDays(-1)).ToString("yyyy-MM-dd") + "T23:59:59+09:00");
$lastmonth = ((Get-Date).AddMonths(-1)).ToString("yyyyMM");
特定の期間でデータ出したいときにstart/endを定義。
日付を比較して一定期間を出す
$insobj_lm = $insobj|Where-Object {[Datetime]$_.registered_date -gt [Datetime]$startdate}|Where-Object {[Datetime]$_.registered_date -lt [Datetime]$enddate}
https://qiita.com/Q11Q/items/2c8ff4fcd0141df3eacb
Timezoneの表示をJSTに変換したい場合
$now.ToUniversalTime()
2021年4月26日 3:46:56
$newtime = $now.ToUniversalTime()
$strCurrentTimeZoneId = "Tokyo Standard Time"
$TZ = [System.TimeZoneInfo]::FindSystemTimeZoneById($strCurrentTimeZoneId)
$LocalTime = [System.TimeZoneInfo]::ConvertTimeFromUtc($newtime, $TZ)
$LocalTime
2021年4月26日 12:46:56
(Get-WmiObject win32_timezone).StandardName
だと二バイト文字で失敗するのでGet-TimeZone -ListAvailable
で目的のIDを見つけて直指定。
作業ディレクトリを定義
#現在のディレクトリから作業ディレクトリを定義
$savedir = (Convert-path .\$yesterday)
#ホームディレクトリから定義
$savedir = (Convert-path ~/$yesterday)
その他ファイル操作
https://qiita.com/mima_ita/items/ae31f3a19389e69b307f
文字列を連結しファイル名にする
$outfile = Join-Path $savedir ($yesterday + "-$computer-$metric" + ".csv")
他、-joinなど
https://www.tekizai.net/entry/powershell_string_concat_1
ファイル名一括置換
ls | Rename-Item -NewName { $_.Name -replace '置換対象文字列','置換後文字列'}
https://qiita.com/hyakuson/items/9e8e239d4ba45b595486
> $datestr="2021-04-29"
> $prefix="[mypj]_"
> ls
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2021/04/30 18:40 22573 2021-04-29-hoge01-cpu_percent.csv
-a---- 2021/04/30 18:40 26057 2021-04-29-hoge01-dwu_consumption_percent.csv
-a---- 2021/04/30 18:40 23525 2021-04-29-hoge01-memory_usage_percent.csv
> ls | Rename-Item -NewName { $_.Name -replace "${datestr}-","${prefix}" -replace ".csv","_${datestr}.csv"}
> ls
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2021/04/30 18:40 22573 [mypj]_hoge01-cpu_percent_2021-04-29.csv
-a---- 2021/04/30 18:40 26057 [mypj]_hoge01-dwu_consumption_percent_2021-04-29.csv
-a---- 2021/04/30 18:40 23525 [mypj]_hoge01-memory_usage_percent_2021-04-29.csv
後方参照+正規表現だとうまくいかずに-replace
を複数書いた
ループ処理する
以下は特定の2つの配列変数に含まれる値に絞ってそれをファイル名にして出力させるために入れ子にした
foreach ( $num in $computers_suffix )
{
$computer = ($computers_prefix + $num)
$computer_selected = $csv_obj|convertto-csv|Select-String $computer
foreach ( $metric in $metrics )
{
$outfile = Join-Path $savedir ($yesterday + "-$computer-$metric" + ".csv")
$computer_selected|Select-String $metric |convertfrom-csv -Header $CsvHeader|Export-Csv -NoTypeInformation $outfile
}
}
以下は一行ずつ読み込んで処理した値をCSVに出力するときに-Appendで1行ずつ追記している
$metric_data = az monitor metrics list --resource $sa_rid_log --metrics UsedCapacity --interval 1h --aggregation Average --start-time $yesterday_start --end-time $yesterday_end --query "value[0].timeseries[0].data[*].{TIME:timeStamp,UsedCapacity:average}" -o json
$metric_obj = @()
$metric_obj = $metric_data|ConvertFrom-Json
$outfile = Join-Path $savedir ($yesterday + "-log-UsedCapacity" + ".csv")
$metric_obj|Export-Csv -NoTypeInformation $outfile
foreach ( $metric in $metric_obj )
{
$limitbyte = 5629499534213120
$used = $metric.UsedCapacity
$free = ($limitbyte - $used)
$outfile = Join-Path $savedir ($yesterday + "-log-FreeCapacity" + ".csv")
$metric_obj2 = [pscustomobject]@{TIME = $metric.TIME; freeCapacity = $free;} | Write-Output
$metric_obj2|Export-Csv -NoTypeInformation -Append $outfile
}
並列処理
1つずつのループではなく時短で並列でやりたい場合には
バージョン古い方(5.1)だとWorkflow
でかこんでforeachに -parallelオプションをつける。
https://trend-desk.com/archives/1372
https://docs.microsoft.com/ja-jp/powershell/module/psworkflow/about/about_parallel?view=powershell-5.1
PS C:\Users\komiyay> Workflow CallTestWorkflow()
>> {
>> $computers_suffix=@('01','02','03','04','05','06','07','08','09','10')
>> $computers_prefix="mytestvm"
>> foreach -parallel ( $num in $computers_suffix )
>> {
>> $computer = ($computers_prefix + $num)
>> Write-Output "$((Get-Date).ToString("MM/dd_HH:mm:ss")) $computer"
>> Start-Sleep -Seconds 1
>> }
>> }
PS C:\Users\komiyay> CallTestWorkflow
04/18_15:57:03 mytestvm10
04/18_15:57:03 mytestvm09
04/18_15:57:03 mytestvm08
04/18_15:57:03 mytestvm07
04/18_15:57:03 mytestvm06
04/18_15:57:03 mytestvm05
04/18_15:57:03 mytestvm04
04/18_15:57:03 mytestvm03
04/18_15:57:03 mytestvm02
04/18_15:57:03 mytestvm01
※数多いと並列じゃないと処理時間がかさんで所定の時間に間に合わないかもというケースにおススメ。
バージョン新しめであれば(PowerShell 7 Preview.3から)以下のようにForEach-Object -Parallel
がつかえる
CloudShellではつかえたがローカルPCで使えなかったりした。Automationだと新しくて7.1なのでバージョン足らないかも)
https://dev.classmethod.jp/articles/about-powershell-foreach-object-parallel/
PS /home/komiya> $computers_suffix=@('01','02','03','04','05','06','07','08','09','10')
PS /home/komiya> $computers_prefix="mytestvm"
PS /home/komiya> echo $computers_suffix | ForEach-Object -Parallel {
>>
>> Write-Output "$((Get-Date).ToString("MM/dd_HH:mm:ss")) $using:computers_prefix$_"
>> Start-Sleep -Seconds 2
>> }
04/18_04:12:19 mytestvm01
04/18_04:12:19 mytestvm02
04/18_04:12:19 mytestvm03
04/18_04:12:19 mytestvm04
04/18_04:12:19 mytestvm05
04/18_04:12:21 mytestvm06
04/18_04:12:21 mytestvm07
04/18_04:12:21 mytestvm08
04/18_04:12:21 mytestvm09
04/18_04:12:21 mytestvm10
PS /home/komiya>
並列じゃない場合との比較(↑↓出力結果の秒数のところに注目してください)
PS C:\Users\komiyay> $computers_suffix=@('01','02','03','04','05','06','07','08','09','10')
PS C:\Users\komiyay> $computers_prefix="mytestvm"
PS C:\Users\komiyay> foreach ( $num in $computers_suffix )
>> {
>> $computer = ($computers_prefix + $num)
>> Write-Output "$((Get-Date).ToString("MM/dd_HH:mm:ss")) $computer"
>> Start-Sleep -Seconds 1
>> }
04/18_13:04:19 mytestvm01
04/18_13:04:20 mytestvm02
04/18_13:04:21 mytestvm03
04/18_13:04:22 mytestvm04
04/18_13:04:23 mytestvm05
04/18_13:04:24 mytestvm06
04/18_13:04:25 mytestvm07
04/18_13:04:26 mytestvm08
04/18_13:04:27 mytestvm09
04/18_13:04:28 mytestvm10
四則演算
$storage_space_free_mb = ([int]$total - [int]$used)
キーが一致する場合の列の追加処理(SQLのJOINライクな処理)
$metric_obj_mi3 = foreach ( $row in $metric_obj_mi1 )
{
$timegenerated = $row.TIME
$total = $row.reserved_storage_mb
$used = $metric_obj_mi2 | Where-Object {$_.TIME -eq $timegenerated} | Select-Object -ExpandProperty storage_space_used_mb
$storage_space_free_mb = ([int]$total - [int]$used)
[pscustomobject]@{TIME = $timegenerated; storage_space_free_mb = $storage_space_free_mb; storage_useage_percent = $storage_useage_percent;} | Write-Output
}
余談でLinuxでやる場合joinコマンドというのがあってループは不要で便利です。
特定の値を出す
イコールの場合
$used = $metric_obj_mi2 | Where-Object {$_.TIME -eq $timegenerated} | Select-Object -ExpandProperty storage_space_used_mb
Where-Objectは条件の定義で絞る、Select-Objectは特定のカラムで絞る、Select-Stringはgrepのようなもの(たぶん操作したい対象の型によって使い分けたほうが効率がよさそうでSelect-StringをObjectに対してつかうと出力の型がStringになりそう)
部分一致検索
$insobj1_az = $insobj1_lm|Where-Object {$_.content_ja -like "*Azure*"}
カウントして表作ってファイルに出力
$insobj1_az2 = Import-Csv -encoding default -Path $outfile3
$stat_comp = ($insobj1_az2 |Where-Object status_name_ja -eq "完了").Count
$stat_l2 = ($insobj1_az2 |Where-Object status_name_ja -eq "受付済").Count
$stat_new = ($insobj1_az2 |Where-Object status_name_ja -eq "新規").Count
$stat_wip = ($insobj1_az2 |Where-Object status_name_ja -eq "対応中").Count
$ins_counts = [PSCustomObject]@{ステータス = "完了"; 件数 = "$stat_comp";},
[PSCustomObject]@{ステータス = "受付済"; 件数 = "$stat_l2";},
[PSCustomObject]@{ステータス = "新規"; 件数 = "$stat_new";},
[PSCustomObject]@{ステータス = "対応中"; 件数 = "$stat_wip";}
$savePath_inscounts = Join-Path $workdir "i-filename2-incident_counts.csv"
$ins_counts |Export-Csv -NoTypeInformation -encoding default $savePath_inscounts
https://stackoverflow.com/questions/11526285/how-to-count-objects-in-powershell
https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/import-csv?view=powershell-7.1
https://qiita.com/nijinagome/items/8daf6d116892eee14cfc
変数の中身を置換する
$metric2 = $metric.Replace('/','Per').Replace(' ','')
SortしてUniqにする
$insobj1_azid = $insobj1_az|select-object incident_mng_nm|sort incident_mng_nm -Unique
URLからファイルをとってくる
$DownloadUri = 'https://www.microsoft.com/en-us/download/confirmation.aspx?id=56519'
$DownloadPage = Invoke-WebRequest -Uri $DownloadUri -UseBasicParsing
$JsonFileUri = ($DownloadPage.RawContent.Split('"') -like 'https://*ServiceTags_Public*')[0]
$Response = Invoke-WebRequest -Uri $JsonFileUri -UseBasicParsing
トライキャッチで存在を確認しエラー処理
try{
$ResponseCheck = Invoke-WebRequest -Uri $DownloadUri -UseBasicParsing
}catch{
$ErrorMsg = "Automation [ Check_Azure_IP_Range ] 実行中に IP-Range ページが見つかりませんでした。URLを確認してください。<br>$DownloadUri"
PostTeams $TeamsUrl $ErrorMsg
throw "URLは無効です。実行を中止します。"
}
文字コード指定
Teamsに送り付けるとき↓
$enc = [System.Text.Encoding]::GetEncoding('ISO-8859-1')
$utf8Bytes = [System.Text.Encoding]::UTF8.GetBytes($msg)
$body = ConvertTo-JSON @{
text = $enc.GetString($utf8Bytes);
CSVがSJISだったとき↓
$OutputEncoding = [Console]::OutputEncoding
$insobj21 = Import-Csv -encoding default -Path $importPath4| Where-Object {$_.incident_mng_nm -eq $insid}
$insobj21|Export-Csv -NoTypeInformation -encoding default -Append $outfile3
※SJISを明示的に指定するのが指定できる文字コードの中に入ってなくて無理だったのでコンソールの文字コードを指定してCSVを取り込みと出力時にデフォルトを指定して文字化けしなくしてる。
文字コード解説
http://taeisheauton4programming.blogspot.com/2018/07/powershell-6shiftjiscsvimport-csv.html
エクセルからCSVにする
エクセルから単にCSVにする
$workdir = (Convert-path ~/$lastmonth)
mv ~/Downloads/i-filename1.xlsx $workdir/
cd $workdir
$tmp2 = "$workdir/i-filename1.xlsx"
$savePath = Join-Path $workdir "i-filename1.csv"
$objExcel = New-Object -ComObject Excel.Application
$objworkbook=$objExcel.Workbooks.Open($tmp2)
$objworkbook.SaveAs($savepath,6) ##6がCSVらしい
$objworkbook.Close($false)
PowerShellによるExcelデータからCSVデータへのコンバート
エクセルのままカラム一個消す
#mkdir ~/$lastmonth
$workdir = (Convert-path ~/$lastmonth)
cd $workdir
$tmp = "$workdir/w-filename2.xlsx"
$savePath = Join-Path $workdir "w-filename2.csv"
$objExcel = New-Object -ComObject Excel.Application
$objworkbook=$objExcel.Workbooks.Open($tmp)
$sheet = $objExcel.Worksheets.Item(1)
$sheet.Columns.item(44).Delete() ##カラム名が重複する不要な行削除
$objworkbook.SaveAs($savepath,6)
$objworkbook.Close($false)
※CSVインポートするときにカラム名が重複してるとエラーで読めない。
https://stackoverflow.com/questions/45999262/powershell-import-csv-without-headers-and-where-object/45999933
https://letspowershell.blogspot.com/2015/06/powershellexcel_55.html
カラム名重複してないならヘッダ消すのも可能
$list | ConvertTo-Csv -NoTypeInformation | Select-Object -Skip 1 | Set-Content "no_title_csv.csv"
https://teratail.com/questions/272241