evolver-tools 1.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- evolver_tools/__init__.py +2 -0
- evolver_tools/__main__.py +3 -0
- evolver_tools/cli.py +89 -0
- evolver_tools/vendor/b64/__init__.py +2 -0
- evolver_tools/vendor/b64/b64.py +176 -0
- evolver_tools/vendor/cal_tool/__init__.py +1 -0
- evolver_tools/vendor/cal_tool/cli.py +234 -0
- evolver_tools/vendor/chart_cli/__init__.py +444 -0
- evolver_tools/vendor/chart_cli/__main__.py +3 -0
- evolver_tools/vendor/colors/__init__.py +5 -0
- evolver_tools/vendor/colors/__main__.py +97 -0
- evolver_tools/vendor/csv_stats/__init__.py +5 -0
- evolver_tools/vendor/csv_stats/__main__.py +4 -0
- evolver_tools/vendor/csv_stats/analyzer.py +258 -0
- evolver_tools/vendor/csv_stats/cli.py +45 -0
- evolver_tools/vendor/dirsize/__init__.py +183 -0
- evolver_tools/vendor/envcheck/__init__.py +426 -0
- evolver_tools/vendor/ff/__init__.py +427 -0
- evolver_tools/vendor/ff/__main__.py +3 -0
- evolver_tools/vendor/find_dups/__init__.py +7 -0
- evolver_tools/vendor/find_dups/cli.py +392 -0
- evolver_tools/vendor/hashsum/__init__.py +211 -0
- evolver_tools/vendor/hashsum/__main__.py +5 -0
- evolver_tools/vendor/http_live/__init__.py +265 -0
- evolver_tools/vendor/http_live/__main__.py +2 -0
- evolver_tools/vendor/ipinfo/__init__.py +3 -0
- evolver_tools/vendor/ipinfo/__main__.py +30 -0
- evolver_tools/vendor/jq_lite/__init__.py +257 -0
- evolver_tools/vendor/jq_lite/__main__.py +5 -0
- evolver_tools/vendor/json2csv/__init__.py +3 -0
- evolver_tools/vendor/json2csv/__main__.py +82 -0
- evolver_tools/vendor/jsonql/__init__.py +326 -0
- evolver_tools/vendor/jsonql/__main__.py +5 -0
- evolver_tools/vendor/license_cli/__init__.py +1 -0
- evolver_tools/vendor/license_cli/__main__.py +4 -0
- evolver_tools/vendor/license_cli/cli.py +289 -0
- evolver_tools/vendor/markdown_check/__init__.py +211 -0
- evolver_tools/vendor/nb/__init__.py +319 -0
- evolver_tools/vendor/nb/__main__.py +3 -0
- evolver_tools/vendor/passgen/__init__.py +224 -0
- evolver_tools/vendor/portcheck/__init__.py +2 -0
- evolver_tools/vendor/portcheck/__main__.py +66 -0
- evolver_tools/vendor/project_doctor/__init__.py +412 -0
- evolver_tools/vendor/project_doctor/__main__.py +3 -0
- evolver_tools/vendor/ren/__init__.py +283 -0
- evolver_tools/vendor/ren/__main__.py +3 -0
- evolver_tools/vendor/siege_lite/__init__.py +250 -0
- evolver_tools/vendor/siege_lite/__main__.py +3 -0
- evolver_tools/vendor/smellfinder/__init__.py +376 -0
- evolver_tools/vendor/smellfinder/__main__.py +3 -0
- evolver_tools/vendor/sqlite_cli/__init__.py +326 -0
- evolver_tools/vendor/sqlite_cli/__main__.py +5 -0
- evolver_tools/vendor/sysmon/__init__.py +299 -0
- evolver_tools/vendor/sysmon/__main__.py +3 -0
- evolver_tools/vendor/timer/__init__.py +127 -0
- evolver_tools/vendor/treedir/__init__.py +2 -0
- evolver_tools/vendor/treedir/__main__.py +128 -0
- evolver_tools/vendor/urlparse_tool/__init__.py +3 -0
- evolver_tools/vendor/urlparse_tool/cli.py +212 -0
- evolver_tools/vendor/web_summary/__init__.py +341 -0
- evolver_tools/vendor/web_summary/__main__.py +3 -0
- evolver_tools/vendor/wordcount/__init__.py +2 -0
- evolver_tools/vendor/wordcount/__main__.py +101 -0
- evolver_tools-1.4.0.dist-info/METADATA +107 -0
- evolver_tools-1.4.0.dist-info/RECORD +69 -0
- evolver_tools-1.4.0.dist-info/WHEEL +5 -0
- evolver_tools-1.4.0.dist-info/entry_points.txt +34 -0
- evolver_tools-1.4.0.dist-info/licenses/LICENSE +21 -0
- evolver_tools-1.4.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""passgen — 密码生成器 / Password & passphrase generator.
|
|
3
|
+
|
|
4
|
+
Zero-dependency CLI for generating secure passwords and memorable passphrases.
|
|
5
|
+
Shows entropy estimation for each generated secret.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
import os
|
|
10
|
+
import math
|
|
11
|
+
import argparse
|
|
12
|
+
import string
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
# Word list for passphrases (from EFF's short word list, ~1296 words)
|
|
16
|
+
WORDS = [
|
|
17
|
+
"acid", "acorn", "actor", "agree", "algae", "alien", "alpha", "angel",
|
|
18
|
+
"apple", "april", "arena", "aroma", "arrow", "attic", "audio", "aurora",
|
|
19
|
+
"axiom", "azure", "bacon", "badge", "bagel", "baker", "basin", "batch",
|
|
20
|
+
"beach", "beard", "beast", "berry", "blade", "blast", "blaze", "blend",
|
|
21
|
+
"blimp", "bliss", "blitz", "bloom", "blues", "bluff", "blunt", "board",
|
|
22
|
+
"bonus", "boost", "booth", "bound", "brain", "brand", "brave", "bread",
|
|
23
|
+
"break", "brick", "bride", "bring", "broad", "brook", "brown", "brush",
|
|
24
|
+
"buddy", "build", "bulge", "bully", "bunch", "cabin", "cable", "camel",
|
|
25
|
+
"candy", "cargo", "carol", "catch", "cause", "cave", "cedar", "chain",
|
|
26
|
+
"chair", "chaos", "charm", "chart", "cheek", "chess", "chest", "chief",
|
|
27
|
+
"child", "chill", "china", "choir", "chord", "chunk", "cigar", "civic",
|
|
28
|
+
"cider", "claim", "clash", "clean", "clear", "click", "cliff", "cling",
|
|
29
|
+
"clock", "cloud", "clown", "coach", "coast", "cocoa", "coil", "coral",
|
|
30
|
+
"couch", "cough", "count", "cover", "crack", "craft", "crane", "crash",
|
|
31
|
+
"crawl", "crazy", "cream", "creek", "crisp", "cross", "crown", "crush",
|
|
32
|
+
"cubic", "curry", "curse", "curve", "cycle", "dairy", "dance", "decoy",
|
|
33
|
+
"delay", "delta", "demon", "dense", "depot", "depth", "derby", "desert",
|
|
34
|
+
"devil", "diesel", "digit", "diner", "dirty", "ditch", "dodge", "doing",
|
|
35
|
+
"donor", "doubt", "dough", "draft", "drain", "drama", "drank", "drape",
|
|
36
|
+
"drawn", "dread", "dream", "dress", "dried", "drift", "drill", "drown",
|
|
37
|
+
"drums", "drunk", "dryer", "dunes", "dwarf", "eager", "eagle", "early",
|
|
38
|
+
"earth", "easel", "eaten", "eater", "ebony", "eclat", "edge", "eight",
|
|
39
|
+
"elder", "elect", "elite", "elope", "emoji", "empty", "enemy", "enjoy",
|
|
40
|
+
"enter", "entry", "equal", "error", "essay", "even", "event", "every",
|
|
41
|
+
"evoke", "exact", "exert", "exile", "exist", "extra", "fable", "facet",
|
|
42
|
+
"faith", "fancy", "fault", "feast", "fence", "ferry", "fetch", "fever",
|
|
43
|
+
"fiber", "field", "fifty", "fight", "final", "finch", "fleet", "flesh",
|
|
44
|
+
"flint", "flock", "flood", "floor", "floss", "flour", "fluid", "flush",
|
|
45
|
+
"flyer", "focal", "focus", "force", "forge", "forth", "forum", "found",
|
|
46
|
+
"frame", "frank", "fraud", "fresh", "front", "frost", "fruit", "fully",
|
|
47
|
+
"ghost", "giant", "given", "glass", "glide", "globe", "gloom", "glory",
|
|
48
|
+
"glove", "glued", "going", "gold", "good", "goose", "grain", "grand",
|
|
49
|
+
"grant", "grape", "graph", "grasp", "grass", "grave", "great", "green",
|
|
50
|
+
"greet", "grief", "grill", "grind", "gross", "group", "grove", "grown",
|
|
51
|
+
"guard", "guess", "guest", "guide", "guild", "guilt", "gully", "guy",
|
|
52
|
+
"habit", "hairy", "happy", "harsh", "haste", "haven", "heart", "heavy",
|
|
53
|
+
"hedge", "hello", "hence", "heron", "hobby", "honey", "honor", "horse",
|
|
54
|
+
"hotel", "house", "hover", "human", "humor", "hurry", "ideal", "image",
|
|
55
|
+
"imply", "index", "indie", "infer", "inner", "input", "irony", "ivory",
|
|
56
|
+
"jazz", "jeans", "jelly", "jewel", "joint", "joker", "joule", "judge",
|
|
57
|
+
"juice", "juicy", "jumbo", "jump", "junior", "junta", "karma", "kayak",
|
|
58
|
+
"kazoo", "keen", "kebab", "ketch", "kiosk", "kitty", "knack", "knew",
|
|
59
|
+
"knife", "knight", "knock", "knot", "known", "koala", "label", "labor",
|
|
60
|
+
"lager", "lance", "lapse", "large", "laser", "lasso", "latch", "later",
|
|
61
|
+
"latin", "laugh", "layer", "learn", "lease", "leave", "legal", "lemon",
|
|
62
|
+
"level", "lever", "light", "limit", "liver", "local", "logic", "login",
|
|
63
|
+
"loose", "lopez", "lords", "loser", "lotto", "loving", "lower", "loyal",
|
|
64
|
+
"lucky", "lunar", "lunch", "lying", "macro", "magic", "major", "maker",
|
|
65
|
+
"maple", "march", "marsh", "mason", "match", "mayor", "media", "mercy",
|
|
66
|
+
"merge", "merit", "merry", "metal", "meter", "might", "minor", "minus",
|
|
67
|
+
"mixed", "model", "mocha", "money", "month", "moral", "motor", "mount",
|
|
68
|
+
"mouse", "mouth", "movie", "music", "naive", "nanny", "nasty", "navel",
|
|
69
|
+
"needs", "nerve", "never", "night", "noble", "noise", "north", "noted",
|
|
70
|
+
"novel", "nudge", "nurse", "nylon", "oasis", "occur", "ocean", "offer",
|
|
71
|
+
"often", "olive", "onion", "onset", "open", "opera", "orbit", "order",
|
|
72
|
+
"organ", "other", "otter", "outer", "owner", "oxide", "ozone", "paddle",
|
|
73
|
+
"paint", "panel", "panic", "paper", "party", "pasta", "paste", "patch",
|
|
74
|
+
"pause", "peace", "peach", "pearl", "pedal", "penny", "perch", "peril",
|
|
75
|
+
"phase", "phone", "photo", "piano", "piece", "pilot", "pixel", "pizza",
|
|
76
|
+
"place", "plain", "plane", "plant", "plate", "plaza", "plead", "pluck",
|
|
77
|
+
"plumb", "plume", "plush", "point", "polar", "pouch", "pound", "power",
|
|
78
|
+
"press", "price", "pride", "prime", "prism", "prize", "probe", "prone",
|
|
79
|
+
"proof", "proud", "prove", "proxy", "psalm", "pulse", "punch", "pupil",
|
|
80
|
+
"purse", "queen", "query", "quest", "quick", "quiet", "quite", "quota",
|
|
81
|
+
"quote", "racer", "radar", "radio", "rally", "ranch", "range", "rapid",
|
|
82
|
+
"ratio", "reach", "react", "ready", "realm", "rebel", "refer", "reign",
|
|
83
|
+
"relax", "reply", "rerun", "resin", "retro", "rider", "ridge", "rifle",
|
|
84
|
+
"right", "rigid", "riley", "risky", "rival", "river", "robin", "robot",
|
|
85
|
+
"rocky", "rogue", "roman", "rouge", "rough", "route", "royal", "rufus",
|
|
86
|
+
"rugby", "ruler", "rural", "sadly", "saint", "salad", "salon", "sandy",
|
|
87
|
+
"satin", "sauce", "scale", "scare", "scene", "scent", "scope", "score",
|
|
88
|
+
"sense", "serve", "setup", "seven", "shade", "shaft", "shake", "shall",
|
|
89
|
+
"shame", "shape", "share", "shark", "sharp", "sheep", "sheer", "sheet",
|
|
90
|
+
"shelf", "shell", "shift", "shine", "shirt", "shock", "shore", "short",
|
|
91
|
+
"shout", "sight", "sigma", "silent", "silly", "since", "sixth", "sixty",
|
|
92
|
+
"sized", "skill", "skull", "slash", "sleep", "slice", "slide", "slope",
|
|
93
|
+
"small", "smart", "smell", "smile", "smoke", "snack", "snake", "solar",
|
|
94
|
+
"solid", "solve", "sorry", "sound", "south", "space", "spare", "spark",
|
|
95
|
+
"speak", "speed", "spell", "spend", "spice", "spill", "spine", "spite",
|
|
96
|
+
"split", "spoke", "spoon", "sport", "spray", "squad", "stack", "staff",
|
|
97
|
+
"stage", "stain", "stake", "stale", "stall", "stamp", "stand", "stark",
|
|
98
|
+
"start", "state", "stays", "steady", "steam", "steel", "steep", "steer",
|
|
99
|
+
"stern", "stick", "stiff", "still", "stock", "stone", "stood", "store",
|
|
100
|
+
"storm", "story", "stout", "stove", "strap", "straw", "strip", "stuck",
|
|
101
|
+
"study", "stuff", "style", "sugar", "suite", "sunny", "super", "surge",
|
|
102
|
+
"swamp", "swarm", "sweet", "swift", "swing", "swirl", "sword", "syrup",
|
|
103
|
+
"table", "taste", "teach", "teeth", "tempt", "terra", "thank", "theme",
|
|
104
|
+
"there", "thick", "thing", "think", "third", "thorn", "those", "three",
|
|
105
|
+
"throw", "thumb", "tiger", "tight", "timer", "title", "token", "total",
|
|
106
|
+
"touch", "towel", "tower", "toxic", "trace", "track", "trade", "train",
|
|
107
|
+
"trait", "trash", "treat", "trend", "trial", "tribe", "trick", "tried",
|
|
108
|
+
"troop", "truck", "truly", "trump", "trunk", "trust", "truth", "tumor",
|
|
109
|
+
"tuned", "twice", "twin", "twist", "ultra", "uncle", "under", "union",
|
|
110
|
+
"unite", "unity", "until", "upper", "upset", "urban", "usage", "usual",
|
|
111
|
+
"utter", "valid", "value", "vapor", "vault", "venue", "verse", "video",
|
|
112
|
+
"vigor", "viral", "virus", "visit", "vista", "vital", "vivid", "vocal",
|
|
113
|
+
"vodka", "voice", "voter", "wager", "wagon", "waist", "watch", "water",
|
|
114
|
+
"waved", "weary", "weave", "wedge", "weird", "whale", "wheat", "wheel",
|
|
115
|
+
"where", "which", "while", "white", "whole", "whose", "wider", "witch",
|
|
116
|
+
"woman", "world", "worry", "worse", "worst", "worth", "would", "wound",
|
|
117
|
+
"wreck", "wrist", "write", "wrong", "wrote", "yacht", "yield", "young",
|
|
118
|
+
"youth", "zebra", "zesty", "zones",
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def entry():
|
|
123
|
+
args = parse_args()
|
|
124
|
+
|
|
125
|
+
if args.passphrase:
|
|
126
|
+
result = generate_passphrase(args.count, args.separator, args.capitalize)
|
|
127
|
+
elif args.pin:
|
|
128
|
+
result = generate_pin(args.count, args.length)
|
|
129
|
+
else:
|
|
130
|
+
result = generate_password(args.count, args.length, args.no_symbols)
|
|
131
|
+
|
|
132
|
+
for secret, entropy in result:
|
|
133
|
+
print(f"{secret} ({entropy:.1f} bits)")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def generate_password(count: int, length: int, no_symbols: bool) -> list[tuple[str, float]]:
|
|
137
|
+
"""Generate random passwords."""
|
|
138
|
+
chars = string.ascii_letters + string.digits
|
|
139
|
+
if not no_symbols:
|
|
140
|
+
chars += "!@#$%^&*()_+-=[]{}|;:,.<>?"
|
|
141
|
+
bits_per_char = math.log2(len(chars))
|
|
142
|
+
entropy = bits_per_char * length
|
|
143
|
+
|
|
144
|
+
results = []
|
|
145
|
+
for _ in range(count):
|
|
146
|
+
password = "".join(chars[os.urandom(1)[0] % len(chars)] for _ in range(length))
|
|
147
|
+
results.append((password, entropy))
|
|
148
|
+
|
|
149
|
+
return results
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def generate_passphrase(count: int, separator: str, capitalize: bool) -> list[tuple[str, float]]:
|
|
153
|
+
"""Generate passphrases from word list."""
|
|
154
|
+
bits_per_word = math.log2(len(WORDS))
|
|
155
|
+
entropy = bits_per_word * 4 # default 4 words
|
|
156
|
+
|
|
157
|
+
results = []
|
|
158
|
+
for _ in range(count):
|
|
159
|
+
words = []
|
|
160
|
+
for _ in range(4):
|
|
161
|
+
idx = int.from_bytes(os.urandom(2), "big") % len(WORDS)
|
|
162
|
+
word = WORDS[idx]
|
|
163
|
+
if capitalize:
|
|
164
|
+
word = word.capitalize()
|
|
165
|
+
words.append(word)
|
|
166
|
+
passphrase = separator.join(words)
|
|
167
|
+
results.append((passphrase, entropy))
|
|
168
|
+
|
|
169
|
+
return results
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def generate_pin(count: int, length: int) -> list[tuple[str, float]]:
|
|
173
|
+
"""Generate numeric PINs."""
|
|
174
|
+
entropy = math.log2(10) * length
|
|
175
|
+
|
|
176
|
+
results = []
|
|
177
|
+
for _ in range(count):
|
|
178
|
+
pin = "".join(str(os.urandom(1)[0] % 10) for _ in range(length))
|
|
179
|
+
results.append((pin, entropy))
|
|
180
|
+
|
|
181
|
+
return results
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def parse_args():
|
|
185
|
+
parser = argparse.ArgumentParser(
|
|
186
|
+
description="passgen — 密码生成器 / Password & passphrase generator",
|
|
187
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
188
|
+
epilog="""Examples:
|
|
189
|
+
passgen # 4 random passwords (16 chars)
|
|
190
|
+
passgen -l 24 # 4 passwords, 24 chars each
|
|
191
|
+
passgen -c 10 # 10 passwords
|
|
192
|
+
passgen --pin # 4 PINs (6 digits)
|
|
193
|
+
passgen --pin -l 8 # 4 PINs, 8 digits
|
|
194
|
+
passgen --passphrase # 4 passphrases (4 words each)
|
|
195
|
+
passgen --passphrase --separator - # Passphrases with '-' separator
|
|
196
|
+
passgen --passphrase --capitalize # Capitalized words
|
|
197
|
+
passgen --no-symbols # Alphanumeric only
|
|
198
|
+
""")
|
|
199
|
+
parser.add_argument("-l", "--length", type=int, default=16,
|
|
200
|
+
help="Password length / digit count (default: 16)")
|
|
201
|
+
parser.add_argument("-c", "--count", type=int, default=4,
|
|
202
|
+
help="Number of passwords to generate (default: 4)")
|
|
203
|
+
parser.add_argument("--pin", action="store_true",
|
|
204
|
+
help="Generate numeric PINs instead of passwords")
|
|
205
|
+
parser.add_argument("--passphrase", action="store_true",
|
|
206
|
+
help="Generate memorable passphrases")
|
|
207
|
+
parser.add_argument("--separator", type=str, default=" ",
|
|
208
|
+
help="Word separator for passphrases (default: space)")
|
|
209
|
+
parser.add_argument("--capitalize", action="store_true",
|
|
210
|
+
help="Capitalize words in passphrase")
|
|
211
|
+
parser.add_argument("--no-symbols", action="store_true",
|
|
212
|
+
help="Alphanumeric only (no special chars)")
|
|
213
|
+
|
|
214
|
+
args = parser.parse_args()
|
|
215
|
+
|
|
216
|
+
if args.passphrase and args.length != 16:
|
|
217
|
+
# For passphrases, length= number of words
|
|
218
|
+
pass
|
|
219
|
+
|
|
220
|
+
return args
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
if __name__ == "__main__":
|
|
224
|
+
entry()
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""portcheck — 端口检查与查找工具。零外部依赖。"""
|
|
3
|
+
import socket
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
def check_port(port, host="127.0.0.1"):
|
|
7
|
+
"""检查端口是否被占用"""
|
|
8
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
9
|
+
s.settimeout(1)
|
|
10
|
+
try:
|
|
11
|
+
s.connect((host, port))
|
|
12
|
+
s.close()
|
|
13
|
+
return True
|
|
14
|
+
except (ConnectionRefusedError, OSError):
|
|
15
|
+
return False
|
|
16
|
+
|
|
17
|
+
def find_free_port(start, end=None):
|
|
18
|
+
"""查找空闲端口"""
|
|
19
|
+
end = end or start + 100
|
|
20
|
+
for port in range(start, end + 1):
|
|
21
|
+
if not check_port(port):
|
|
22
|
+
return port
|
|
23
|
+
return None
|
|
24
|
+
|
|
25
|
+
def format_output(port, in_use):
|
|
26
|
+
status = "🔴 已占用" if in_use else "🟢 空闲"
|
|
27
|
+
return f"Port {port:<5} {status}"
|
|
28
|
+
|
|
29
|
+
def main():
|
|
30
|
+
args = sys.argv[1:]
|
|
31
|
+
if not args or args[0] in ("-h", "--help"):
|
|
32
|
+
print("用法:")
|
|
33
|
+
print(" portcheck <port> — 检查指定端口")
|
|
34
|
+
print(" portcheck --free [start] [end] — 查找空闲端口")
|
|
35
|
+
print(" portcheck --list <start>-<end> — 列出端口状态范围")
|
|
36
|
+
print("示例:")
|
|
37
|
+
print(" portcheck 3000")
|
|
38
|
+
print(" portcheck --free 3000 3100")
|
|
39
|
+
print(" portcheck --list 3000-3010")
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
if args[0] == "--free":
|
|
43
|
+
start = int(args[1]) if len(args) > 1 else 3000
|
|
44
|
+
end = int(args[2]) if len(args) > 2 else start + 100
|
|
45
|
+
port = find_free_port(start, end)
|
|
46
|
+
if port:
|
|
47
|
+
print(f"✅ 空闲端口: {port}")
|
|
48
|
+
else:
|
|
49
|
+
print(f"❌ 在 {start}-{end} 范围内未找到空闲端口")
|
|
50
|
+
elif args[0] == "--list":
|
|
51
|
+
parts = args[1].split("-") if len(args) > 1 else []
|
|
52
|
+
start = int(parts[0]) if parts else 3000
|
|
53
|
+
end = int(parts[1]) if len(parts) > 1 else start + 10
|
|
54
|
+
for port in range(start, end + 1):
|
|
55
|
+
print(format_output(port, check_port(port)))
|
|
56
|
+
else:
|
|
57
|
+
port = int(args[0])
|
|
58
|
+
if port < 1 or port > 65535:
|
|
59
|
+
print(f"❌ 端口号必须在 1-65535 范围内: {port}", file=sys.stderr)
|
|
60
|
+
sys.exit(1)
|
|
61
|
+
in_use = check_port(port)
|
|
62
|
+
print(format_output(port, in_use))
|
|
63
|
+
sys.exit(0 if not in_use else 1)
|
|
64
|
+
|
|
65
|
+
if __name__ == "__main__":
|
|
66
|
+
main()
|