狂自私

导航

python 仿造eveything软件-命令行

出于某些原因,不能使用eveything这个软件,但是这个软件的功能是真挺好用的,免去了在文件海洋中寻找;

扫描模块

import os
import re
import subprocess
import time
import string
'''
获取盘符列表,返回元组,元组第一个元素为bool值,表示函数成功与否,第二个值表示原因(失败的情况,字符串形式)或结果(成功的情况,列表形式)
'''

def get_DriveLetter():
    result_str = subprocess.run('''powershell $my_path = $env:temp + '\Get-Volume.txt';(Get-Volume).DriveLetter |Out-File -Encoding utf8 $my_path''')
    if result_str.returncode == 0:
        env_temp_path_str = os.environ['TEMP'] + r'\Get-Volume.txt'
        with open(env_temp_path_str, 'r', encoding='utf-8_sig')as fd:
            result = fd.read().splitlines()
            return (True, result)
    else:
        return (False, "cmd_Fail")

def get_DriveLetter_v2():
    '''
    获取盘符列表,返回元组,元组第一个元素为bool值,表示函数成功与否,第二个值表示原因(失败的情况,字符串形式)或结果(成功的情况,列表形式)
    此为v2版本,不同于v1版本使用powershell命令,而是通过猜测验证其是否存在。
    相比V1版本,会节约一点点时间
    '''
    result = []
    for i in string.ascii_uppercase:
        if os.path.exists(i+ ':\\'):
            result.append(i)
        #else的情况不考虑停止,实际上,用户的磁盘盘符完全可以不连续。
    return (True, result)

'''
初始目录initial_path已被记录,本次不记录
因为重复的文件名或目录名,并且要考虑到以后有拓展信息字段的需求,所以数据形状设计为这样:{'name':[['path1','other'],['path2','other'],]}
规定,初始目录字符串结尾必须带\
'''


def get_fileList(all_file_dict, initial_path):
    if os.path.isdir(initial_path):
        # 部分目录不允许访问,不过这部分目录也不在我的需求范围内。
        try:
            child_file_list = os.listdir(initial_path)
        except:
            print(initial_path)
            return
        for i in child_file_list:
            if i in all_file_dict.keys():
                all_file_dict[i].append([initial_path, ])
            else:
                all_file_dict[i] = [[initial_path, ], ]
            new_path_str = initial_path + i + '\\'
            if os.path.isdir(new_path_str):
                get_fileList(all_file_dict, new_path_str)

def get_fileList_v2(all_file_dict, initial_path):
    '''
    获取文件名、路径信息
    本函数是v2版本,用以提升扫描性能,用法与V1版本相同。
    initial_path:必定是目录
    '''
    try:
        child_file_list = os.scandir(initial_path)
    except:
        #print(initial_path)
        return
    for file_ in child_file_list:
        if file_.name in all_file_dict.keys():
            all_file_dict[file_.name].append([initial_path, ])
        else:
            all_file_dict[file_.name] = [[initial_path, ], ]
        #是否是目录
        if file_.is_dir():
            get_fileList_v2(all_file_dict,file_.path)

'''
将结果字典格式化输出到文件中存储。
输出到用户家目录下存放,名称叫eveything_data.data
'''


def Formatted_output(all_file_dict):
    out_file_path_str = os.environ['USERPROFILE'] + '\\eveything_data.data'
    with open(out_file_path_str, 'w', encoding='utf-8')as fd:
        for key, value in all_file_dict.items():
            for value_i in value:
                line_str = key
                for value_i_i in value_i:
                    line_str += "\t{}".format(value_i_i)
                fd.write(line_str + '\n')


'''
直接输出
输出到用户家目录下存放,名称叫eveything_data.data
'''


def Direct_output(all_file_dict):
    out_file_path_str = os.environ['USERPROFILE'] + 'eveything_data.data'
    with open(out_file_path_str, 'w', encoding='utf-8')as fd:
        print(all_file_dict, file=fd)


'''
主控函数
'''


def main():
    start_time = time.time()
    result = get_DriveLetter_v2()
    if result[0]:
        for i in result[1]:
            get_fileList_v2(all_file_dict=all_file_dict, initial_path='{}:\\'.format(i))
    # get_fileList(all_file_dict = all_file_dict,initial_path ='F:\\temp\\')
    Formatted_output(all_file_dict)

    # Direct_output(all_file_dict)

    end_time = time.time()
    print("扫描花费时间:{}".format(end_time - start_time))
    return all_file_dict


'''
全局变量,主要是为了共享变量,与交互式命令行中的代码
'''
all_file_dict = {}

if __name__ == '__main__':
    main()

 

 

人机交互模块

import 扫描模块 as saomiao
import re
import time
import threading
import os
from copy import _reconstruct
import colorama
import traceback

def get_data_csv(filepath_str):
    '''
    读取csv格式的数据文件(以制表符为分隔符的文件)
    '''
    if os.path.exists(filepath_str):
        with open(filepath_str,'r',encoding= 'utf-8-sig') as fd:
            lines_list= fd.read().splitlines()
            lines_list_ = [i.split('\t') for i in lines_list]
            return lines_list_
    else:
        print("路径:{};不存在!".format(filepath_str))
        return []

def get_data_dict():
    '''
    从扫描模块中直接获取其运行结果数据,这里会比较慢,大概在5-10分钟才能获取到结果
    '''
    saomiao.main()
#检验是否含有中文字符
def is_contains_chinese(strs):
    for _char in strs:
        if '\u4e00' <= _char <= '\u9fa5':
            return True
    return False
#计算字符串中中文字符个数
def count_chinese_num(strs):
    ret_num = 0
    for _char in strs:
        if '\u4e00' <= _char <= '\u9fa5':
            ret_num +=1
    return ret_num

def count_notASCII_num(strs):
    '''
    计算字符串中的非英文字符个数;
    不咋个完美,字符问题真他妈头大,有些非英文字符只占用一个字节,有些又占用两个,喵了个咪的!
    '''
    return int((len(strs.encode()) - len(strs))/2)

def beautify_output(data_result,regex_str):
    '''
    专门美化输出的函数
    参数是列表类型
    原本还计划着说根据命令行窗口大小来进行省略超长部分,但是我本就是查询路径信息,省略了我如何看呢,所以便不进行此操作
    '''
    #查询出最长的文件名长度
    name_Maxlen =0
    for i in data_result:
        if len(i[0]) > name_Maxlen:
            name_Maxlen = len(i[0])
    #获取命令行的高
    terminal_high = os.get_terminal_size()[1]
    #输出
    num_line = 0    #记录输出的行数
    for i in data_result:
        #目前只有两列信息,名称和路径,后续需要的话再加即可
        key_words_str = re.findall(regex_str,i[0],re.I)[0]
        key_words_index = i[0].find(key_words_str)
        print("{}\033[1;32;40m{}\033[0m{}{} | {}".format(i[0][0 : key_words_index],i[0][key_words_index : key_words_index + len(key_words_str)],i[0][key_words_index + len(key_words_str) :] , ' ' * (name_Maxlen - len(i[0]) - count_notASCII_num(i[0])),i[1]))
        num_line += 1
        if num_line % (terminal_high - 2) == 0: #这里并不是很准确的显示下一页,因为某些文件的路径过长会导致换行输出。
            print("按下字母q结束输出,按其他键继续输出下一页:",end='')
            in_str = input()
            if in_str == 'Q' or in_str == 'q':
                break
    print("总计{}个结果!".format(len(data_result)))    #这里输出的是文件的路径数量(即文件可能有重复)
    print("输出完毕!\n\n")

def beautify_output_v2(data_result,regex_str):
    '''
    专门美化输出的函数V2:新增自适应命令行界面输出的功能
    参数是列表类型
    原本还计划着说根据命令行窗口大小来进行省略超长部分,但是我本就是查询路径信息,省略了我如何看呢,所以便不进行此操作
    '''
    #print("data_result = {}".format(data_result))
    #累计输出了多少行
    exported_lines_num_int = 0
    while True:
        name_Maxlen =0
        temp_list = []
        #计算当前应该输出多少行
        out_lines_num_int = int(os.get_terminal_size()[1]) - 2
        if (out_lines_num_int + exported_lines_num_int) > len(data_result):
            if exported_lines_num_int == 0:
                temp_list = data_result[exported_lines_num_int :]
            else:
                temp_list = data_result[exported_lines_num_int - 1:]
        else:
            if exported_lines_num_int == 0 :
                temp_list = data_result[exported_lines_num_int :exported_lines_num_int + out_lines_num_int]
            else:
                temp_list = data_result[exported_lines_num_int - 1 :exported_lines_num_int + out_lines_num_int]
        for i in temp_list:
            if len(i[0]) > name_Maxlen:
                name_Maxlen = len(i[0])
        #print('name_Maxlen={}'.format(name_Maxlen))
        #print('out_lines_num_int={}'.format(out_lines_num_int))
        for i in temp_list:
            #目前只有两列信息,名称和路径,后续需要的话再加即可
            key_words_str = re.findall(regex_str,i[0],re.I)[0]
            key_words_index = i[0].find(key_words_str)
            print("{}\033[1;32;40m{}\033[0m{}{} | {}".format(i[0][0 : key_words_index],i[0][key_words_index : key_words_index + len(key_words_str)],i[0][key_words_index + len(key_words_str) :] , ' ' * (name_Maxlen - len(i[0]) - count_notASCII_num(i[0])),i[1]))
        exported_lines_num_int += out_lines_num_int
        if exported_lines_num_int >= len(data_result):
            break
        print("按下字母q结束输出,按其他键继续输出下一页:",end='')
        in_str = input()
        if in_str == 'Q' or in_str == 'q':
            break
    print("总计{}个结果!".format(len(data_result)))    #这里输出的是文件的路径数量(即文件可能有重复)
    print("输出完毕!\n\n")

def beautify_output_v3(data_result,regex_str):
    '''
    专门美化输出的函数V3:新增命令支持,暂时只支持open打开文件;输出下标
    参数是列表类型
    原本还计划着说根据命令行窗口大小来进行省略超长部分,但是我本就是查询路径信息,省略了我如何看呢,所以便不进行此操作
    '''
    #print("data_result = {}".format(data_result))
    #累计输出了多少行
    exported_lines_num_int = 0
    #总数据长度的字符长度
    all_data_len_int = len(str(len(data_result)))
    if len(data_result) != 0:
        while True:
            name_Maxlen =0
            temp_list = []
            #计算当前应该输出多少行
            out_lines_num_int = int(os.get_terminal_size()[1]) - 2
            if (out_lines_num_int + exported_lines_num_int) > len(data_result):
                if exported_lines_num_int == 0:
                    temp_list = data_result[exported_lines_num_int :]
                else:
                    temp_list = data_result[exported_lines_num_int - 1:]
            else:
                if exported_lines_num_int == 0 :
                    temp_list = data_result[exported_lines_num_int :exported_lines_num_int + out_lines_num_int]
                else:
                    temp_list = data_result[exported_lines_num_int - 1 :exported_lines_num_int + out_lines_num_int]
            for i in temp_list:
                if len(i[0]) > name_Maxlen:
                    name_Maxlen = len(i[0])
            #print('name_Maxlen={}'.format(name_Maxlen))
            #print('out_lines_num_int={}'.format(out_lines_num_int))
            for i in temp_list:
                #目前只有两列信息,名称和路径,后续需要的话再加即可
                key_words_str = re.findall(regex_str,i[0],re.I)[0]
                key_words_index = i[0].find(key_words_str)
                print("{}{} | {}\033[1;32;40m{}\033[0m{}{} | {}".format(exported_lines_num_int + 1 , ' ' * (all_data_len_int - len(str(exported_lines_num_int + 1))), i[0][0 : key_words_index],i[0][key_words_index : key_words_index + len(key_words_str)],i[0][key_words_index + len(key_words_str) :] , ' ' * (name_Maxlen - len(i[0]) - count_notASCII_num(i[0])),i[1]))
                exported_lines_num_int += 1
            #print("按下字母q结束输出,输入:open 序号 可以打开对应的文件;按其他键继续输出下一页:",end='')
            processed_cmd_ret_int = dealWith_user_cmd(data_result)
            if processed_cmd_ret_int == -1:
                break
            elif processed_cmd_ret_int == 0:
                #什么都不做
                pass
            if exported_lines_num_int >= len(data_result):
                break
    print("总计{}个结果!输出完毕!\n\n".format(len(data_result)))

def dealWith_user_cmd(data):
    '''
    处理用户输入的命令
        返回0表示继续输出
        返回-1表示结束输出
    '''

    while True:
        print("按下字母q结束输出;输入::open 序号 可以打开对应的文件;输入::del 序号 可以删除对应的文件;按其他键继续输出下一页:", end='')
        cmd_str = input()
        if len(cmd_str) == 0:
            return 0
        if cmd_str == 'Q' or cmd_str == 'q':
            return -1
        elif cmd_str[0] == ':':
            #open命令最少7个字符
            if len(cmd_str) >= 7:
                if cmd_str[1:5] == 'open':
                    num_ = cmd_str[5:]
                    index_int = re.findall(r'\d+',num_)
                    if len(index_int) > 0:
                        index_int = int(index_int[0])
                        if index_int <= len(data):
                            ret_ = open_file(data[int(index_int) - 1][1] + "\\" + data[int(index_int) - 1][0])
                            if(ret_):
                                print("文件(夹)打开成功。")
                            else:
                                print("文件(夹)打开失败。")
                        else:
                            print("不存在这条数据!")
                #return 1
            elif  len(cmd_str) >= 6:
                #del命令最少5个字符
                if cmd_str[1:4] == 'del':
                    num_ = cmd_str[4:]
                    index_int = re.findall(r'\d+',num_)
                    if len(index_int) > 0:
                        index_int = int(index_int[0])
                        if index_int <= len(data):
                            ret_ = del_file(data[index_int - 1])
                            if(ret_):
                                print("文件(夹)删除成功。")
                            else:
                                print("文件(夹)删除失败。")
                        else:
                            print("不存在这条数据!")
                #return 1
            else:
                return 0
        else:
            return 0

def Human_computer_interaction():
    '''
    处理用户输入和系统的提示性输出的,返回元组,一个是原始输入的,另一个是处理之后的
    '''
    while True:
        print('请输入需要查找的文件表达式:')
        regex_str = input()
        if regex_str == '':
            continue
        else:
            #需要判断输入的正则表达式是否合法,不合法的话,就需要提示用户重新输入,之所以不纠正是因为不要猜测用户的意图。
            try:
                re.compile(regex_str)
            except:
                print("输入的正则表达式有误!")
                continue
            if regex_str[0] == '^':
                regex_str = regex_str[1:]
                replace_result_str = r"(?<=\t)"
            else:
                replace_result_str = r'(?<=\t)[^\t]*?'
            for index in range(len(regex_str)):
                if regex_str[index] == '.':
                    if index != 0 and regex_str[index - 1] == '\\':
                        replace_result_str += regex_str[index]
                    else:
                        replace_result_str += r'[^\t]'
                else:
                    replace_result_str += regex_str[index]
            if replace_result_str[-1] == "$":
                replace_result_str = replace_result_str[0:-1]
                replace_result_str += r'(?=\t)'
            else:
                replace_result_str += r'[^\t]*?(?=\t)'
            #print(replace_result_str)
            return (regex_str,replace_result_str)

def open_file(filepath):
    '''
    打开指定的文件,使用系统默认方式;
    打开之前会检查文件/目录是否存在。
    成功返回True,不成功返回False
    '''
    if os.path.exists(filepath):
        try:
            os.startfile(filepath)
        except OSError as msg:
            traceback.print_exc()
            return False
        return True
    else:
        print("该文件已经不存在了。")
        return False

def del_file(line_list):
    '''
        删除该文件,需要考虑
            是否有权限
            删除之后需要同步删除字典里面的数据中的路径,在这中,实质上是删除路径,若是路径只有一个,则键也需要删除
            如何杀死当前子进程,新起一个子进程用于更新数据。    未完成
        完成删除返回True,否则返回False
    '''
    name = line_list[0];
    path = line_list[1];
    file_path = "{}\\{}".format(path,name);
    ret_num = del_dictElement(name,path);
    if(ret_num == 1):
        if(del_file_system(file_path)):
            return True
        else:
            return False
    else:
        return False

def del_file_system(filepath):
    '''
        实际去做删除文件、目录操作的函数
        成功删除(或者不存在)返回True;否则返回FALSE
    '''
    if(os.path.exists(filepath)):
        try:
            if(os.path.isdir(filepath)):
                os.rmdir(filepath)
            else:
                os.remove(filepath)
        except e:
            print(e)
            return False
        return True
    else:
        #文件不存在了,虽非我之功,但也完成目的了。
        return True

def del_dictElement(name,path):
    """
        操作字典数据变量,删除对应的path,必要时还需删除对应key
        需要返回值,返回值含义
        0:表示扫描字典数据为空,未完成第一次扫描,此时不能执行扫描操作
        1:表示完成删除操作
        -1:未找到对应路径,未能完成删除
        -2:没有找到key,即文件名不存在与字典数据中
    """
    global saomiao_result_data
    if(len(saomiao_result_data)>0):
        if(name in saomiao_result_data.keys()):
            if(len(saomiao_result_data[name])>1):
                #不需要删除key
                list_ = saomiao_result_data[name]
                index = 0
                is_del = False
                for path_i in list_:
                    if(path_i == path):
                        del saomiao_result_data[name][index]
                        is_del = True
                    index += 1
                if(not is_del):
                    print("字典数据删除失败!未找到该路径")
                    return -1
                else:
                    return 1
            else:
                #需要删除key
                del saomiao_result_data[name]
                return 1
        else:
            print("找不到key!")
            return -2
    else:
        print("此时还不能执行删除操作哦,请耐心等待一下。")
        return 0

def restart_childThread():
    '''
    重启子线程 
    无返回值,默认全成功
    如何杀死子进程是个麻烦事儿
    '''
    global threading_Operating_condition
    if(threading_Operating_condition != ""):
        #存在子进程且子进程正在运行才有意义
        if(threading_Operating_condition.is_alive()):
            #子进程还在运行,重启
            pass
        else:
            #子进程已结束,重运行
            threading_Operating_condition = threading.Thread(target = get_data_dict)
            threading_Operating_condition.start()
    else:
        #不需要做处理
        pass

def find_match(data,regex_str):
    '''
    查询函数,从列表中读取
    '''
    #print("regex_str = {}".format(regex_str))
    start_time = time.time()
    result_list = []
    for i in data:
        result_regex = re.findall(regex_str,i[0],re.I)
        if len(result_regex) != 0:
            result_list.append(i)
    print('查询花费:{}秒。'.format(time.time() - start_time))
    return result_list

def find_match_dict(data,regex_str):
    '''
    查询函数,从字典中读取,并生成结果列表类型变量
    '''
    start_time = time.time()
    result_list = []
    if type({}) == type(data):
        for key in data.keys():
            result_regex = re.findall(regex_str,key,re.I)
            if len(result_regex) != 0:
                temp_ = {}
                temp_[key] = data[key]
                result_list += dict_Conversion_list(temp_)
    else:
        print("未传入字典类型变量!")
        exit()
    print('查询花费:{}秒。'.format(time.time() - start_time))
    return result_list

def find_match_dict_v2(data,regex_str):
    '''
    查询函数V2,从字典中读取,并生成结果列表类型变量,先将可以聚合成大字符串,在一次性匹配出所有符合条件的值。
    '''
    start_time = time.time()
    result_list = []
    if type({}) == type(data):
        keys = data.keys()
        keys_str = "\t"
        keys_str += '\t'.join(keys)
        keys_str += '\t'
        result_key = re.findall(regex_str,keys_str,re.I)
        print("匹配出{}个结果。".format(len(result_key)))   #这里输出的是不重复的文件数量
        #print("result_key = {}".format(result_key))
        for key in result_key:
            temp_ = {}
            temp_[key] = data[key]
            result_list += dict_Conversion_list(temp_)
    else:
        print("未传入字典类型变量!")
        exit()
    print('查询花费:{}秒。'.format(time.time() - start_time))
    #print("result_list = {}".format(result_list))
    return result_list

def dict_Conversion_list(dict_data):
    '''
    将字典键和值转变为列表
    '''
    if type({}) != type(dict_data):
        return []
    return_result = []
    for key,value in dict_data.items():
        for value_i in value:
            temp_list = []
            for value_i_i in value_i:
                temp_list.append(key)
                temp_list.append(value_i_i)
                return_result.append(temp_list)
    return return_result

def my_deepcopy(x):
    '''
    深拷贝,copy库的deepcopy性能不太行,所以在deepcopy的基础上改了一下
    这个深拷贝函数仅用于本脚本中,copy库中的deepcopy函数慢是有原因的,了解之后再使用。
    '''
    reductor = getattr(x, "__reduce_ex__", None)
    if reductor is not None:
        rv = reductor(4)
    y = _reconstruct(x, None, *rv)
    return y

def main():
    colorama.init(autoreset=True)   #启用颜色模块
    #程序刚运行时使用之前扫描的格式化输出结果文件
    data_file_path_str = r'C:\Users\gyj\eveything_data.data'
    data = get_data_csv(data_file_path_str)
    global threading_Operating_condition
    global saomiao_result_data
    while True:
        if threading_Operating_condition != '' and threading_Operating_condition.is_alive() == False:
            #线程结束,可以获取数据了
            _time_ = time.time()
            #释放空间
            if saomiao_result_data != '':
                saomiao_result_data = ""
            saomiao_result_data = my_deepcopy(saomiao.all_file_dict)
            #print("深拷贝所用时间:{}".format(time.time() - _time_))
            #深拷贝完成之后清除全局变量的数据,避免出现重复值的bug,同时节约空间
            saomiao.all_file_dict.clear()
            #销毁从文件中读取的数据
            if 'data' in locals().keys():
                del data
            #print("数据量:{}".format(len(saomiao_result_data)))
        #只有在线程结束或者未开始时才运行
        if threading_Operating_condition == '' or threading_Operating_condition.is_alive() == False:
            #get_data_dict()
            threading_Operating_condition = threading.Thread(target = get_data_dict)
            threading_Operating_condition.start()


        user_in_tuple = Human_computer_interaction()
        result_list = []
        if saomiao_result_data != '':
            result_list = find_match_dict_v2(data = saomiao_result_data,regex_str = user_in_tuple[1])
        else:
            #列表查询用原始输入
            result_list = find_match(data = data,regex_str = user_in_tuple[0])
        beautify_output_v3(result_list,user_in_tuple[0])

saomiao_result_data = ''                #全局变量,存储扫描模块返回来的数据
threading_Operating_condition = ''      #子线程运行情况
if __name__ == '__main__':
    main()

 

我很尽力的去做对齐了,但是这个超出了我能力范围,字符真是个好垃圾!

历经几次迭代和优化,现在查询96万个结果只需要3秒左右,优化显示效果,优化正则表达式匹配的速度

更新-----现在扫描的速度很快了,当然是不能跟everything相比了,它是直接读取ntfs的文件表而非扫描的。

运行结果:

 

 

 

 

posted on 2021-12-25 18:17  狂自私  阅读(224)  评论(0编辑  收藏  举报