{"id":9424,"date":"2026-08-10T13:37:16","date_gmt":"2026-08-10T11:37:16","guid":{"rendered":"https:\/\/www.lukaswojcik.com\/blog\/werkzeuge\/pihole-adguard-blocklist-converter-overlap\/"},"modified":"2026-08-10T13:45:08","modified_gmt":"2026-08-10T11:45:08","slug":"pihole-adguard-blocklist-converter-overlap","status":"publish","type":"page","link":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/pihole-adguard-blocklist-converter-overlap\/","title":{"rendered":"Pi-hole-\/AdGuard-Blocklisten-Konverter mit \u00dcberschneidungsanalyse"},"content":{"rendered":"<div class=\"gtm-analyser-container\" style=\"background: var(--bg-panel, #1e1e24); padding: 25px; border-radius: 8px; border: 1px solid var(--border, #2a2a35);\">\n<p style=\"color: var(--text-secondary, #a0a0ab); margin-bottom: 20px;\">Pi-hole- und AdGuard-Blocklisten zusammenf\u00fchren, in das vom eigenen Resolver erwartete Format umwandeln und sehen, wie stark sich die Listen tats\u00e4chlich \u00fcberschneiden. Alles l\u00e4uft im Browser-Tab, nichts wird hochgeladen.<\/p>\n<div style=\"display: flex; gap: 15px; flex-wrap: wrap; margin-bottom: 20px;\">\n<div style=\"flex: 1; min-width: 280px;\">\n            <label style=\"color: var(--text-secondary, #a0a0ab); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Listenname<\/label><br \/>\n            <input type=\"text\" id=\"bl-name-1\" value=\"Blocklist 1\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ea); border-radius: 6px; margin-bottom: 8px;\"><br \/>\n            <label style=\"color: var(--text-secondary, #a0a0ab); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Eintr\u00e4ge &#8211; hosts, reine Domains, AdGuard- oder dnsmasq-Syntax, einer pro Zeile<\/label><br \/>\n            <textarea id=\"bl-list-1\" rows=\"8\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ea); border-radius: 6px; font-family: monospace; font-size: 0.85rem;\"># hosts<br \/>\n0.0.0.0 ads.example.com<br \/>\n0.0.0.0 example.com<br \/>\n0.0.0.0 tracker.example.net<br \/>\n127.0.0.1 metrics.example.org<br \/>\n0.0.0.0 pixel.example.net<\/textarea>\n        <\/div>\n<div style=\"flex: 1; min-width: 280px;\">\n            <label style=\"color: var(--text-secondary, #a0a0ab); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Listenname<\/label><br \/>\n            <input type=\"text\" id=\"bl-name-2\" value=\"Blocklist 2\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ea); border-radius: 6px; margin-bottom: 8px;\"><br \/>\n            <label style=\"color: var(--text-secondary, #a0a0ab); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Eintr\u00e4ge &#8211; hosts, reine Domains, AdGuard- oder dnsmasq-Syntax, einer pro Zeile<\/label><br \/>\n            <textarea id=\"bl-list-2\" rows=\"8\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ea); border-radius: 6px; font-family: monospace; font-size: 0.85rem;\">! adguard<br \/>\n||tracker.example.net^<br \/>\n||analytics.example.io^$important<br \/>\n||example.com^<br \/>\n||cdn.example.io^<br \/>\naddress=\/beacon.example.io\/0.0.0.0<\/textarea>\n        <\/div>\n<div style=\"flex: 1; min-width: 280px;\">\n            <label style=\"color: var(--text-secondary, #a0a0ab); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Listenname<\/label><br \/>\n            <input type=\"text\" id=\"bl-name-3\" value=\"Blocklist 3\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ea); border-radius: 6px; margin-bottom: 8px;\"><br \/>\n            <label style=\"color: var(--text-secondary, #a0a0ab); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Eintr\u00e4ge &#8211; hosts, reine Domains, AdGuard- oder dnsmasq-Syntax, einer pro Zeile<\/label><br \/>\n            <textarea id=\"bl-list-3\" rows=\"8\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ea); border-radius: 6px; font-family: monospace; font-size: 0.85rem;\"><\/textarea>\n        <\/div>\n<div style=\"flex: 1; min-width: 280px;\">\n            <label style=\"color: var(--text-secondary, #a0a0ab); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Listenname<\/label><br \/>\n            <input type=\"text\" id=\"bl-name-4\" value=\"Blocklist 4\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ea); border-radius: 6px; margin-bottom: 8px;\"><br \/>\n            <label style=\"color: var(--text-secondary, #a0a0ab); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Eintr\u00e4ge &#8211; hosts, reine Domains, AdGuard- oder dnsmasq-Syntax, einer pro Zeile<\/label><br \/>\n            <textarea id=\"bl-list-4\" rows=\"8\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ea); border-radius: 6px; font-family: monospace; font-size: 0.85rem;\"><\/textarea>\n        <\/div>\n<\/p><\/div>\n<div style=\"margin-bottom: 20px;\">\n        <label style=\"color: var(--text-secondary, #a0a0ab); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Whitelist (optional)<\/label><\/p>\n<p style=\"color: var(--text-secondary, #a0a0ab); font-size: 0.8rem; margin: 0 0 5px 0;\">Domains, die erreichbar bleiben m\u00fcssen. Ein Eintrag gilt als neutralisiert, wenn er einer Whitelist-Domain entspricht oder eine Subdomain davon ist.<\/p>\n<p>        <textarea id=\"bl-white\" rows=\"4\" class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ea); border-radius: 6px; font-family: monospace; font-size: 0.85rem;\">metrics.example.org<br \/>\nexample.io<\/textarea>\n    <\/div>\n<div style=\"margin-bottom: 15px; max-width: 480px;\">\n        <label style=\"color: var(--text-secondary, #a0a0ab); display: block; font-size: 0.85rem; margin-bottom: 5px;\">Zielformat<\/label><br \/>\n        <select id=\"bl-format\" class=\"form-control\" style=\"width: 100%; padding: 12px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ea); border-radius: 6px;\"><option value=\"hosts\">hosts-Datei &#8211; 0.0.0.0 domain.tld<\/option><option value=\"domains\">Reine Domainliste &#8211; domain.tld<\/option><option value=\"adguard\">AdGuard oder uBlock Origin &#8211; ||domain.tld^<\/option><option value=\"dnsmasq\">dnsmasq &#8211; address=\/domain.tld\/0.0.0.0<\/option><\/select>\n    <\/div>\n<div style=\"margin-bottom: 20px;\">\n        <label style=\"color: var(--text-secondary, #a0a0ab); display: block; font-size: 0.85rem; margin-bottom: 6px;\"><input type=\"checkbox\" id=\"bl-opt-white\" checked style=\"margin-right: 8px;\">Eintr\u00e4ge verwerfen, die die Whitelist neutralisiert<\/label><br \/>\n        <label style=\"color: var(--text-secondary, #a0a0ab); display: block; font-size: 0.85rem;\"><input type=\"checkbox\" id=\"bl-opt-red\" style=\"margin-right: 8px;\">Subdomains verwerfen, die ein \u00fcbergeordneter Eintrag bereits abdeckt (Wildcard-Abdeckung gibt es nur in den Formaten AdGuard und dnsmasq)<\/label>\n    <\/div>\n<div style=\"margin-bottom: 20px;\">\n        <button id=\"bl-btn\" class=\"button\" style=\"background: var(--accent, #4ade80); color: #0b1114; border: none; padding: 12px 24px; border-radius: 6px; font-weight: 700; cursor: pointer;\">Analysieren und umwandeln<\/button>\n    <\/div>\n<div id=\"bl-ergebnis\"><\/div>\n<\/div>\n<p><script>\n(function () {\n    'use strict';<\/p>\n<p>    \/\/ --- reine Rechenlogik, ohne jeden DOM-Zugriff ---<\/p>\n<p>    const LABEL_RE = \/^[a-z0-9_*]([a-z0-9_*-]*[a-z0-9_*])?$\/;<\/p>\n<p>    function normalisiereDomain(roh) {\n        if (typeof roh !== 'string') { return ''; }\n        let d = roh.trim().toLowerCase();\n        if (!d) { return ''; }\n        while (d.length > 1 && d.charAt(d.length - 1) === '.') { d = d.slice(0, d.length - 1); }\n        const fuehrenderPunkt = d.charAt(0) === '.';\n        const kern = fuehrenderPunkt ? d.slice(1) : d;\n        if (!kern || kern.length > 253) { return ''; }\n        const teile = kern.split('.');\n        if (teile.length < 2) { return ''; }\n        for (let i = 0; i < teile.length; i++) {\n            if (teile[i].length < 1 || teile[i].length > 63) { return ''; }\n            if (!LABEL_RE.test(teile[i])) { return ''; }\n        }\n        return fuehrenderPunkt ? '.' + kern : kern;\n    }<\/p>\n<p>    \/\/ null = bewusst uebersprungen (Kommentar, Leerzeile), [] = nicht erkannt\n    function parseZeileAlle(zeile) {\n        if (typeof zeile !== 'string') { return null; }\n        let z = zeile.replace(\/\\r\/g, '').trim();\n        if (!z) { return null; }\n        const erstes = z.charAt(0);\n        if (erstes === '#' || erstes === '!' || erstes === ';') { return null; }<\/p>\n<p>        let treffer = z.match(\/^\\|\\|([^\\^\\s\\\/\\$\\|]+)\\^\/);\n        if (treffer) {\n            const adguard = normalisiereDomain(treffer[1]);\n            return adguard ? [adguard] : [];\n        }<\/p>\n<p>        treffer = z.match(\/^(?:address|server|local)\\s*=\\s*\\\/([^\\\/\\s]+)\\\/\/);\n        if (treffer) {\n            const dnsmasq = normalisiereDomain(treffer[1]);\n            return dnsmasq ? [dnsmasq] : [];\n        }<\/p>\n<p>        const kommentar = z.search(\/\\s[#!]\/);\n        if (kommentar > -1) { z = z.slice(0, kommentar).trim(); }\n        if (!z) { return null; }<\/p>\n<p>        const teile = z.split(\/\\s+\/);\n        if (teile.length > 1) {\n            if (!\/^(?:0\\.0\\.0\\.0|127\\.0\\.0\\.1|255\\.255\\.255\\.255|::1|::)$\/.test(teile[0])) { return []; }\n            const domains = [];\n            for (let i = 1; i < teile.length; i++) {\n                const wirt = normalisiereDomain(teile[i]);\n                if (wirt) { domains.push(wirt); }\n            }\n            return domains;\n        }\n        const einzeln = normalisiereDomain(teile[0]);\n        return einzeln ? [einzeln] : [];\n    }\n\n    function parseZeile(zeile) {\n        const domains = parseZeileAlle(zeile);\n        if (domains === null) { return null; }\n        return domains.length ? domains[0] : '';\n    }\n\n    function parseListe(text) {\n        const ergebnis = { domains: [], eindeutig: 0, doppelt: 0, uebersprungen: 0, unlesbar: 0 };\n        if (typeof text !== 'string' || !text.trim()) { return ergebnis; }\n        const zeilen = text.split('\\n');\n        const gesehen = new Set();\n        for (let i = 0; i < zeilen.length; i++) {\n            const domains = parseZeileAlle(zeilen[i]);\n            if (domains === null) { ergebnis.uebersprungen++; continue; }\n            if (!domains.length) { ergebnis.unlesbar++; continue; }\n            for (let k = 0; k < domains.length; k++) {\n                if (gesehen.has(domains[k])) { ergebnis.doppelt++; continue; }\n                gesehen.add(domains[k]);\n                ergebnis.domains.push(domains[k]);\n            }\n        }\n        ergebnis.eindeutig = ergebnis.domains.length;\n        return ergebnis;\n    }\n\n    \/\/ alle Domains, auf die \"domain\" mit '.' + jene endet - laengste zuerst\n    function elternKandidaten(domain) {\n        const liste = [];\n        if (typeof domain !== 'string') { return liste; }\n        for (let i = 0; i < domain.length - 1; i++) {\n            if (domain.charAt(i) === '.') { liste.push(domain.slice(i + 1)); }\n        }\n        return liste;\n    }\n\n    function findeAbgedeckte(domains) {\n        const treffer = [];\n        const menge = new Set(domains);\n        for (let i = 0; i < domains.length; i++) {\n            const kandidaten = elternKandidaten(domains[i]);\n            for (let k = 0; k < kandidaten.length; k++) {\n                if (menge.has(kandidaten[k])) {\n                    treffer.push({ eintrag: domains[i], elter: kandidaten[k] });\n                    break;\n                }\n            }\n        }\n        return treffer;\n    }\n\n    function findeFreigegebene(domains, whitelist) {\n        const treffer = [];\n        const menge = new Set(whitelist);\n        if (menge.size === 0) { return treffer; }\n        for (let i = 0; i < domains.length; i++) {\n            const domain = domains[i];\n            if (menge.has(domain)) {\n                treffer.push({ eintrag: domain, elter: domain });\n                continue;\n            }\n            const kandidaten = elternKandidaten(domain);\n            for (let k = 0; k < kandidaten.length; k++) {\n                if (menge.has(kandidaten[k])) {\n                    treffer.push({ eintrag: domain, elter: kandidaten[k] });\n                    break;\n                }\n            }\n        }\n        return treffer;\n    }\n\n    function ueberschneidung(domainsA, domainsB) {\n        const menge = new Set(domainsB);\n        let anzahl = 0;\n        for (let i = 0; i < domainsA.length; i++) {\n            if (menge.has(domainsA[i])) { anzahl++; }\n        }\n        return anzahl;\n    }\n\n    function formatiereDomain(domain, ziel) {\n        if (ziel === 'hosts') { return '0.0.0.0 ' + domain; }\n        if (ziel === 'adguard') { return '||' + domain + '^'; }\n        if (ziel === 'dnsmasq') { return 'address=\/' + domain + '\/0.0.0.0'; }\n        return domain;\n    }\n\n    function formatiere(domains, ziel) {\n        if (!domains || !domains.length) { return ''; }\n        return domains.map(function (domain) { return formatiereDomain(domain, ziel); }).join('\\n');\n    }\n\n    function anteil(teil, ganz) {\n        if (!ganz) { return 0; }\n        return teil \/ ganz * 100;\n    }\n\n    function berechne(eingaben, whitelistText, ziel, optWhitelist, optRedundanz) {\n        const quellen = Array.isArray(eingaben) ? eingaben : [];\n        const listen = [];\n        for (let i = 0; i < quellen.length; i++) {\n            const text = quellen[i] &#038;&#038; typeof quellen[i].text === 'string' ? quellen[i].text : '';\n            if (!text.trim()) { continue; }\n            const geparst = parseListe(text);\n            const name = quellen[i].name &#038;&#038; String(quellen[i].name).trim() ? String(quellen[i].name).trim() : '#' + (i + 1);\n            listen.push({\n                name: name,\n                domains: geparst.domains,\n                eindeutig: geparst.eindeutig,\n                doppelt: geparst.doppelt,\n                uebersprungen: geparst.uebersprungen,\n                unlesbar: geparst.unlesbar\n            });\n        }\n\n        const leer = {\n            fehler: '', listen: [], whitelistAnzahl: 0, gelesen: 0, gesamtDoppelt: 0,\n            gesamtUebersprungen: 0, gesamtUnlesbar: 0, matrix: [], freigegeben: [],\n            abgedeckt: [], ausgabe: [], text: ''\n        };\n\n        if (listen.length === 0) { leer.fehler = 'Mindestens eine Liste einf\u00fcgen, dann die Analyse erneut starten.'; return leer; }\n\n        const whitelist = parseListe(whitelistText);\n\n        let gelesen = 0;\n        let gesamtUebersprungen = 0;\n        let gesamtUnlesbar = 0;\n        const gesehen = new Set();\n        const vereinigung = [];\n        for (let i = 0; i < listen.length; i++) {\n            gelesen += listen[i].eindeutig + listen[i].doppelt;\n            gesamtUebersprungen += listen[i].uebersprungen;\n            gesamtUnlesbar += listen[i].unlesbar;\n            for (let j = 0; j < listen[i].domains.length; j++) {\n                const domain = listen[i].domains[j];\n                if (!gesehen.has(domain)) { gesehen.add(domain); vereinigung.push(domain); }\n            }\n        }\n\n        if (vereinigung.length === 0) {\n            leer.fehler = 'In den eingef\u00fcgten Zeilen wurde keine g\u00fcltige Domain gefunden. Bitte die Syntax der Eintr\u00e4ge pr\u00fcfen.';\n            leer.listen = listen;\n            leer.whitelistAnzahl = whitelist.eindeutig;\n            leer.gesamtUebersprungen = gesamtUebersprungen;\n            leer.gesamtUnlesbar = gesamtUnlesbar;\n            return leer;\n        }\n\n        const matrix = [];\n        for (let i = 0; i < listen.length; i++) {\n            const zeile = [];\n            for (let j = 0; j < listen.length; j++) {\n                const gemeinsam = i === j ? listen[i].eindeutig : ueberschneidung(listen[i].domains, listen[j].domains);\n                zeile.push({ gemeinsam: gemeinsam, anteil: anteil(gemeinsam, listen[i].eindeutig) });\n            }\n            matrix.push(zeile);\n        }\n\n        const freigegeben = findeFreigegebene(vereinigung, whitelist.domains);\n        const freiMenge = new Set(freigegeben.map(function (eintrag) { return eintrag.eintrag; }));\n\n        let basis = vereinigung;\n        if (optWhitelist &#038;&#038; freiMenge.size > 0) {\n            basis = vereinigung.filter(function (domain) { return !freiMenge.has(domain); });\n        }<\/p>\n<p>        const abgedeckt = findeAbgedeckte(basis);\n        const abgedecktMenge = new Set(abgedeckt.map(function (eintrag) { return eintrag.eintrag; }));<\/p>\n<p>        let ausgabe = basis;\n        if (optRedundanz && abgedecktMenge.size > 0) {\n            ausgabe = basis.filter(function (domain) { return !abgedecktMenge.has(domain); });\n        }\n        ausgabe = ausgabe.slice().sort();<\/p>\n<p>        return {\n            fehler: '',\n            listen: listen,\n            whitelistAnzahl: whitelist.eindeutig,\n            gelesen: gelesen,\n            gesamtDoppelt: gelesen - vereinigung.length,\n            gesamtUebersprungen: gesamtUebersprungen,\n            gesamtUnlesbar: gesamtUnlesbar,\n            matrix: matrix,\n            freigegeben: freigegeben,\n            abgedeckt: abgedeckt,\n            ausgabe: ausgabe,\n            text: formatiere(ausgabe, ziel)\n        };\n    }<\/p>\n<p>    \/\/ --- Darstellung, ebenfalls ohne DOM-Zugriff ---<\/p>\n<p>    const ERSATZ = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;' };<\/p>\n<p>    function sicher(wert) {\n        return String(wert).replace(\/[&<>\"]\/g, function (zeichen) { return ERSATZ[zeichen]; });\n    }<\/p>\n<p>    function zahl(wert) {\n        if (typeof wert !== 'number' || !isFinite(wert)) { return '0'; }\n        return String(Math.round(wert));\n    }<\/p>\n<p>    function prozentText(wert) {\n        if (typeof wert !== 'number' || !isFinite(wert)) { return '0.0 %'; }\n        return wert.toFixed(1) + ' %';\n    }<\/p>\n<p>    function meldung(text) {\n        return '<\/p>\n<div style=\"background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); border-radius: 6px; padding: 15px; color: var(--text-primary, #e8e8ea);\">' + text + '<\/div>\n<p>';\n    }<\/p>\n<p>    function ueberschrift(text) {\n        return '<\/p>\n<h4 style=\"color: var(--text-primary, #e8e8ea); margin: 25px 0 10px 0; font-size: 1rem;\">' + text + '<\/h4>\n<p>';\n    }<\/p>\n<p>    function zeileWert(bezeichnung, wert) {\n        return '<\/p>\n<div style=\"display: flex; justify-content: space-between; gap: 15px; padding: 6px 0; border-bottom: 1px solid var(--border, #2a2a35);\">' +\n            '<span style=\"color: var(--text-secondary, #a0a0ab);\">' + bezeichnung + '<\/span>' +\n            '<span style=\"color: var(--text-primary, #e8e8ea); font-family: monospace;\">' + wert + '<\/span><\/div>\n<p>';\n    }<\/p>\n<p>    function eintragsListe(eintraege, beziehung) {\n        if (!eintraege.length) {\n            return '<\/p>\n<p style=\"color: var(--text-secondary, #a0a0ab); margin: 0;\">Keine Eintr\u00e4ge.<\/p>\n<p>';\n        }\n        const grenze = 200;\n        const teile = [];\n        for (let i = 0; i < eintraege.length &#038;&#038; i < grenze; i++) {\n            const zusatz = eintraege[i].elter === eintraege[i].eintrag ? '' :\n                ' <span style=\"color: var(--text-secondary, #a0a0ab);\">' + beziehung + ' ' + sicher(eintraege[i].elter) + '<\/span>';\n            teile.push('<\/p>\n<div>' + sicher(eintraege[i].eintrag) + zusatz + '<\/div>\n<p>');\n        }\n        if (eintraege.length > grenze) {\n            teile.push('<\/p>\n<div style=\"color: var(--text-secondary, #a0a0ab); margin-top: 6px;\">' +\n                'und {n} weitere Eintr\u00e4ge'.replace('{n}', zahl(eintraege.length - grenze)) + '<\/div>\n<p>');\n        }\n        return '<\/p>\n<div style=\"max-height: 220px; overflow: auto; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); border-radius: 6px; padding: 10px; font-family: monospace; font-size: 0.85rem; color: var(--text-primary, #e8e8ea);\">' +\n            teile.join('') + '<\/div>\n<p>';\n    }<\/p>\n<p>    function baueHtml(ergebnis, optWhitelist, optRedundanz) {\n        if (ergebnis.fehler) { return meldung(ergebnis.fehler); }<\/p>\n<p>        const teile = [];<\/p>\n<p>        teile.push(ueberschrift('Umgewandelte Liste'));\n        teile.push('<\/p>\n<p style=\"color: var(--text-secondary, #a0a0ab); margin: 0 0 8px 0; font-size: 0.85rem;\">Den Text unten markieren und in die eigene Blocklisten-Datei kopieren.<\/p>\n<p>');\n        teile.push('<textarea id=\"bl-ausgabe\" rows=\"12\" readonly class=\"form-control\" style=\"width: 100%; padding: 10px; background: var(--bg-body, #14141a); border: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ea); border-radius: 6px; font-family: monospace; font-size: 0.85rem;\"><\/textarea>');<\/p>\n<p>        teile.push(ueberschrift('Summen'));\n        teile.push(zeileWert('Eindeutige Domains in der Ausgabe', zahl(ergebnis.ausgabe.length)));\n        teile.push(zeileWert('Entfernte doppelte Eintr\u00e4ge', zahl(ergebnis.gesamtDoppelt)));\n        teile.push(zeileWert('\u00dcbersprungene Kommentar- und Leerzeilen', zahl(ergebnis.gesamtUebersprungen)));\n        teile.push(zeileWert('Nicht erkannte Zeilen', zahl(ergebnis.gesamtUnlesbar)));\n        teile.push(zeileWert('Erkannte Whitelist-Domains', zahl(ergebnis.whitelistAnzahl)));\n        teile.push(zeileWert('Von der Whitelist neutralisierte Eintr\u00e4ge', zahl(ergebnis.freigegeben.length) + ' - ' + (optWhitelist ? 'aus der Ausgabe entfernt' : 'in der Ausgabe behalten')));\n        teile.push(zeileWert('Von einem \u00fcbergeordneten Eintrag abgedeckte Eintr\u00e4ge', zahl(ergebnis.abgedeckt.length) + ' - ' + (optRedundanz ? 'aus der Ausgabe entfernt' : 'in der Ausgabe behalten')));<\/p>\n<p>        teile.push(ueberschrift('Je Liste'));\n        const kopf = '<\/p>\n<th style=\"text-align: left; padding: 8px; border-bottom: 1px solid var(--border, #2a2a35); color: var(--text-secondary, #a0a0ab); font-weight: 600;\">';\n        const zelle = '<\/p>\n<td style=\"padding: 8px; border-bottom: 1px solid var(--border, #2a2a35); color: var(--text-primary, #e8e8ea);\">';\n        let tabelle = '<\/p>\n<div style=\"overflow-x: auto;\">\n<table style=\"width: 100%; border-collapse: collapse; font-size: 0.9rem;\">\n<thead>\n<tr>' +\n            kopf + 'Liste<\/th>\n<p>' + kopf + 'Eindeutig<\/th>\n<p>' + kopf + 'Duplikate<\/th>\n<p>' + kopf + '\u00dcbersprungen<\/th>\n<p>' + kopf + 'Unlesbar<\/th>\n<\/tr>\n<\/thead>\n<tbody>';\n        for (let i = 0; i < ergebnis.listen.length; i++) {\n            const liste = ergebnis.listen[i];\n            tabelle += '\n\n<tr>' + zelle + sicher(liste.name) + '<\/td>\n<p>' +\n                zelle + zahl(liste.eindeutig) + '<\/td>\n<p>' +\n                zelle + zahl(liste.doppelt) + '<\/td>\n<p>' +\n                zelle + zahl(liste.uebersprungen) + '<\/td>\n<p>' +\n                zelle + zahl(liste.unlesbar) + '<\/td>\n<\/tr>\n<p>';\n        }\n        tabelle += '<\/tbody>\n<\/table>\n<\/div>\n<p>';\n        teile.push(tabelle);<\/p>\n<p>        if (ergebnis.listen.length > 1) {\n            teile.push(ueberschrift('\u00dcberschneidungsmatrix'));\n            teile.push('<\/p>\n<p style=\"color: var(--text-secondary, #a0a0ab); margin: 0 0 10px 0; font-size: 0.85rem;\">Jede Zelle z\u00e4hlt die Domains der Zeilenliste, die auch in der Spaltenliste vorkommen, gefolgt vom Anteil an der Zeilenliste.<\/p>\n<p>');\n            let matrixHtml = '<\/p>\n<div style=\"overflow-x: auto;\">\n<table style=\"width: 100%; border-collapse: collapse; font-size: 0.9rem;\">\n<thead>\n<tr>' + kopf + '<\/th>\n<p>';\n            for (let i = 0; i < ergebnis.listen.length; i++) {\n                matrixHtml += kopf + sicher(ergebnis.listen[i].name) + '<\/th>\n<p>';\n            }\n            matrixHtml += '<\/tr>\n<\/thead>\n<tbody>';\n            for (let i = 0; i < ergebnis.matrix.length; i++) {\n                matrixHtml += '\n\n<tr>' + zelle + '<strong>' + sicher(ergebnis.listen[i].name) + '<\/strong><\/td>\n<p>';\n                for (let j = 0; j < ergebnis.matrix[i].length; j++) {\n                    const feld = ergebnis.matrix[i][j];\n                    matrixHtml += zelle + '<span style=\"font-family: monospace;\">' + zahl(feld.gemeinsam) + '<\/span>' +\n                        '<span style=\"color: var(--text-secondary, #a0a0ab); font-family: monospace;\"> (' + prozentText(feld.anteil) + ')<\/span><\/td>\n<p>';\n                }\n                matrixHtml += '<\/tr>\n<p>';\n            }\n            matrixHtml += '<\/tbody>\n<\/table>\n<\/div>\n<p>';\n            teile.push(matrixHtml);\n        }<\/p>\n<p>        teile.push(ueberschrift('Von der Whitelist neutralisiert'));\n        teile.push('<\/p>\n<p style=\"color: var(--text-secondary, #a0a0ab); margin: 0 0 10px 0; font-size: 0.85rem;\">Diese Eintr\u00e4ge greifen nie, weil die Whitelist sie wieder erlaubt.<\/p>\n<p>');\n        teile.push(eintragsListe(ergebnis.freigegeben, 'erlaubt durch'));<\/p>\n<p>        teile.push(ueberschrift('Redundante Eintr\u00e4ge'));\n        teile.push('<\/p>\n<p style=\"color: var(--text-secondary, #a0a0ab); margin: 0 0 10px 0; font-size: 0.85rem;\">Ein breiterer Eintrag deckt diese Domains bereits ab. In den Formaten AdGuard und dnsmasq lassen sie sich entfernen, ohne dass sich am Blockieren etwas \u00e4ndert.<\/p>\n<p>');\n        teile.push(eintragsListe(ergebnis.abgedeckt, 'abgedeckt durch'));<\/p>\n<p>        return teile.join('');\n    }<\/p>\n<p>    \/\/ fuer die Pruefung von aussen erreichbar\n    if (typeof window !== 'undefined') {\n        window.LW_TEST = window.LW_TEST || {};\n        window.LW_TEST.BL = {\n            berechne: berechne,\n            formatiere: formatiere,\n            normalisiereDomain: normalisiereDomain,\n            parseZeile: parseZeile,\n            parseZeileAlle: parseZeileAlle,\n            parseListe: parseListe,\n            elternKandidaten: elternKandidaten,\n            findeAbgedeckte: findeAbgedeckte,\n            findeFreigegebene: findeFreigegebene,\n            ueberschneidung: ueberschneidung,\n            formatiereDomain: formatiereDomain,\n            baueHtml: baueHtml\n        };\n    }<\/p>\n<p>    \/\/ --- DOM-Anbindung, sauber abgesichert ---\n    const btn = document.getElementById('bl-btn');\n    const out = document.getElementById('bl-ergebnis');\n    if (!btn || !out) { return; }<\/p>\n<p>    function wert(id) {\n        const feld = document.getElementById(id);\n        return feld && typeof feld.value === 'string' ? feld.value : '';\n    }<\/p>\n<p>    function angehakt(id) {\n        const feld = document.getElementById(id);\n        return feld ? feld.checked === true : false;\n    }<\/p>\n<p>    btn.addEventListener('click', function () {\n        try {\n            const eingaben = [];\n            for (let i = 1; i <= 4; i++) {\n                eingaben.push({ name: wert('bl-name-' + i), text: wert('bl-list-' + i) });\n            }\n            const ziel = wert('bl-format') || 'hosts';\n            const optWhitelist = angehakt('bl-opt-white');\n            const optRedundanz = angehakt('bl-opt-red');\n            const ergebnis = berechne(eingaben, wert('bl-white'), ziel, optWhitelist, optRedundanz);\n            out.innerHTML = baueHtml(ergebnis, optWhitelist, optRedundanz);\n            const feld = document.getElementById('bl-ausgabe');\n            if (feld) { feld.value = ergebnis.text; }\n        } catch (fehler) {\n            out.innerHTML = meldung('Die Listen konnten nicht analysiert werden. Bitte die Eingabe pr\u00fcfen und erneut versuchen.');\n        }\n    });\n})();\n<\/script><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Pi-hole- und AdGuard-Blocklisten zusammenf\u00fchren, in das vom eigenen Resolver erwartete Format umwandeln und sehen, wie stark sich die Listen tats\u00e4chlich \u00fcberschneiden. Alles l\u00e4uft im Browser-Tab, nichts wird hochgeladen. Listenname Eintr\u00e4ge &#8211; hosts, reine Domains, AdGuard- oder dnsmasq-Syntax, einer pro Zeile # hosts 0.0.0.0 ads.example.com 0.0.0.0 example.com 0.0.0.0 tracker.example.net 127.0.0.1 metrics.example.org 0.0.0.0 pixel.example.net Listenname Eintr\u00e4ge &#8211; [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":9283,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"template-tool-base.php","meta":{"footnotes":""},"class_list":["post-9424","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Pi-hole-\/AdGuard-Blocklisten-Konverter mit \u00dcberschneidungsanalyse - Lukas Wojcik - Blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/pihole-adguard-blocklist-converter-overlap\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Pi-hole-\/AdGuard-Blocklisten-Konverter mit \u00dcberschneidungsanalyse - Lukas Wojcik - Blog\" \/>\n<meta property=\"og:description\" content=\"Pi-hole- und AdGuard-Blocklisten zusammenf\u00fchren, in das vom eigenen Resolver erwartete Format umwandeln und sehen, wie stark sich die Listen tats\u00e4chlich \u00fcberschneiden. Alles l\u00e4uft im Browser-Tab, nichts wird hochgeladen. Listenname Eintr\u00e4ge &#8211; hosts, reine Domains, AdGuard- oder dnsmasq-Syntax, einer pro Zeile # hosts 0.0.0.0 ads.example.com 0.0.0.0 example.com 0.0.0.0 tracker.example.net 127.0.0.1 metrics.example.org 0.0.0.0 pixel.example.net Listenname Eintr\u00e4ge &#8211; [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/pihole-adguard-blocklist-converter-overlap\/\" \/>\n<meta property=\"og:site_name\" content=\"Lukas Wojcik - Blog\" \/>\n<meta property=\"article:modified_time\" content=\"2026-08-10T11:45:08+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data1\" content=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/pihole-adguard-blocklist-converter-overlap\\\/\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/pihole-adguard-blocklist-converter-overlap\\\/\",\"name\":\"Pi-hole-\\\/AdGuard-Blocklisten-Konverter mit \u00dcberschneidungsanalyse - Lukas Wojcik - Blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\"},\"datePublished\":\"2026-08-10T11:37:16+00:00\",\"dateModified\":\"2026-08-10T11:45:08+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/pihole-adguard-blocklist-converter-overlap\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/pihole-adguard-blocklist-converter-overlap\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/de\\\/werkzeuge\\\/pihole-adguard-blocklist-converter-overlap\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Toolbox\",\"item\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/toolbox-2\\\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Pi-hole-\\\/AdGuard-Blocklisten-Konverter mit \u00dcberschneidungsanalyse\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/\",\"name\":\"Lukas Wojcik - Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/#\\\/schema\\\/person\\\/895f7604f9b6b71aad9bba33af28d0f9\",\"name\":\"luky\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"url\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"contentUrl\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\",\"width\":424,\"height\":636,\"caption\":\"luky\"},\"logo\":{\"@id\":\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\\\/wp-content\\\/uploads\\\/2026\\\/07\\\/lw-x2.jpg\"},\"sameAs\":[\"https:\\\/\\\/www.lukaswojcik.com\\\/blog\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Pi-hole-\/AdGuard-Blocklisten-Konverter mit \u00dcberschneidungsanalyse - Lukas Wojcik - Blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/pihole-adguard-blocklist-converter-overlap\/","og_locale":"en_US","og_type":"article","og_title":"Pi-hole-\/AdGuard-Blocklisten-Konverter mit \u00dcberschneidungsanalyse - Lukas Wojcik - Blog","og_description":"Pi-hole- und AdGuard-Blocklisten zusammenf\u00fchren, in das vom eigenen Resolver erwartete Format umwandeln und sehen, wie stark sich die Listen tats\u00e4chlich \u00fcberschneiden. Alles l\u00e4uft im Browser-Tab, nichts wird hochgeladen. Listenname Eintr\u00e4ge &#8211; hosts, reine Domains, AdGuard- oder dnsmasq-Syntax, einer pro Zeile # hosts 0.0.0.0 ads.example.com 0.0.0.0 example.com 0.0.0.0 tracker.example.net 127.0.0.1 metrics.example.org 0.0.0.0 pixel.example.net Listenname Eintr\u00e4ge &#8211; [&hellip;]","og_url":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/pihole-adguard-blocklist-converter-overlap\/","og_site_name":"Lukas Wojcik - Blog","article_modified_time":"2026-08-10T11:45:08+00:00","og_image":[{"width":1200,"height":630,"url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/08\/og-default.jpg","type":"image\/jpeg"}],"twitter_card":"summary_large_image","twitter_misc":{"Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/pihole-adguard-blocklist-converter-overlap\/","url":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/pihole-adguard-blocklist-converter-overlap\/","name":"Pi-hole-\/AdGuard-Blocklisten-Konverter mit \u00dcberschneidungsanalyse - Lukas Wojcik - Blog","isPartOf":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#website"},"datePublished":"2026-08-10T11:37:16+00:00","dateModified":"2026-08-10T11:45:08+00:00","breadcrumb":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/pihole-adguard-blocklist-converter-overlap\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/pihole-adguard-blocklist-converter-overlap\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.lukaswojcik.com\/blog\/de\/werkzeuge\/pihole-adguard-blocklist-converter-overlap\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.lukaswojcik.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Toolbox","item":"https:\/\/www.lukaswojcik.com\/blog\/toolbox-2\/"},{"@type":"ListItem","position":3,"name":"Pi-hole-\/AdGuard-Blocklisten-Konverter mit \u00dcberschneidungsanalyse"}]},{"@type":"WebSite","@id":"https:\/\/www.lukaswojcik.com\/blog\/#website","url":"https:\/\/www.lukaswojcik.com\/blog\/","name":"Lukas Wojcik - Blog","description":"","publisher":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.lukaswojcik.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/www.lukaswojcik.com\/blog\/#\/schema\/person\/895f7604f9b6b71aad9bba33af28d0f9","name":"luky","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","url":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","contentUrl":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg","width":424,"height":636,"caption":"luky"},"logo":{"@id":"https:\/\/www.lukaswojcik.com\/blog\/wp-content\/uploads\/2026\/07\/lw-x2.jpg"},"sameAs":["https:\/\/www.lukaswojcik.com\/blog"]}]}},"_links":{"self":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/9424","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/comments?post=9424"}],"version-history":[{"count":1,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/9424\/revisions"}],"predecessor-version":[{"id":9442,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/9424\/revisions\/9442"}],"up":[{"embeddable":true,"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/pages\/9283"}],"wp:attachment":[{"href":"https:\/\/www.lukaswojcik.com\/blog\/wp-json\/wp\/v2\/media?parent=9424"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}