質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

Q&A

解決済

1回答

5794閲覧

PowerShell で ls の結果に grep をかけようとして select-string を記載するとファイル内を検索してしまうのはなぜですか?

standard-soft

総合スコア197

PowerShell

Windows PowerShellはコマンドラインインターフェースであり、システム管理を含むWindowsタスク自動化のためのスクリプト言語です。

0グッド

0クリップ

投稿2022/01/04 05:31

編集2022/01/04 07:49

概要はタイトルの通りとなります。

詳細を書きます。

Windows PowerShell で

ls

の結果のテキストをフィルタしようとして次のように記載しました。

ls | select-string .json

.jsonは例です。

このように書いて動かすと、ls の結果に対してではなく、lsでリストしたファイルの中身に対して、 .json という文字列を含む行が出力されてしまう様子に見えます。

なぜこのようになるのでしょうか?

Zuishinさんからコメントいただき、

単にlsの結果の中から .json ファイルのリストだけを取得したい場合にはどのようにするとよいのでしょうか?

という疑問に対しては、

ls *.json

でいいと教えていだきましたので、結果は得ることができたのですが、仕組みとして、ls の出力結果のテキストに対してのgrep(フィルタ)ではなく、lsでリストされたファイルの中身に対する grep(フィルタ) になっている仕組みも知っておきたいです。

たとえば、lsで出力される内容に対して、特定日付のファイルだけをgrepするなどが、select-stringで出来る方法がしりたかったり、あるいは、なぜ、select-stringがうまくいくコマンドとうまくいかないlsのようなコマンドがあるのか、ということについてお詳しい方おられましたら、教えてください。

よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2022/01/04 05:33

ls *.json ではいけませんか?
standard-soft

2022/01/04 07:40

おお、たしかに ls の場合はこれでいけるんですね。ありがとうございます。 なぜlsのときに、selet-stringが効かないのかがわからないので、質問を修正してみます。
Zuishin

2022/01/04 07:48

そういう仕様だからです。 https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/select-string?view=powershell-7.2 ls の出力は PSPath プロパティをもちますが、これは Select-String の LiteralPath のエイリアスなので、文字列ではなくファイル名が与えられたものと認識されます。 他の言語っぽく言うと、ファイル名を引数にしたオーバーロードはそのファイルの中身を調べます。
guest

回答1

0

ベストアンサー

PowerShell のパイプに流れるのはテキストではなくオブジェクトです。
これが画面に表示されるまたはファイルに保存される時などに文字列に加工されます。
しかし Select-String に渡った時にはまだ文字列ではなくオブジェクトです。

ls の出力するオブジェクトは FileSystemInfo ですが、PowerShell のオブジェクトはそのままではなく PSCustomObject でラップされるので、元のオブジェクトにはないカスタムメソッドやカスタムプロパティを追加できます。
ls は FileSystemInfo に PSPath というプロパティを自動的に追加します。

そして Select-String は入力に PSPath というプロパティを発見すると、それをファイル名と認識し、ファイルを開いてその中身を検索します。
これが今回の質問の原因です。

ファイル名や更新日時や属性などで検索したい場合には、通常 Select-String ではなく Where-Object を使います。

powershell

1Get-ChildItem | Where-Object { $_.Name -like '*.json' }

ここで $_.Name は現在処理の対象となっている FileSystemInfo の Name プロパティを指します。

これと同じものをエイリアスを使って書くと次のようになります。

powershell

1ls | ? { $_.Name -like '*.json' }

正規表現を使いたい場合には -like ではなく -match を使います。

Select-String を使いたい場合には、まず文字列に直してから行います。

powershell

1Get-ChildItem | Foreach-Object Name | Select-String '.json'

なぜ PSPath をファイル名として扱うかというと、コマンドレットにはパラメーターセットと呼ばれる、オーバーロードのようなものがあり、引数によって処理を変えるからです。
これは Get-Help で調べることができます。
そして Select-String は LiteralPath という引数が与えられた時にそれをファイル名とみなしますが、LiteralPath はプロパティとして与えることのできる ValueFromPipelineByPropertyName 属性を持つからです。
また LiteralPath は PSPath というエイリアスも持ちます。
これにより、Get-ChildItem の出力にある PSPath プロパティの値が Select-String の LiteralPath に割り当てられます。

投稿2022/01/04 09:25

Zuishin

総合スコア28669

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

standard-soft

2022/01/05 14:18

詳細まで素晴らしい回答ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問