前面介绍了如何枚举全部文件,这里介绍一下如何枚举特定的问题。比如,用 “M*.*” 匹配全部 M开头的文件。
参考 touch 命令的Source Code 很快有了方案,使用:ShellOpenFileMetaArg
对应的头文件在 \ShellPkg\Include\Library\ShellLib.h
/**
Opens a group of files based on a path.
This function uses the Arg to open all the matching files. Each matched
file has a SHELL_FILE_ARG structure to record the file information. These
structures are placed on the list ListHead. Users can get the SHELL_FILE_ARG
structures from ListHead to access each file. This function supports wildcards
and will process '?' and '*' as such. The list must be freed with a call to
ShellCloseFileMetaArg().
If you are NOT appending to an existing list *ListHead must be NULL. If
*ListHead is NULL then it must be callee freed.
@param[in] Arg The pointer to path string.
@param[in] OpenMode Mode to open files with.
@param[in, out] ListHead Head of linked list of results.
@retval EFI_SUCCESS The operation was sucessful and the list head
contains the list of opened files.
@retval != EFI_SUCCESS The operation failed.
@sa InternalShellConvertFileListType
**/
EFI_STATUS
EFIAPI
ShellOpenFileMetaArg (
IN CHAR16 *Arg,
IN UINT64 OpenMode,
IN OUT EFI_SHELL_FILE_INFO **ListHead
);
从介绍上来看,最主要的参数有2个:Arg输入要查找的路径,可以使用通配符。ListHead 返回结果。结果实际上是两部分一部分是 SHELL_FILE_ARG 结构体,另外一部分是 EFI_SHELL_FILE_INFO 结构体。前者只有一个,后者有一个或者很多个。他们使用链表结构联系在一起。上面这样的结构体感觉上很奇怪,不过确实是这样的。可以在Touch的 Source Code中看到。他使用 GetFirstNode 来跳过第一个不需要的结构体。这个函数可以在 \MdePkg\Library\BaseLib\LinkedList.c 里面看到
/**
Retrieves the first node of a doubly-linked list.
Returns the first node of a doubly-linked list. List must have been
initialized with INTIALIZE_LIST_HEAD_VARIABLE() or InitializeListHead().
If List is empty, then List is returned.
If List is NULL, then ASSERT().
If List was not initialized with INTIALIZE_LIST_HEAD_VARIABLE() or
InitializeListHead(), then ASSERT().
If PcdMaximumLinkedListLenth is not zero, and the number of nodes
in List, including the List node, is greater than or equal to
PcdMaximumLinkedListLength, then ASSERT().
@param List A pointer to the head node of a doubly-linked list.
@return The first node of a doubly-linked list.
@retval NULL The list is empty.
**/
LIST_ENTRY *
EFIAPI
GetFirstNode (
IN CONST LIST_ENTRY *List
)
{
//
// ASSERT List not too long
//
ASSERT (InternalBaseLibIsNodeInList (List, List, FALSE));
return List->ForwardLink;
}
最终编写程序如下
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <stdio.h>
#include <stdlib.h>
#include <wchar.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>
extern EFI_BOOT_SERVICES *gBS;
extern EFI_SYSTEM_TABLE *gST;
extern EFI_RUNTIME_SERVICES *gRT;
extern EFI_SHELL_PROTOCOL *gEfiShellProtocol;
void PrintShellFileInfo(EFI_SHELL_FILE_INFO *ShellFileInfo)
{
//Print(L"Status [%d]\n",ShellFileInfo-> Status);
Print(L"FullName [%s]\n",ShellFileInfo-> FullName);
//Print(L"FileName [%s]\n",ShellFileInfo-> FileName);
//Print(L"Handle [%d]\n",ShellFileInfo->Handle);
}
int
EFIAPI
main (
IN int Argc,
IN char **Argv
)
{
//EFI_FILE_HANDLE DirHandle;
RETURN_STATUS Status;
EFI_SHELL_FILE_INFO *FileList=NULL;
EFI_SHELL_FILE_INFO *Node;
Status = ShellOpenFileMetaArg(L"fsnt0:\\a*.*", EFI_FILE_MODE_READ, &FileList);
if(Status != RETURN_SUCCESS) {
Print(L"OpenFile failed!\n");
return EFI_SUCCESS;
}
//Print(L"Signature [%X]\n",((SHELL_FILE_ARG *)&FileList)->Signature);
//Print(L"Status []\n",((SHELL_FILE_ARG *)&FileList)->Status);
//Print(L"ParentName [%s]\n",((SHELL_FILE_ARG *)&FileList)->ParentName);
//Print(L"FullName [%s]\n",((SHELL_FILE_ARG *)&FileList)->FullName);
//Print(L"FileName [%s]\n",((SHELL_FILE_ARG *)&FileList)->FileName);
// check that we have at least 1 file
//
if (FileList == NULL || IsListEmpty(&FileList->Link)) {
Print(L"No Files Found!\n");
} else {
//
// loop through the list and make sure we are not aborting...
//
for ( Node = (EFI_SHELL_FILE_INFO*)GetFirstNode(&FileList->Link)
; !IsNull(&FileList->Link, &Node->Link) && !ShellGetExecutionBreakFlag()
; Node = (EFI_SHELL_FILE_INFO*)GetNextNode(&FileList->Link, &Node->Link)){
PrintShellFileInfo(Node);
//
// make sure the file opened ok
//
if (EFI_ERROR(Node->Status)){
Print(L"OpenFile Error!\n");
}
}
}
//
// Free the fileList
//
if (FileList != NULL && !IsListEmpty(&FileList->Link)) {
Status = ShellCloseFileMetaArg(&FileList);
}
FileList = NULL;
return EFI_SUCCESS;
}
运行结果
可以看出能够正常查找到我们需要的 fsnt0:x下面a开头的文件。
代码下载
===============================================================================
最后有一个问题:
前面说过,ShellOpenFileMetaArg 输出的结果是2部分组成的,我的程序只是输出了后面的那个结构体,那么前面的那个结构体呢?
Status = ShellOpenFileMetaArg(L"fsnt0:\\a*.*", EFI_FILE_MODE_READ, &FileList);
if(Status != RETURN_SUCCESS) {
Print(L"OpenFile failed!\n");
return EFI_SUCCESS;
}
Print(L"Signature [%X]\n",((SHELL_FILE_ARG *)&FileList)->Signature);
Print(L"Status []\n",((SHELL_FILE_ARG *)&FileList)->Status);
Print(L"ParentName [%s]\n",((SHELL_FILE_ARG *)&FileList)->ParentName);
Print(L"FullName [%s]\n",((SHELL_FILE_ARG *)&FileList)->FullName);
Print(L"FileName [%s]\n",((SHELL_FILE_ARG *)&FileList)->FileName);
结果出来之后非常奇怪的是 Signature 的输出并非固定的数值,而根据 EfiShellEnvironment2.h 来看,这个似乎应该是一个固定的值。
不知道是我理解有偏差还是输出方法不对。有懂的朋友请指教一下。谢谢!














