[PowerShell] たまに書くワンライナー

PowerShellには`力`があり、配列、連想配列、オブジェクト等を扱えます。パイプライン処理もできます。これを生かして手元で何かごちゃごちゃすると便利なのですが、普段使わないので忘れがちです。それを書き留めます。そして忘れます。


# 基礎
パイプライン処理では`foreach, where, select, sort`などのコマンドをよく使う。
渡されたデータの各要素に`$_`でアクセスできる。
`%`は`foreach`を、`?`は`where`を意味するエイリアス。ややこしいので気をつけて使う。

```powershell:（例）where でフィルター、foreachで加工、改行せずにコンソール出力
@(1..10) | where {$_ % 2 -eq 0} | foreach {"${_} "} | Write-Host -NoNewline
# 2 4 6 8 10 
```
（1-10の連番を生成し、2で割り切れる数のみ、空白で区切って、改行せずに出力）

# ファイル名の変更

```powershell:カレントディレクトリの全ファイルの拡張子を.txtにする
ls | foreach {ren $_ ($_.BaseName + ".txt")}
```

```powershell:.logファイルの拡張子を.txtにする
ls | ?{$_.Extension -eq ".log"} | %{ren $_ ($_.BaseName + ".txt")}
```

`ls`はカレントディレクトリのファイルを列挙する。各ファイルオブジェクトには`.BaseName`のようなメンバがある。
また、`ren`はファイルオブジェクトと新ファイル名を受け取ってリネームする。


# 文字列を扱う

```powershell:ファイルから空行を消す
cat sparse.txt | where {$_ -ne ""} > dense.txt
# sparse.txt の空行を消したものを dense.txt として出力
```

```powershell:（数値型変数の展開）
$n = 10

echo ("$n + 1 = " + ($n+1))
# 10 + 1 = 11

echo(      $n + 1 )
# 11
echo("" + ($n + 1))
# 11
echo("" +  $n + 1 )
# 101

echo("${n}yen")
# 10yen
echo('${n}yen')
# ${n}yen
```


# 日付時刻

```powershell:（Get-Dateを使う）
(Get-Date).GetType().Fullname
# System.DataTime という型

(Get-Date).AddHours(-1)
# 1時間前のDataTimeが得られる
```

```powershell:1時間以内に作成されたファイルを表示
ls | where {(Get-Date).AddHours(-1) -le $_.CreationTime}
```

```powershell:1時間以前に作成されたファイルを全消去（危ない）
ls | where {$_.CreationTime -le (Get-Date).AddHours(-1)} | rm
```
`()`でコマンドを囲むと結果をオブジェクトとして扱える

# 連番で何かする
`@(n..m)`で配列を作ってパイプで渡す。`(n..m)`とも書ける。

```powershell:CSVを作成（列名なし）
@(1..10000) | foreach {"${_},user_${_}"} > foo.csv
```
<dl>
  <dt>生成されるデータ</dt>
  <dd>1,user_1</dd>
  <dd>2,user_2 ...</dd>
</dl>

```powershell:値にダブルクォーテーションを含めたい場合
@(1..10000) | foreach{"$_" + ',"user ' + $_ + '　is here"'} > foo.csv
```
<dl>
  <dt>生成されるデータ</dt>
  <dd>1,"user 1 is here"</dd>
  <dd>2,"user 2 is here" ...</dd>
</dl>

- `""`で括らないと変数が展開されないので先頭は`"$_"`
- 文字としてのダブルクォーテーションは`''`の中に書いた方が分かりやすい
- 真ん中にある`$_`は`""`で括る必要はない
    - （前方の文字列と`+`しているので文字列と解釈される）

# 補足
## 文法をおさらい
- 変数の先頭に`$`が付く
- `|`でパイプ（次のコマンドに向けて出力）して、`>`とか`>>`でファイルに出力
- `$_`でパイプで送られてきたデータの各要素を参照
- `"`で囲むと`$n `や`${n}`という表記で、文字列中で変数を参照できる
- `'`で囲むと変数を展開しない
- `{}`で囲まれた処理を「スクリプトブロック」と言う
- `()`でコマンドを囲むと結果をオブジェクトとして扱える
- `[]`で配列やオブジェクトの各要素にアクセスする
    - `(ls)[0]` でカレントディレクトリのファイル1つを対象に取れる


## cmdletの補足
- `ls`は[Get-ChildItem](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.management/get-childitem?view=powershell-7.3)のエイリアスで、例文の場合`FileInfo`を返す
    - `BaseName`というプロパティに関するドキュメントが見つからない
    - [stackoverflow](https://stackoverflow.com/questions/21739926/fileinfo-basename-exists-in-powershell-but-not-in-straight-net)曰く、これは[ScriptProperty](https://learn.microsoft.com/ja-jp/powershell/scripting/developer/cmdlet/extending-properties-for-objects?view=powershell-7.3#script-properties)なので色々処理があって結局`Name`を取ってくるらしい
    - （[プロパティ値について](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.core/about/about_properties?view=powershell-7.3#property-values)）

```powershell:こんな感じでプロパティについて調べる（stackoberflowより）
ls testfile1.txt | Get-Member BaseName | format-list

TypeName   : System.IO.FileInfo
Name       : BaseName
MemberType : ScriptProperty
Definition : System.Object BaseName {get=if ($this.Extension.Length -gt 0){$this.Name.Remove($this.Name.Length - 
         $this.Extension.Length)}else{$this.Name};}
```

- `ren`は[Rename-Item](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.management/rename-item?view=powershell-7.3)のエイリアス
- [Get-Date](https://learn.microsoft.com/ja-jp/powershell/module/microsoft.powershell.utility/get-date?view=powershell-7.3)の返す`DateTime`について`オブジェクトのプロパティとメソッドを表示するには、パイプラインの下に オブジェクトを 送信します Get-Member。 たとえば、「 Get-Date | Get-Member 」のように入力します。`だそうです
    - いやドキュメントに書いといてくれよ


## これマジ？
`"a" + 1` 👈 😉「a1」
`1 + "a"` 👈 🤬「値”a”を型"System.Int32"に変換できません。」
（ `"" + 1 + "a"` 👈 😌「1a」）

`@(1..10)` 👈 😉「1, 2, ... 10」
`@(1...10)` 👈 😤「1, 0」（`.10`が`0.1`と解釈されて、丸めて`0`）
`@(1...50)` 👈 🤔「1, 0」
`@(1...51)` 👈 🤔「1」

`@(1...50000000000000005)` 👈 🤯「1, 0」
`@(1...50000000000000006)` 👈 😇「1」（環境に依存しそう）

**ビル・ゲイツ！！！**