我已经实现了一个简单的PowerShell NavigationCmdletProvider
。PowerShell提供程序相对路径制表符完成问题
对于那些不知道的人来说,这意味着我可以使用cmdlet创建一个管理单元,该管理单元实际上是一个虚拟文件系统驱动器;这个驱动器可以像任何普通文件夹一样从PowerShell安装和导航。针对驱动器的每个操作(例如,检查路径指向有效项目,获取文件夹中子项目的名称列表等)被映射到继承自NavigationCmdletProvider
类的.NET类的方法。
我正面临tab完成问题,并希望找到解决方案。我发现使用相对路径时,制表符填充会导致错误的结果。对于绝对路径,它工作正常。
对于那些不知道的人,NavigationCmdletProvider
的标签填写通过调用GetChildNames
方法的PowerShell工作,该方法被NavigationCmdletProvider
类覆盖。
的issue--
--Demonstration假设我有一个供应商, 'TEST',具有下列文件夹层次:
TEST::child1
TEST::child1\child1a
TEST::child1\child1b
TEST::child2
TEST::child2\child2a
TEST::child2\child2b
TEST::child3
TEST::child3\child3a
TEST::child3\child3b
绝对路径:
如果我输入“dir TEST::child1\
”并按标签几次,它给我的预计业绩:
> dir TEST::child1\child1a
> dir TEST::child1\child1b
相对路径:
首先,我浏览到 “TEST :: child1”:
> cd TEST::child1
然后,如果我输入“dir
space”并按标签几次,它给我不正确的结果:
> dir .\child1\child1a
> dir .\child1\child1b
我希望看到这些来代替:
> dir .\child1a
> dir .\child1b
这是在PowerShell中的错误,还是我做错了什么?
这里是为提供完整的,自包含的代码:
[CmdletProvider("TEST", ProviderCapabilities.None)]
public class MyTestProvider : NavigationCmdletProvider
{
private Node m_Root;
private void ConstructTestHierarchy()
{
//
// Create the nodes
//
Node root = new Node("");
Node child1 = new Node("child1");
Node child1a = new Node("child1a");
Node child1b = new Node("child1b");
Node child2 = new Node("child2");
Node child2a = new Node("child2a");
Node child2b = new Node("child2b");
Node child3 = new Node("child3");
Node child3a = new Node("child3a");
Node child3b = new Node("child3b");
//
// Construct node hierarchy
//
m_Root = root;
root.AddChild(child1);
child1.AddChild(child1a);
child1.AddChild(child1b);
root.AddChild(child2);
child2.AddChild(child2a);
child2.AddChild(child2b);
root.AddChild(child3);
child3.AddChild(child3a);
child3.AddChild(child3b);
}
public MyTestProvider()
{
ConstructTestHierarchy();
}
protected override bool IsValidPath(string path)
{
return m_Root.ItemExistsAtPath(path);
}
protected override bool ItemExists(string path)
{
return m_Root.ItemExistsAtPath(path);
}
protected override void GetChildNames(string path, ReturnContainers returnContainers)
{
var children = m_Root.GetItemAtPath(path).Children;
foreach (var child in children)
{
WriteItemObject(child.Name, child.Name, true);
}
}
protected override bool IsItemContainer(string path)
{
return true;
}
protected override void GetChildItems(string path, bool recurse)
{
var children = m_Root.GetItemAtPath(path).Children;
foreach (var child in children)
{
WriteItemObject(child.Name, child.Name, true);
}
}
}
/// <summary>
/// This is a node used to represent a folder inside a PowerShell provider
/// </summary>
public class Node
{
private string m_Name;
private List<Node> m_Children;
public string Name { get { return m_Name; } }
public ICollection<Node> Children { get { return m_Children; } }
public Node(string name)
{
m_Name = name;
m_Children = new List<Node>();
}
/// <summary>
/// Adds a node to this node's list of children
/// </summary>
public void AddChild(Node node)
{
m_Children.Add(node);
}
/// <summary>
/// Test whether a string matches a wildcard string ('*' must be at end of wildcardstring)
/// </summary>
private bool WildcardMatch(string basestring, string wildcardstring)
{
//
// If wildcardstring has no *, just do a string comparison
//
if (!wildcardstring.Contains('*'))
{
return String.Equals(basestring, wildcardstring);
}
else
{
//
// If wildcardstring is really just '*', then any name works
//
if (String.Equals(wildcardstring, "*"))
return true;
//
// Given the wildcardstring "abc*", we just need to test if basestring starts with "abc"
//
string leftOfAsterisk = wildcardstring.Split(new char[] { '*' })[0];
return basestring.StartsWith(leftOfAsterisk);
}
}
/// <summary>
/// Recursively check if "child1\child2\child3" exists
/// </summary>
public bool ItemExistsAtPath(string path)
{
//
// If path is self, return self
//
if (String.Equals(path, "")) return true;
//
// If path has no slashes, test if it matches the child name
//
if(!path.Contains(@"\"))
{
//
// See if any children have this name
//
foreach (var child in m_Children)
{
if (WildcardMatch(child.Name, path))
return true;
}
return false;
}
else
{
//
// Split the path
//
string[] pathChunks = path.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
//
// Take out the first chunk; this is the child we're going to search
//
string nextChild = pathChunks[0];
//
// Combine the rest of the path; this is the path we're going to provide to the child
//
string nextPath = String.Join(@"\", pathChunks.Skip(1).ToArray());
//
// Recurse into child
//
foreach (var child in m_Children)
{
if (String.Equals(child.Name, nextChild))
return child.ItemExistsAtPath(nextPath);
}
return false;
}
}
/// <summary>
/// Recursively fetch "child1\child2\child3"
/// </summary>
public Node GetItemAtPath(string path)
{
//
// If path is self, return self
//
if (String.Equals(path, "")) return this;
//
// If path has no slashes, test if it matches the child name
//
if (!path.Contains(@"\"))
{
//
// See if any children have this name
//
foreach (var child in m_Children)
{
if (WildcardMatch(child.Name, path))
return child;
}
throw new ApplicationException("Child doesn't exist!");
}
else
{
//
// Split the path
//
string[] pathChunks = path.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
//
// Take out the first chunk; this is the child we're going to search
//
string nextChild = pathChunks[0];
//
// Combine the rest of the path; this is the path we're going to provide to the child
//
string nextPath = String.Join(@"\", pathChunks.Skip(1).ToArray());
//
// Recurse into child
//
foreach (var child in m_Children)
{
if (String.Equals(child.Name, nextChild))
return child.GetItemAtPath(nextPath);
}
throw new ApplicationException("Child doesn't exist!");
}
}
}
你能指出在dir例子中返回name的函数吗?你检查了什么是“正常”行为? (例如,如果您从“相对路径开始”,某些unix shell将执行相同的操作)。 – 2012-03-30 09:03:29
GetChildNames方法正在返回名称。在我的相对路径示例中,它返回“child1a”和“child1b”。我试过返回各种各样的组合,总是得到相同的结果。亲自尝试一下。 – 2012-03-30 14:37:34
我想任何人都会同意这不应该是“正常”的行为,如果是的话,这是一个错误。例如,当您在PowerShell中遍历C驱动器时,它从来不会这样做。我也不知道你的意思是“从何而来”。 – 2012-03-30 14:39:32