You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python中os.rmdir/os.remove排除指定目录失效问题求助

修复Kodi清理脚本中/media目录误删的问题

我仔细排查了你的代码,发现media目录排除失效的核心问题是排除逻辑没有和实际的目录遍历删除流程有效绑定——你虽然把media加到了排除列表,但原代码的删除循环(你贴的代码未写完)大概率没在遍历到目录时先做排除判断就直接执行了删除操作。下面是具体问题分析和修复方案:

核心问题拆解

  1. 排除项未参与删除判断:你在keep_xvbmc分支里把'media'追加到了dir_exclude元组,但如果删除逻辑是直接遍历所有目录后删除,完全没做排除校验,这个配置就形同虚设。
  2. 缺少递归场景的排除处理:如果是递归删除子目录,不仅要检查当前目录名,还要确保不会误触排除目录的子内容(不过你的需求是保留整个media目录,重点是跳过目录本身)。
  3. 删除逻辑不完整:你贴的代码到try: for roo...就中断了,推测这部分遍历删除的核心逻辑没有加入排除判断环节。

修复后的完整代码(核心部分)

我补全并修正了删除逻辑,确保排除项真正生效:

import os
import re
import shutil
import xbmc
import xbmcaddon
import xbmcgui

# 替换为你的插件ID和常量定义
addon_id = "plugin.program.xvbmcinstaller.nl"
AddonTitle = "XvBMC"
USERADDONDATA = xbmc.translatePath(xbmcaddon.Addon().getAddonInfo('profile'))
USERDATA = xbmc.translatePath("special://userdata")
databasePath = os.path.join(USERDATA, "Database")

def rejuvXvbmc():
    yes_pressed = Common.message_yes_no(
        "[COLOR dodgerblue]"+AddonTitle+"[/COLOR] [COLOR red][B]- Reset![/B][/COLOR]",
        'Wilt u uw XvBMC \'build\' volledig opschonen (wipe) en Kodi Krypton [B]leeg[/B] her-configureren?',
        '[COLOR dimgray]Please confirm that you wish you wipe clean your current configuration and reconfigure Kodi.[/COLOR]'
    )
    if yes_pressed:
        dp = xbmcgui.DialogProgress()
        dp.create(
            "[COLOR white]"+AddonTitle+"[/COLOR] [COLOR red][B]- Reset![/B][/COLOR]",
            'Snelle XvBMC Krypton reset, even geduld...',
            ' ', '[COLOR dimgray](Quick XvBMC Krypton reset, please wait...)'
        )
        # 原有路径计算逻辑保留
        profileDir = xbmcaddon.Addon(id=addon_id).getAddonInfo('path')
        profileDir = xbmc.translatePath(profileDir)
        xbmcPath = os.path.join(profileDir,"..","..")
        xbmcPath = os.path.abspath(xbmcPath)
        Common.log("rejuvXvbmc.main_XvBMC: xbmcPath="+xbmcPath)

        # 初始排除项定义
        dir_exclude = ('addons','Database','packages','userdata')
        sub_dir_exclude = ('metadata.album.universal','metadata.artists.universal', 
                          'metadata.common.imdb.com','metadata.common.musicbrainz.org',
                          'metadata.common.theaudiodb.com','metadata.common.themoviedb.org', 
                          'metadata.themoviedb.org','metadata.tvdb.com', 
                          'plugin.program.super.favourites','plugin.program.xvbmcinstaller.nl',
                          'repository.xvbmc','resource.language.nl_nl','script.xvbmc.updatertools',
                          'service.xbmc.versioncheck','script.module.xbmc.shared', 
                          'skin.aeon.nox.spin','script.grab.fanart','service.library.data.provider',
                          'resource.images.recordlabels.white','resource.images.studios.coloured',
                          'resource.images.studios.white','xbmc.gui','script.skinshortcuts',
                          'script.module.simplejson','script.module.unidecode')
        file_exclude = ('guisettings.xml','kodi.log','Textures13.db')

        # 数据库文件排除逻辑保留
        dbList = os.listdir(databasePath)
        dbAddons = []
        for file in dbList:
            if re.findall('Addons(\d+)\.db', file):
                dbAddons.append(file)
        for file in dbAddons:
            try:
                file_exclude = (file,) + file_exclude
                Common.log("XvBMC.file_exclude_dB="+str(file_exclude))
            except:
                Common.log("XvBMC.file_exclude_dB=EXCEPTION")

        dp.update(11,'','***Clean: files+folders...')
        keep_xvbmc = Common.message_yes_no(
            "[COLOR white][B]"+AddonTitle+"[/B][/COLOR]",
            'Wilt u het XvBMC-NL basis \'framework\' handhaven na reset? Verwijderd alles behalve XvBMC (aanbeveling).',
            '[COLOR dimgray](do you wish to keep XvBMC\'s default framework?)[/COLOR]'
        )
        # 保留/不保留框架时的排除项更新逻辑保留
        if keep_xvbmc:
            dir_exclude = ('addon_data','media',) + dir_exclude
            sub_dir_exclude = ('inputstream.rtmp','keymaps','service.subtitles.addic7ed',
                              'service.subtitles.opensubtitles_by_opensubtitles','service.subtitles.opensubtitlesBeta',
                              'service.subtitles.podnapisi','service.subtitles.subscene',
                              'script.module.resolveurl','script.module.urlresolver',) + sub_dir_exclude
            file_exclude = ('advancedsettings.xml','favourites.xml','profiles.xml','RssFeeds.xml','sources.xml',) + file_exclude
        else:
            dir_exclude = ('addon_data',) + dir_exclude
            sub_dir_exclude = ('inputstream.rtmp',) + sub_dir_exclude
            file_exclude = ('advancedsettings.xml','RssFeeds.xml',) + file_exclude

        Common.log("XvBMC.dir_exclude="+str(dir_exclude))
        Common.log("XvBMC.sub_dir_exclude="+str(sub_dir_exclude))
        Common.log("XvBMC.file_exclude="+str(file_exclude))

        # 原有Superfavo和SkinShrtct删除逻辑保留
        Superfavo = xbmc.translatePath(os.path.join(USERADDONDATA,'plugin.program.super.favourites','Super Favourites'))
        SkinShrtct = xbmc.translatePath(os.path.join(USERDATA,'addon_data','script.skinshortcuts'))
        try:
            shutil.rmtree(Superfavo)
        except Exception as e:
            Common.log("rejuvXvbmc.keep_xvbmc: XvBMC-vOoDoO @ " + str(e))
        try:
            shutil.rmtree(SkinShrtct)
        except Exception as e:
            Common.log("rejuvXvbmc.keep_xvbmc: XvBMC-vOoDoO @ " + str(e))

        # -------------------------- 核心修复:带排除项的递归删除逻辑 --------------------------
        def delete_with_exclusions(root_path):
            # 先删文件再删目录(避免目录非空无法用os.rmdir删除)
            for entry in os.scandir(root_path):
                if entry.is_file():
                    # 文件排除判断
                    if entry.name not in file_exclude:
                        try:
                            os.remove(entry.path)
                            Common.log(f"Deleted file: {entry.path}")
                        except Exception as e:
                            Common.log(f"Failed to delete file {entry.path}: {str(e)}")
                elif entry.is_dir():
                    # 顶层目录排除判断(media就在这里被过滤)
                    if entry.name in dir_exclude:
                        Common.log(f"Skipping excluded directory: {entry.path}")
                        continue
                    # Addons目录下的子插件排除判断
                    if os.path.basename(os.path.dirname(entry.path)) == 'addons' and entry.name in sub_dir_exclude:
                        Common.log(f"Skipping excluded addon subdirectory: {entry.path}")
                        continue
                    # 递归处理子目录
                    delete_with_exclusions(entry.path)
                    # 递归完成后删除空目录
                    try:
                        os.rmdir(entry.path)
                        Common.log(f"Deleted empty directory: {entry.path}")
                    except Exception as e:
                        Common.log(f"Failed to delete directory {entry.path} (may not be empty): {str(e)}")

        # 执行删除(务必确认xbmcPath是正确的Kodi目录,避免误删系统文件!)
        delete_with_exclusions(xbmcPath)

        dp.close()
        Common.message_ok(AddonTitle, "Reset completed successfully!")

关键修复点说明

  1. 新增递归删除函数delete_with_exclusions:专门处理带排除项的清理,先处理文件再处理目录,确保目录为空时才能用os.rmdir删除。
  2. 双层目录排除校验:
    • 先检查当前目录名是否在dir_exclude中(media目录会在这里被直接跳过);
    • 针对addons目录下的子插件,额外校验是否在sub_dir_exclude列表中,避免误删指定插件。
  3. 文件排除校验:删除文件前先检查文件名是否在file_exclude中,符合条件的文件会被保留。
  4. 操作日志:每一步删除/跳过操作都记录日志,方便后续排查问题。

额外安全提示

请先在测试环境中运行脚本,确认xbmcPath指向的是正确的Kodi安装目录,避免误删系统文件!

内容的提问来源于stack exchange,提问作者APOC

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:58:46