我很难将文件传送到Get-Content
(PS 5.1版本(。
模块代码:
[Cmdlet(VerbsDiagnostic.Test, "FilePiping")]
public class PSTestFilePiping : PSCmdlet
{
[Parameter(Position = 0, Mandatory = true)]
public string FileName { get; set; }
protected override void ProcessRecord()
{
WriteObject(new System.IO.FileInfo(FileName));
}
}
命令:
$x = Test-FilePiping .readme.txt
$y = gci .readme.txt
(为了简单起见,我使用的是相对路径(
根据GetType()
:,$x
和$y
都是FileInfo
对象
IsPublic IsSerial Name BaseType
True True FileInfo System.IO.FileSystemInfo
但我做不到:
$x | gc
The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
但我可以:
$y | gc
这让我很困惑。
我哪里错了?
除了@santiago squarzon的有用答案外,这里还有两件事值得注意:
- 像
Get-Item
和Get-ChildItem
这样的PowerShell命令不仅仅是为FileSystem生成的,因此返回相关提供程序的信息,PSPath这样的值就是从这里来的 - 以
new FileInfo()
为目标的相对路径可能找不到您期望的文件,因此我们应该获取完整路径以备不时之需
这可以在cmdlet中实现,以便它返回Get-Content
所需的PSPath
属性。
样品:
dotnet new classlib -o PSSample -n PSSample
cd PSSample
dotnet add package PowerShellStandard.Library --version 5.1.0
code . # I am using VSC - open your editor in the directory.
# Set target framework to netstandard2.0 in .csproj
# Copy and paste below in to Class1.cs
dotnet restore
dotnet build
ipmo .binDebugnetstandard2.0PSSample.dll
Test-FilePiping -FileName .Class1.cs | FL *
Test-FilePiping -FileName .Class1.cs | Get-Content
Cmdlet类:
using System;
using System.IO;
using System.Linq;
using System.Management.Automation;
namespace PSSample
{
[Cmdlet(VerbsDiagnostic.Test, "FilePiping")]
public class PSTestFilePiping : PSCmdlet
{
[Parameter(Position = 0, Mandatory = true)]
public string FileName { get; set; }
protected override void ProcessRecord()
{
string filePath = GetRelativePath(FileName);
var item = InvokeProvider.Item.Get(filePath);
WriteObject(item, true); // true enumerates the collection, because InvokeProvider.Item.Get() returns a collection.
}
protected string GetRelativePath(string path)
{
string currentDir = GetVariableValue("PWD").ToString();
if (Path.IsPathRooted(path) == true || path.StartsWith("\", StringComparison.CurrentCulture))
{
// Nothing to see here.
}
else
{
if (path == ".")
{
path = currentDir;
}
else if (path.StartsWith("..", StringComparison.CurrentCulture))
{
path = Path.Combine(
string.Join("\",
currentDir.Split('\').Take(currentDir.Split('\').Count() - path.Split('\').Count(p => p == "..")).ToArray()
),
string.Join("\", path.Split('\').Where(f => f != "..").ToArray()));
}
else if (path.StartsWith(".", StringComparison.CurrentCulture))
{
path = Path.Combine(currentDir, path.Substring(2));
}
else
{
path = Path.Combine(currentDir, path);
}
}
return path;
}
}
}
输出:
PS C:CodePSSample> Test-FilePiping -FileName .Class1.cs | FL *
PSPath : Microsoft.PowerShell.CoreFileSystem::C:CodePSSampleClass1.cs
PSParentPath : Microsoft.PowerShell.CoreFileSystem::C:CodePSSample
PSChildName : Class1.cs
PSDrive : C
PSProvider : Microsoft.PowerShell.CoreFileSystem
PSIsContainer : False
Mode : -a----
VersionInfo : File: C:CodePSSampleClass1.cs
InternalName:
OriginalFilename:
FileVersion:
FileDescription:
Product:
ProductVersion:
Debug: False
Patched: False
PreRelease: False
PrivateBuild: False
SpecialBuild: False
Language:
BaseName : Class1
Target : {}
LinkType :
Name : Class1.cs
Length : 1947
DirectoryName : C:CodePSSample
Directory : C:CodePSSample
IsReadOnly : False
Exists : True
FullName : C:CodePSSampleClass1.cs
Extension : .cs
CreationTime : 02/05/2021 13:03:22
CreationTimeUtc : 02/05/2021 12:03:22
LastAccessTime : 02/05/2021 13:04:13
LastAccessTimeUtc : 02/05/2021 12:04:13
LastWriteTime : 02/05/2021 13:04:00
LastWriteTimeUtc : 02/05/2021 12:04:00
Attributes : Archive
PS C:CodePSSample> Test-FilePiping -FileName .Class1.cs | Get-Content
using System;
using System.IO;
using System.Linq;
using System.Management.Automation;
namespace PSSample
{
[Cmdlet(VerbsDiagnostic.Test, "FilePiping")]
public class PSTestFilePiping : PSCmdlet
{
[Parameter(Position = 0, Mandatory = true)]
public string FileName { get; set; }
protected override void ProcessRecord()
{
string filePath = GetRelativePath(FileName);
var item = InvokeProvider.Item.Get(filePath);
WriteObject(item, true); // true enumerates the collection, because InvokeProvider.Item.Get() returns a collection.
}
protected string GetRelativePath(string path)
{
string currentDir = GetVariableValue("PWD").ToString();
if (Path.IsPathRooted(path) == true || path.StartsWith("\", StringComparison.CurrentCulture))
{
// Nothing to see here.
}
else
{
if (path == ".")
{
path = currentDir;
}
else if (path.StartsWith("..", StringComparison.CurrentCulture))
{
path = Path.Combine(
string.Join("\",
currentDir.Split('\').Take(currentDir.Split('\').Count() - path.Split('\').Count(p => p == "..")).ToArray()
),
string.Join("\", path.Split('\').Where(f => f != "..").ToArray()));
}
else if (path.StartsWith(".", StringComparison.CurrentCulture))
{
path = Path.Combine(currentDir, path.Substring(2));
}
else
{
path = Path.Combine(currentDir, path);
}
}
return path;
}
}
}
用一些背景信息补充现有的有用答案:
是PowerShellFileSystem
提供程序使用PowerShell的ETS(扩展类型系统(,用附加属性(如PSPath
(以及其他几个[1]((装饰Get-ChildItem
和Get-Item
发出的System.IO.FileSystemInfo
实例(System.IO.FileInfo
和System.IO.DirectoryInfo
(。
管道输入对象的.PSPath
属性值是绑定到cmdlet(如Get-Content
(的-LiteralPath
参数的属性值,因为所述参数是用ValueFromPipelineByPropertyName
属性声明的,并且LiteralPath
用[Alias("PSPath")]
属性修饰;即PSPath
是别名(LiteralPath
的另一名称(。
在撰写本文时,PowerShell提供程序将这些ETS属性添加为实例属性,这就是直接构建的System.IO.FileInfo
不具有这些属性的原因,从而导致出现您看到的症状(.PSPath
属性的缺失阻止了绑定到-LiteralPath
参数(。
如果提供者使用类型级别ETS属性(与.NET类型本身相关,而不是与它的特定实例相关(,这个问题就会消失,因为无论实例是如何构造的,这些属性都会出现。
有关讨论,请参阅GitHub第4347期。
[1]添加属性的完整列表为:PSPath
、PSParentPath
、PSChildName
、PSDrive
、PSProvider
、PSIsContainer
,Mode
、BaseName
、Target
、LinkType
希望这能帮助您识别错误:
PS /> 'Hello world!' > test.txt
PS /> $reader = [System.IO.FileInfo]::new('test.txt')
如果我尝试将此对象直接管道传输到Get-Content
,它将抛出与您得到的错误相同的错误:
使用此:
PS /> $reader | Get-Content
或者这个:
PS /> $reader.FullName | Get-Content
结果:
Get-Content: The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
这是因为[System.IO.FileInfo]
返回的属性都与Get-Content
:接受为ValueFromPipelineByProperyName
的参数不匹配
PS /> $reader | gm -MemberType Property
TypeName: System.IO.FileInfo
Name MemberType Definition
---- ---------- ----------
Attributes Property System.IO.FileAttributes Attributes {get;set;}
CreationTime Property datetime CreationTime {get;set;}
CreationTimeUtc Property datetime CreationTimeUtc {get;set;}
Directory Property System.IO.DirectoryInfo Directory {get;}
DirectoryName Property string DirectoryName {get;}
Exists Property bool Exists {get;}
Extension Property string Extension {get;}
FullName Property string FullName {get;}
IsReadOnly Property bool IsReadOnly {get;set;}
LastAccessTime Property datetime LastAccessTime {get;set;}
LastAccessTimeUtc Property datetime LastAccessTimeUtc {get;set;}
LastWriteTime Property datetime LastWriteTime {get;set;}
LastWriteTimeUtc Property datetime LastWriteTimeUtc {get;set;}
Length Property long Length {get;}
Name Property string Name {get;}
然而,如果我们创建一个具有我在评论中提到的任何属性的对象,Get-Content
将接受它作为输入并读取它而不会出现任何问题:
PS /> [PSCustomObject]@{ Path = $reader.FullName } | Get-Content
Hello world!
PS /> [PSCustomObject]@{ LiteralPath = $reader.FullName } | Get-Content
Hello world!
PS /> [PSCustomObject]@{ PSPath = $reader.FullName } | Get-Content
Hello world!
Get-ChildItem
工作正常,因为返回的对象具有PSPath属性
识别哪些参数被接受为ValueFromPipelineByProperyName
的最佳方法是依赖MS文档:
-LiteralPath | |
---|---|
类型 | String[] |
别名 | PSPath,LP |
职位 | 已命名 |
默认值 | None |
接受管道输入 | 真 |
接受通配符 | False |