palle
|
Posted: Thu Feb 12, 2026 18:02 Post subject: |
|
|
Добрый день!
Надоело следить за раскладкой при копировании с помощью Lst2Clip.
Я скормил readme.htm из комплекта утилиты нейросетям и получил работающий питон-скрипт, повторяющий её функционал и даже с сохранением синтаксиса командной строки.
 Hidden text | Code: | """ListToClip — transform file paths / text lines, copy result to clipboard.
Usage:
python ListToClip.py /L [options] — read from stdin
python ListToClip.py /L:"source" [opts] — read from file or inline text
pythonw ListToClip.py /L:"source" [opts] — same, no console window
Options:
/U Mapped drive → UNC
/NE Remove file extension
/PD:start,end Limit path depth
/RB Remove trailing slash
/P:"text" Add prefix
/S:"text" Add suffix
/R:"old"="new" Replace (repeatable, spaces preserved inside quotes)
Requires: pip install pyperclip
"""
from __future__ import annotations
import ctypes
import ctypes.wintypes as w
import os
import re
import subprocess
import sys
import pyperclip
# ── Защита от pythonw.exe (нет консоли → stdout/stderr = None) ────────────
if sys.stdout is None:
sys.stdout = open(os.devnull, "w", encoding="utf-8")
if sys.stderr is None:
sys.stderr = open(os.devnull, "w", encoding="utf-8")
# ── Raw command line ──────────────────────────────────────────────────────
_GetCommandLineW = ctypes.windll.kernel32.GetCommandLineW
_GetCommandLineW.restype = ctypes.c_wchar_p
def _raw_cmdline() -> str:
return _GetCommandLineW() or ""
def _split_cmdline(cmd: str) -> list[str]:
"""Split keeping backslashes literal; only quotes toggle quoting."""
args: list[str] = []
buf: list[str] = []
q = False
i = 0
n = len(cmd)
while i < n:
ch = cmd[i]
if ch == '"':
q = not q
elif not q and ch.isspace():
if buf:
args.append("".join(buf))
buf.clear()
i += 1
while i < n and cmd[i].isspace():
i += 1
continue
else:
buf.append(ch)
i += 1
if buf:
args.append("".join(buf))
return args
def _argv() -> list[str]:
parts = _split_cmdline(_raw_cmdline())
if not parts:
return sys.argv[1:]
lo = parts[0].replace("/", "\\").lower()
py = lo.endswith(("\\python.exe", "\\pythonw.exe", "\\py.exe")) or \
lo in ("python.exe", "pythonw.exe", "py.exe", "python", "pythonw", "py")
start = 1
if py and len(parts) >= 2 and parts[1].lower().endswith((".py", ".pyw")):
start = 2
return parts[start:]
# ── /R:"old"="new" — парсинг прямо из сырой строки ───────────────────────
def _extract_q(s: str) -> tuple[str | None, str]:
"""Extract quoted value: '"...' → (content, rest)."""
if not s or s[0] != '"':
return None, s
i = 1
buf: list[str] = []
while i < len(s):
if s[i] == '"':
return "".join(buf), s[i + 1:]
buf.append(s[i])
i += 1
return "".join(buf), ""
def _parse_replacements(raw: str) -> list[tuple[str, str]]:
result: list[tuple[str, str]] = []
up = raw.upper()
i = 0
while True:
pos = up.find("/R:", i)
if pos == -1:
break
rest = raw[pos + 3:]
if rest.startswith('"'):
old, after = _extract_q(rest)
if old is not None and after.startswith("="):
new, _ = _extract_q(after[1:])
if new is not None:
if old: # пустой old — skip
result.append((old, new))
i = pos + 3
continue
# unquoted fallback
tok = ""
for ch in rest:
if ch.isspace():
break
tok += ch
if "=" in tok:
l, r = tok.split("=", 1)
if l:
result.append((l, r))
i = pos + 3
return result
# ── UNC ───────────────────────────────────────────────────────────────────
def _to_unc(path: str) -> str:
if len(path) < 2 or path[1] != ":":
return path
drv = path[0].upper()
try:
out = subprocess.run(
["net", "use"], capture_output=True, text=True, timeout=5,
creationflags=subprocess.CREATE_NO_WINDOW,
).stdout
for line in out.splitlines():
parts = line.split()
for j, p in enumerate(parts):
if p.upper() == f"{drv}:" and j + 1 < len(parts) and parts[j + 1].startswith("\\\\"):
return parts[j + 1].rstrip("\\") + path[2:]
except Exception:
pass
try:
import win32wnet # type: ignore
return win32wnet.WNetGetUniversalName(path, 1)
except Exception:
pass
return path
# ── Path helpers ──────────────────────────────────────────────────────────
def _sep(p: str) -> str:
return "\\" if "\\" in p else "/" if "/" in p else "\\"
def _rm_ext(p: str) -> str:
r, e = os.path.splitext(p)
return r if e else p
def _depth(path: str, start: int, end: int) -> str:
sep = _sep(path)
unc = path.startswith("\\\\")
pfx, work = (path[:2], path[2:]) if unc else ("", path)
pts = [x for x in work.split(sep) if x]
n = len(pts)
s, e = max(0, start), max(0, end)
if s + e >= n:
return path
mid = sep.join(pts[:s]) + sep + "..." + sep + sep.join(pts[n - e:])
if unc:
mid = pfx + mid
elif len(path) >= 2 and path[1] == ":" and not mid[:2].upper().startswith(path[0].upper() + ":"):
mid = path[0] + ":" + sep + mid
return mid
def _rm_slash(p: str) -> str:
if re.fullmatch(r"[A-Za-z]:[\\/]", p):
return p
return p.rstrip("\\/") if len(p) > 1 and p[-1] in "\\/" else p
# ── Argument parsing ─────────────────────────────────────────────────────
def _parse_args(argv: list[str]) -> dict:
cfg: dict = dict(
source=None, unc=False, no_ext=False, pd=None,
rb=False, pfx="", sfx="", repl=[],
)
for a in argv:
u = a.upper()
if u == "/L":
cfg["source"] = None
elif u.startswith("/L:"):
cfg["source"] = a[3:]
elif u == "/U":
cfg["unc"] = True
elif u == "/NE":
cfg["no_ext"] = True
elif u.startswith("/PD:"):
ns = a[4:].split(",")
cfg["pd"] = (int(ns[0] or 0), int(ns[1] or 0) if len(ns) > 1 else 0)
elif u == "/RB":
cfg["rb"] = True
elif u.startswith("/P:"):
v = a[3:]
cfg["pfx"] = v[1:-1] if len(v) >= 2 and v[0] == '"' and v[-1] == '"' else v
elif u.startswith("/S:"):
v = a[3:]
cfg["sfx"] = v[1:-1] if len(v) >= 2 and v[0] == '"' and v[-1] == '"' else v
return cfg
# ── Input ─────────────────────────────────────────────────────────────────
def _decode(data: bytes) -> str:
for bom, enc in ((b"\xff\xfe", "utf-16le"), (b"\xfe\xff", "utf-16be"), (b"\xef\xbb\xbf", "utf-8-sig")):
if data.startswith(bom):
return data.decode(enc, errors="replace")
try:
return data.decode("utf-8")
except UnicodeDecodeError:
return data.decode("cp1251", errors="replace")
def _split_quoted(text: str) -> list[str]:
out: list[str] = []
buf: list[str] = []
q = False
i = 0
n = len(text)
while i < n:
ch = text[i]
if ch == '"':
q = not q
elif not q and ch.isspace():
if buf:
out.append("".join(buf))
buf.clear()
i += 1
while i < n and text[i].isspace():
i += 1
continue
else:
buf.append(ch)
i += 1
if buf:
out.append("".join(buf))
return [t for t in out if t]
def _lines(cfg: dict) -> list[str]:
src = cfg["source"]
# stdin
if src is None:
out: list[str] = []
try:
for ln in sys.stdin:
ln = ln.rstrip("\r\n")
if ln:
out.append(ln.lstrip("\ufeff"))
except (KeyboardInterrupt, OSError):
pass
return out
# normalize
if src.upper().startswith("/L:"):
src = src[3:]
if src.startswith("@") and len(src) > 1:
src = src[1:]
if src.startswith("\\\\?\\") or src.startswith("\\\\.\\"):
src = src[4:]
# file
if os.path.isfile(src):
text = _decode(open(src, "rb").read()).lstrip("\ufeff")
return [ln for ln in text.splitlines() if ln.strip()]
# inline
if "\n" in src or "\r" in src:
return [ln for ln in src.splitlines() if ln.strip()]
parts = _split_quoted(src)
return parts if len(parts) > 1 else ([src.strip()] if src.strip() else [])
# ── Processing ────────────────────────────────────────────────────────────
def _process(line: str, cfg: dict) -> str:
s = line.rstrip("\r\n")
if not s.strip():
return s
if cfg["unc"]:
s = _to_unc(s)
if cfg["no_ext"]:
s = _rm_ext(s)
if cfg["pd"]:
s = _depth(s, *cfg["pd"])
if cfg["rb"]:
s = _rm_slash(s)
for old, new in cfg["repl"]:
s = s.replace(old, new)
return cfg["pfx"] + s + cfg["sfx"]
# ── Main ──────────────────────────────────────────────────────────────────
def main() -> None:
argv = _argv()
if not argv:
print(__doc__)
print("Error: /L must be the first parameter.")
sys.exit(1)
f = argv[0].upper()
if f != "/L" and not f.startswith("/L:"):
print(__doc__)
print(f"Error: /L must be the first parameter. Got: {argv[0]}")
sys.exit(1)
cfg = _parse_args(argv)
cfg["repl"] = _parse_replacements(_raw_cmdline())
results = [_process(ln, cfg) for ln in _lines(cfg) if ln.strip()]
if not results:
print("No lines to process.", file=sys.stderr)
return
output = "\n".join(results)
print(output)
try:
pyperclip.copy(output)
print(f"\n--- Copied {len(results)} line(s) to clipboard ---", file=sys.stderr)
except Exception:
print("\n--- Warning: Could not copy to clipboard ---", file=sys.stderr)
if __name__ == "__main__":
main()
|
|
|