前面介绍了如何枚举全部文件,这里介绍一下如何枚举特定的问题。比如,用 “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 来看,这个似乎应该是一个固定的值。
不知道是我理解有偏差还是输出方法不对。有懂的朋友请指教一下。谢谢!