bashexplain 1.0.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.
bash_explain.py ADDED
@@ -0,0 +1,613 @@
1
+ """
2
+ BashExplain - A command-line tool to explain Bash commands, errors, and scripts
3
+ """
4
+
5
+ import sys
6
+ import re
7
+ import argparse
8
+ from typing import Dict, List, Tuple, Optional
9
+ from unicodedata import name
10
+
11
+
12
+ # Terminal Colors
13
+
14
+ class Colors:
15
+ RED = '\033[91m'
16
+ GREEN = '\033[92m'
17
+ YELLOW = '\033[93m'
18
+ BLUE = '\033[94m'
19
+ MAGENTA = '\033[95m'
20
+ CYAN = '\033[96m'
21
+ WHITE = '\033[97m'
22
+ BOLD = '\033[1m'
23
+ DIM = '\033[2m'
24
+ RESET = '\033[0m'
25
+
26
+ @staticmethod
27
+ def disable():
28
+ """Turn off all colors (when output is piped or --no-color is used)"""
29
+ Colors.RED = Colors.GREEN = Colors.YELLOW = Colors.BLUE = ''
30
+ Colors.MAGENTA = Colors.CYAN = Colors.WHITE = ''
31
+ Colors.BOLD = Colors.DIM = Colors.RESET = ''
32
+
33
+
34
+ def clr(color: str, text: str) -> str:
35
+ """Wrap text in a color code."""
36
+ return f"{color}{text}{Colors.RESET}"
37
+
38
+
39
+ class CommandExplainer:
40
+ """Explains individual Bash commands and their components"""
41
+
42
+ COMMON_COMMANDS = {
43
+ 'ls': 'List directory contents',
44
+ 'cd': 'Change directory',
45
+ 'pwd': 'Print working directory',
46
+ 'mkdir': 'Make directory',
47
+ 'rmdir': 'Remove empty directory',
48
+ 'rm': 'Remove files or directories',
49
+ 'cp': 'Copy files or directories',
50
+ 'mv': 'Move or rename files',
51
+ 'cat': 'Concatenate and display file contents',
52
+ 'grep': 'Search for patterns in text',
53
+ 'find': 'Search for files in directory hierarchy',
54
+ 'chmod': 'Change file permissions',
55
+ 'chown': 'Change file owner and group',
56
+ 'sudo': 'Execute command as superuser',
57
+ 'apt': 'Package manager (Debian/Ubuntu)',
58
+ 'yum': 'Package manager (RedHat/CentOS)',
59
+ 'systemctl': 'Control systemd services',
60
+ 'ps': 'Report process status',
61
+ 'kill': 'Send signal to process',
62
+ 'top': 'Display running processes',
63
+ 'echo': 'Display text or variables',
64
+ 'export': 'Set environment variable',
65
+ 'source': 'Execute commands from file in current shell',
66
+ 'tar': 'Archive files',
67
+ 'gzip': 'Compress files',
68
+ 'gunzip': 'Decompress files',
69
+ 'wget': 'Download files from the web',
70
+ 'curl': 'Transfer data from or to a server',
71
+ 'ssh': 'Secure shell remote login',
72
+ 'scp': 'Secure copy files over SSH',
73
+ 'git': 'Version control system',
74
+ 'docker': 'Container platform',
75
+ 'pip': 'Python package installer',
76
+ 'npm': 'Node.js package manager',
77
+ 'touch': 'Create empty file or update timestamp',
78
+ 'head': 'Display first lines of file',
79
+ 'tail': 'Display last lines of file',
80
+ 'less': 'View file contents page by page',
81
+ 'more': 'View file contents page by page (simpler)',
82
+ 'diff': 'Compare files line by line',
83
+ 'sed': 'Stream editor for text transformation',
84
+ 'awk': 'Pattern scanning and text processing',
85
+ 'sort': 'Sort lines of text',
86
+ 'uniq': 'Remove duplicate lines',
87
+ 'wc': 'Word, line, and byte count',
88
+ 'man': 'Display manual pages',
89
+ 'which': 'Show full path of command',
90
+ 'whereis': 'Locate binary, source, and manual page',
91
+ 'alias': 'Create command shortcut',
92
+ 'history': 'Show command history',
93
+ 'df': 'Report file system disk space usage',
94
+ 'du': 'Estimate file space usage',
95
+ 'free': 'Display memory usage',
96
+ 'uname': 'Print system information',
97
+ 'hostname': 'Show or set system hostname',
98
+ 'ping': 'Test network connectivity',
99
+ 'netstat': 'Network statistics',
100
+ 'ifconfig': 'Configure network interface',
101
+ 'ip': 'Show/manipulate routing and network devices',
102
+ }
103
+
104
+ COMMON_FLAGS = {
105
+ '-a': 'All (include hidden files)',
106
+ '-l': 'Long format (detailed listing)',
107
+ '-h': 'Human-readable (sizes in KB, MB, GB)',
108
+ '-r': 'Recursive (include subdirectories)',
109
+ '-f': "Force (don't prompt for confirmation)",
110
+ '-v': 'Verbose (show detailed output)',
111
+ '-i': 'Interactive (prompt before action)',
112
+ '-n': 'Numeric (use numbers instead of names)',
113
+ '-p': 'Parent (create parent directories if needed)',
114
+ '-R': 'Recursive (uppercase version)',
115
+ '-d': 'Directory (operate on directory itself)',
116
+ '-s': 'Silent or summarize',
117
+ '-u': 'Update or user',
118
+ '-g': 'Group',
119
+ '-o': 'Output file',
120
+ '-e': 'Expression or edit',
121
+ '-w': 'Word match',
122
+ '-c': 'Count',
123
+ '-x': 'Exclude or execute',
124
+ '-z': 'Compress',
125
+ '-t': 'Time or timestamp',
126
+ '-k': 'Kill or kilobytes',
127
+ '-m': 'Modify or message',
128
+ '-q': 'Quiet (suppress output)',
129
+ '-y': 'Yes (assume yes to prompts)',
130
+ '-b': 'Backup',
131
+ '--help': 'Display help information',
132
+ '--version': 'Display version information',
133
+ '--force': 'Force operation (do not prompt for confirmation)',
134
+ '--recursive': 'Operate recursively (same as -r)',
135
+ '--no-preserve-root': 'Do not treat "/" as special for "rm -rf" (dangerous)',
136
+ '--dry-run': 'Show what would be done without making changes',
137
+ '--no-clobber': 'Do not overwrite existing files',
138
+ '--verbose': 'Show detailed output (same as -v)',
139
+ '--quiet': 'Suppress most output (same as -q)',
140
+ }
141
+
142
+ UNSAFE_PATTERNS = [
143
+ (r'rm\s+-rf\s+/', 'DANGEROUS: This will delete files starting from root directory'),
144
+ (r'rm\s+-rf\s+--no-preserve-root', 'EXTREMELY DANGEROUS: Overrides safety and can delete the entire filesystem'),
145
+ (r'rm\s+-rf\s+\.', 'DANGEROUS: Deletes the current directory recursively'),
146
+ (r'rm\s+-rf\s+\*', 'DANGEROUS: Deletes all files in the current directory'),
147
+ (r'chmod\s+777', 'UNSAFE: Grants full permissions to everyone'),
148
+ (r'curl\s+.*\|\s*bash', 'RISKY: Executing downloaded script without inspection'),
149
+ (r'curl\s+-sSL\s+.*\|\s*sh', 'RISKY: Silent download-and-execute without inspection'),
150
+ (r'wget\s+.*\|\s*sh', 'RISKY: Executing downloaded script without inspection'),
151
+ (r'wget\s+-qO-.*\|\s*bash', 'RISKY: Silent download-and-execute without inspection'),
152
+ (r'sudo\s+chmod', 'CAUTION: Changing permissions as superuser'),
153
+ (r'sudo\s+rm', 'CAUTION: Deleting files as superuser'),
154
+ (r':\(\)\{.*\};:', 'DANGEROUS: Fork bomb - will crash system'),
155
+ (r'dd\s+if=.*of=/dev/(?:sd|nvme|mmcblk)\b', 'DANGEROUS: Writing raw data to a disk device'),
156
+ (r'fdisk\s+/dev/(?:sd|nvme|mmcblk)', 'DANGEROUS: Partitioning can destroy existing data'),
157
+ (r'mkfs\.?', 'DANGEROUS: Formatting disk - will erase all data'),
158
+ (r'echo\s+.*>\s*/etc/passwd', 'DANGEROUS: Overwriting system password file'),
159
+ (r'kill\s+-9\s+1\b', 'DANGEROUS: Killing init process may crash the system'),
160
+ (r'iptables\s+-F', 'CAUTION: Flushing firewall rules may break connectivity'),
161
+ (r'systemctl\s+stop\s+ssh', 'CAUTION: Stopping SSH may lock you out of remote servers'),
162
+ (r'chown\s+-R\s+root:root\s+/', 'CAUTION: Recursively changing ownership of root can break permissions'),
163
+ (r'>\s*/dev/(?:sd|nvme|mmcblk)', 'DANGEROUS: Redirecting output to a disk device can overwrite it'),
164
+ (r'cat\s+/dev/zero\s*>\s*/dev/(?:sd|nvme|mmcblk)', 'DANGEROUS: Overwriting disk with zeros'),
165
+ (r'nohup\s+.*&\s*$', 'CAUTION: Backgrounding processes without control may leave unmanaged jobs'),
166
+ ]
167
+
168
+ def explain_command(self, command: str) -> str:
169
+ """Explain a Bash command in detail"""
170
+ command = command.strip()
171
+
172
+ if not command:
173
+ return "No command provided."
174
+
175
+ output = []
176
+ output.append(clr(Colors.BOLD + Colors.BLUE, f"📝 Explaining: {command}") + "\n")
177
+
178
+ # Check for unsafe patterns
179
+ warnings = self._check_safety(command)
180
+ if warnings:
181
+ output.append(clr(Colors.RED + Colors.BOLD, "⚠️ SAFETY WARNINGS:"))
182
+ for warning in warnings:
183
+ output.append(clr(Colors.RED, f" {warning}"))
184
+ output.append("")
185
+
186
+ # Parse the command
187
+ parts = self._parse_command(command)
188
+
189
+ # Explain main command
190
+ if parts['command']:
191
+ cmd_name = parts['command']
192
+ cmd_desc = self.COMMON_COMMANDS.get(cmd_name, 'Custom command or script')
193
+ output.append(clr(Colors.CYAN + Colors.BOLD, f"🔧 Command: {cmd_name}"))
194
+ output.append(clr(Colors.DIM, f" {cmd_desc}") + "\n")
195
+
196
+ # Explain flags
197
+ if parts['flags']:
198
+ output.append(clr(Colors.YELLOW + Colors.BOLD, "🚩 Flags:"))
199
+ for flag in parts['flags']:
200
+ flag_desc = self.COMMON_FLAGS.get(flag, self._guess_flag_meaning(flag))
201
+ output.append(f" {clr(Colors.YELLOW, flag)} → {flag_desc}")
202
+ output.append("")
203
+
204
+ # Explain arguments
205
+ if parts['arguments']:
206
+ output.append(clr(Colors.GREEN + Colors.BOLD, "📂 Arguments:"))
207
+ for i, arg in enumerate(parts['arguments'], 1):
208
+ arg_type = self._identify_argument_type(arg)
209
+ output.append(f" {i}. {clr(Colors.GREEN, arg)} ({arg_type})")
210
+ output.append("")
211
+
212
+ # Explain pipes
213
+ if parts['pipes']:
214
+ output.append(clr(Colors.MAGENTA + Colors.BOLD, "🔀 Pipes:"))
215
+ output.append(" Commands are chained - output of one becomes input of next")
216
+ for i, pipe_cmd in enumerate(parts['pipes'], 1):
217
+ output.append(f" {i}. {clr(Colors.MAGENTA, pipe_cmd)}")
218
+ output.append("")
219
+
220
+ # Explain redirections
221
+ if parts['redirections']:
222
+ output.append(clr(Colors.BLUE + Colors.BOLD, "↩️ Redirections:"))
223
+ for redir in parts['redirections']:
224
+ output.append(f" {clr(Colors.BLUE, redir['symbol'])} {clr(Colors.GREEN, redir['target'])} → {redir['description']}")
225
+ output.append("")
226
+
227
+ # Add educational note
228
+ output.append(clr(Colors.GREEN, "💡 Tip: Use 'man <command>' for detailed documentation"))
229
+
230
+ return "\n".join(output)
231
+
232
+ def _parse_command(self, command: str) -> Dict:
233
+ """Parse command into components"""
234
+ result = {
235
+ 'command': None,
236
+ 'flags': [],
237
+ 'arguments': [],
238
+ 'pipes': [],
239
+ 'redirections': []
240
+ }
241
+
242
+ # Check for pipes
243
+ if '|' in command:
244
+ pipe_parts = command.split('|')
245
+ result['pipes'] = [p.strip() for p in pipe_parts]
246
+ command = pipe_parts[0].strip() # Process first part
247
+
248
+ # Check for redirections
249
+ redir_patterns = [
250
+ (r'>>?\s*(\S+)', 'redirect output to file'),
251
+ (r'2>>?\s*(\S+)', 'redirect errors to file'),
252
+ (r'&>>?\s*(\S+)', 'redirect both output and errors to file'),
253
+ (r'<\s*(\S+)', 'read input from file'),
254
+ ]
255
+
256
+ for pattern, desc in redir_patterns:
257
+ matches = re.finditer(pattern, command)
258
+ for match in matches:
259
+ symbol = match.group(0).split()[0]
260
+ target = match.group(1)
261
+ result['redirections'].append({
262
+ 'symbol': symbol,
263
+ 'target': target,
264
+ 'description': desc
265
+ })
266
+ command = command.replace(match.group(0), '')
267
+
268
+ # Split into tokens
269
+ tokens = command.split()
270
+
271
+ if not tokens:
272
+ return result
273
+
274
+ # First token is usually the command
275
+ result['command'] = tokens[0]
276
+
277
+ # Process remaining tokens
278
+ for token in tokens[1:]:
279
+ if token.startswith('-'):
280
+ result['flags'].append(token)
281
+ else:
282
+ result['arguments'].append(token)
283
+
284
+ return result
285
+
286
+ def _check_safety(self, command: str) -> List[str]:
287
+ """Check for unsafe command patterns"""
288
+ warnings = []
289
+ for pattern, warning in self.UNSAFE_PATTERNS:
290
+ if re.search(pattern, command):
291
+ warnings.append(warning)
292
+ return warnings
293
+
294
+ def _guess_flag_meaning(self, flag: str) -> str:
295
+ """Guess the meaning of an unknown flag"""
296
+ if flag.startswith('--'):
297
+ name = flag[2:].replace('-', ' ')
298
+ return f"Long option: {name}"
299
+ elif flag.startswith('-') and len(flag) > 2:
300
+ return "Combined short options: " + ", ".join(f"-{c}" for c in flag[1:])
301
+ return "Option (see 'man' for details)"
302
+
303
+ def _identify_argument_type(self, arg: str) -> str:
304
+ """Identify what type of argument this is"""
305
+ if arg.startswith('/'):
306
+ return "Absolute path"
307
+ elif arg.startswith('~'):
308
+ return "Home directory path"
309
+ elif arg.startswith('./') or arg.startswith('../'):
310
+ return "Relative path"
311
+ elif '/' in arg:
312
+ return "Path"
313
+ elif arg.startswith('$'):
314
+ return "Variable"
315
+ elif arg.startswith('"') or arg.startswith("'"):
316
+ return "String literal"
317
+ elif arg.isdigit():
318
+ return "Number"
319
+ elif '*' in arg or '?' in arg:
320
+ return "Glob pattern"
321
+ else:
322
+ return "Filename or value"
323
+
324
+ class ErrorExplainer:
325
+ """Explains common Bash and terminal errors"""
326
+
327
+ ERROR_PATTERNS = {
328
+ r'command not found': {
329
+ 'explanation': 'The shell cannot find the command you typed.',
330
+ 'causes': [
331
+ 'Typo in command name',
332
+ 'Command not installed on system',
333
+ 'Command not in PATH environment variable',
334
+ 'Missing ./ prefix for local script'
335
+ ],
336
+ 'solutions': [
337
+ 'Check spelling of command',
338
+ 'Install the required package',
339
+ 'Use full path to command',
340
+ 'For local script, use: ./script.sh'
341
+ ]
342
+ },
343
+ r'Permission denied': {
344
+ 'explanation': "You don't have permission to perform this operation.",
345
+ 'causes': [
346
+ 'File is not executable',
347
+ 'Insufficient user permissions',
348
+ 'File/directory owned by another user',
349
+ 'SELinux or AppArmor restrictions'
350
+ ],
351
+ 'solutions': [
352
+ 'Make file executable: chmod +x filename',
353
+ 'Use sudo if appropriate (be careful!)',
354
+ 'Check file ownership: ls -l filename',
355
+ 'Request access from system administrator'
356
+ ]
357
+ },
358
+ r'No such file or directory': {
359
+ 'explanation': 'The file or directory you specified does not exist.',
360
+ 'causes': [
361
+ 'Typo in filename or path',
362
+ 'File was moved or deleted',
363
+ 'Wrong current directory',
364
+ 'Case sensitivity (Linux is case-sensitive)'
365
+ ],
366
+ 'solutions': [
367
+ 'Check spelling and case',
368
+ 'Use ls to list available files',
369
+ 'Use pwd to verify current directory',
370
+ 'Use absolute path instead of relative'
371
+ ]
372
+ },
373
+ r'syntax error': {
374
+ 'explanation': 'The shell found invalid syntax in your command or script.',
375
+ 'causes': [
376
+ 'Missing quotes or brackets',
377
+ 'Unclosed string or command substitution',
378
+ 'Invalid operator or special character',
379
+ 'Wrong syntax for control structure'
380
+ ],
381
+ 'solutions': [
382
+ 'Check for matching quotes and brackets',
383
+ 'Review Bash syntax documentation',
384
+ 'Use shellcheck to validate scripts',
385
+ 'Break complex commands into simpler parts'
386
+ ]
387
+ },
388
+ r'cannot remove.*Directory not empty': {
389
+ 'explanation': 'Cannot delete directory because it contains files.',
390
+ 'causes': [
391
+ 'Using rmdir on non-empty directory',
392
+ 'Directory contains hidden files'
393
+ ],
394
+ 'solutions': [
395
+ 'Use rm -r to remove directory and contents',
396
+ 'Use rm -ri for interactive deletion (safer)',
397
+ 'Check for hidden files with ls -la'
398
+ ]
399
+ },
400
+ r'Disk quota exceeded': {
401
+ 'explanation': 'You have exceeded your allocated disk space.',
402
+ 'causes': [
403
+ 'Too many files or large files',
404
+ 'Disk quota policy on system'
405
+ ],
406
+ 'solutions': [
407
+ 'Delete unnecessary files',
408
+ 'Check disk usage: du -sh *',
409
+ 'Contact administrator for quota increase'
410
+ ]
411
+ },
412
+ r'Text file busy': {
413
+ 'explanation': 'Cannot modify file because it is being executed.',
414
+ 'causes': [
415
+ 'Trying to edit or delete running script/binary',
416
+ 'File is currently in use by a process'
417
+ ],
418
+ 'solutions': [
419
+ 'Stop the running process first',
420
+ 'Use ps aux | grep filename to find process',
421
+ 'Kill process with: kill <PID>'
422
+ ]
423
+ },
424
+ }
425
+
426
+ def explain_error(self, error_message: str) -> str:
427
+ """Explain a Bash error message"""
428
+ output = []
429
+ output.append(clr(Colors.RED + Colors.BOLD, f"🔴 Error Message: {error_message}") + "\n")
430
+
431
+ # Find matching error pattern
432
+ matched = False
433
+ for pattern, info in self.ERROR_PATTERNS.items():
434
+ if re.search(pattern, error_message, re.IGNORECASE):
435
+ matched = True
436
+ output.append(clr(Colors.CYAN + Colors.BOLD, "📖 Explanation:"))
437
+ output.append(f" {info['explanation']}\n")
438
+
439
+ output.append(clr(Colors.YELLOW + Colors.BOLD, "🔍 Common Causes:"))
440
+ for i, cause in enumerate(info['causes'], 1):
441
+ output.append(f" {i}. {cause}")
442
+ output.append("")
443
+
444
+ output.append(clr(Colors.GREEN + Colors.BOLD, "✅ Possible Solutions:"))
445
+ for i, solution in enumerate(info['solutions'], 1):
446
+ output.append(f" {i}. {solution}")
447
+ output.append("")
448
+
449
+ break
450
+
451
+ if not matched:
452
+ output.append(clr(Colors.DIM, " This error is not in our database yet."))
453
+ output.append(" General troubleshooting steps:")
454
+ output.append(" 1. Read the full error message carefully")
455
+ output.append(" 2. Check your command syntax")
456
+ output.append(" 3. Verify file/directory names and paths")
457
+ output.append(" 4. Search online for the specific error")
458
+ output.append(" 5. Check relevant log files")
459
+ output.append("")
460
+
461
+ output.append(clr(Colors.GREEN, "💡 Tip: Copy the exact error message when searching online"))
462
+
463
+ return "\n".join(output)
464
+
465
+ class ScriptExplainer:
466
+ """Explains Bash scripts line by line"""
467
+
468
+ def explain_script(self, script_path: str) -> str:
469
+ """Explain a Bash script"""
470
+ try:
471
+ with open(script_path, 'r') as f:
472
+ lines = f.readlines()
473
+ except FileNotFoundError:
474
+ return clr(Colors.RED, f"❌ Error: Script file '{script_path}' not found")
475
+ except PermissionError:
476
+ return clr(Colors.RED, f"❌ Error: Permission denied reading '{script_path}'")
477
+
478
+ output = []
479
+ output.append(clr(Colors.CYAN + Colors.BOLD, f"📜 Explaining Script: {script_path}") + "\n")
480
+
481
+ for i, line in enumerate(lines, 1):
482
+ line = line.rstrip()
483
+
484
+ if not line or line.strip().startswith('#'):
485
+ # Comment or empty line
486
+ if line.strip().startswith('#!'):
487
+ output.append(clr(Colors.DIM, f"{i:3d} | ") + clr(Colors.MAGENTA + Colors.BOLD, line))
488
+ output.append(clr(Colors.DIM, " ↳ Shebang: Tells system which interpreter to use") + "\n")
489
+ elif line.strip().startswith('#'):
490
+ output.append(clr(Colors.DIM, f"{i:3d} | {line}"))
491
+ output.append(clr(Colors.DIM, " ↳ Comment: Explanation for humans, ignored by shell") + "\n")
492
+ else:
493
+ output.append(clr(Colors.DIM, f"{i:3d} | {line}") + "\n")
494
+ else:
495
+ output.append(clr(Colors.DIM, f"{i:3d} | ") + line)
496
+ explanation = self._explain_line(line.strip())
497
+ if explanation:
498
+ output.append(clr(Colors.DIM, " ↳ ") + clr(Colors.GREEN, explanation) + "\n")
499
+
500
+ return "\n".join(output)
501
+
502
+ def _explain_line(self, line: str) -> str:
503
+ """Explain a single line of script"""
504
+ # Variable assignment
505
+ if '=' in line and not line.startswith('if') and not line.startswith('['):
506
+ if line.split('=')[0].strip().isidentifier():
507
+ var_name = line.split('=')[0].strip()
508
+ return f"Assigns value to variable '{var_name}'"
509
+
510
+ # Conditionals
511
+ if line.startswith('if'):
512
+ return "Conditional: Executes code if condition is true"
513
+ if line.startswith('elif'):
514
+ return "Else-if: Alternative condition if previous was false"
515
+ if line.startswith('else'):
516
+ return "Else: Executes if all conditions were false"
517
+ if line.startswith('fi'):
518
+ return "Ends if statement"
519
+
520
+ # Loops
521
+ if line.startswith('for'):
522
+ return "For loop: Iterates over a list of items"
523
+ if line.startswith('while'):
524
+ return "While loop: Repeats while condition is true"
525
+ if line.startswith('until'):
526
+ return "Until loop: Repeats until condition becomes true"
527
+ if line.startswith('done'):
528
+ return "Ends loop"
529
+
530
+ # Functions
531
+ if '()' in line and '{' in line:
532
+ func_name = line.split('()')[0].strip()
533
+ return f"Defines function '{func_name}'"
534
+
535
+ # Case statement
536
+ if line.startswith('case'):
537
+ return "Case statement: Matches value against patterns"
538
+ if line.startswith('esac'):
539
+ return "Ends case statement"
540
+
541
+ # Common commands
542
+ if line.startswith('echo'):
543
+ return "Prints text or variables to terminal"
544
+ if line.startswith('read'):
545
+ return "Reads input from user into variable"
546
+ if line.startswith('exit'):
547
+ return "Exits script with status code"
548
+ if line.startswith('return'):
549
+ return "Returns from function with status code"
550
+
551
+ # Brackets and tests
552
+ if line.startswith('[') or line.startswith('[['):
553
+ return "Test condition (checks if something is true)"
554
+
555
+ return "Executes command (see command explanation for details)"
556
+
557
+ def main():
558
+ parser = argparse.ArgumentParser(
559
+ description='BashExplain - Understand Bash commands, errors, and scripts',
560
+ formatter_class=argparse.RawDescriptionHelpFormatter,
561
+ epilog="""
562
+ Examples:
563
+ bashexplain command "ls -lah /home"
564
+ bashexplain error "bash: command not found"
565
+ bashexplain script myscript.sh
566
+ """
567
+ )
568
+
569
+ parser.add_argument(
570
+ '--no-color', action='store_true',
571
+ help='Disable colored output (useful when piping)'
572
+ )
573
+
574
+ subparsers = parser.add_subparsers(dest='mode', help='Mode of operation')
575
+
576
+ # Command mode
577
+ cmd_parser = subparsers.add_parser('command', help='Explain a Bash command')
578
+ cmd_parser.add_argument('command', nargs='+', help='Command to explain')
579
+
580
+ # Error mode
581
+ err_parser = subparsers.add_parser('error', help='Explain an error message')
582
+ err_parser.add_argument('message', nargs='+', help='Error message to explain')
583
+
584
+ # Script mode
585
+ script_parser = subparsers.add_parser('script', help='Explain a Bash script')
586
+ script_parser.add_argument('file', help='Script file to explain')
587
+
588
+ args = parser.parse_args()
589
+
590
+ # Disable color when piping or --no-color flag is used
591
+ if args.no_color or not sys.stdout.isatty():
592
+ Colors.disable()
593
+
594
+ if not args.mode:
595
+ parser.print_help()
596
+ return
597
+
598
+ if args.mode == 'command':
599
+ command = ' '.join(args.command)
600
+ explainer = CommandExplainer()
601
+ print(explainer.explain_command(command))
602
+
603
+ elif args.mode == 'error':
604
+ error = ' '.join(args.message)
605
+ explainer = ErrorExplainer()
606
+ print(explainer.explain_error(error))
607
+
608
+ elif args.mode == 'script':
609
+ explainer = ScriptExplainer()
610
+ print(explainer.explain_script(args.file))
611
+
612
+ if __name__ == '__main__':
613
+ main()
@@ -0,0 +1,88 @@
1
+ Metadata-Version: 2.4
2
+ Name: bashexplain
3
+ Version: 1.0.0
4
+ Summary: A command-line tool to explain Bash commands, errors, and scripts
5
+ Author-email: Ashish Singh <ashishsingh.mail26@gmail.com>, Ravi Poddar <ravipoddar2006@gmail.com>
6
+ Project-URL: Homepage, https://github.com/singhashish12238-pixel/bash-explain
7
+ Project-URL: Bug Tracker, https://github.com/singhashish12238-pixel/bash-explain/issues
8
+ Project-URL: Source Code, https://github.com/singhashish12238-pixel/bash-explain
9
+ Project-URL: Documentation, https://github.com/singhashish12238-pixel/bash-explain#readme
10
+ Keywords: bash,shell,terminal,cli,education,learning,command-line
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Intended Audience :: Education
14
+ Classifier: Intended Audience :: System Administrators
15
+ Classifier: Topic :: Education
16
+ Classifier: Topic :: System :: Shells
17
+ Classifier: Topic :: Utilities
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3.6
21
+ Classifier: Programming Language :: Python :: 3.7
22
+ Classifier: Programming Language :: Python :: 3.8
23
+ Classifier: Programming Language :: Python :: 3.9
24
+ Classifier: Programming Language :: Python :: 3.10
25
+ Classifier: Programming Language :: Python :: 3.11
26
+ Classifier: Programming Language :: Python :: 3.12
27
+ Classifier: Operating System :: POSIX :: Linux
28
+ Classifier: Operating System :: MacOS
29
+ Classifier: Operating System :: Microsoft :: Windows
30
+ Requires-Python: >=3.6
31
+ Description-Content-Type: text/markdown
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=6.0; extra == "dev"
34
+ Requires-Dist: black>=21.0; extra == "dev"
35
+ Requires-Dist: flake8>=3.9; extra == "dev"
36
+ Requires-Dist: twine>=4.0; extra == "dev"
37
+ Requires-Dist: build>=0.10; extra == "dev"
38
+
39
+ # Bash Explain
40
+
41
+ A Python-based tool for explaining Bash commands with intelligent parsing, flag recognition, and security analysis. Helps developers understand command syntax and identify potentially dangerous operations.
42
+
43
+ ## Features Implemented
44
+
45
+ ### Command Explanation Engine
46
+ - Parses and explains bash commands with detailed breakdowns
47
+ - 60+ common commands with descriptions across multiple categories
48
+
49
+ ### Flag & Option Recognition
50
+ - 40+ common flag patterns (e.g., `-a`, `-r`, `-v`, `--help`)
51
+ - Automatic meaning identification
52
+
53
+ ### Security Analysis
54
+ - Detects 24+ dangerous command patterns
55
+ - Warns about destructive operations, unsafe downloads, permission changes
56
+ - Identifies fork bombs, system modifications, and more
57
+
58
+ ### Command Parsing
59
+ - Extracts flags, arguments, pipes, and redirections
60
+ - Classifies argument types
61
+ - Handles custom commands
62
+
63
+ ## Technology
64
+
65
+ - **Language**: Python 3
66
+ - **Design**: OOP with `CommandExplainer` class
67
+ - **Dependencies**: Standard library only
68
+ - **Method**: Pattern matching and rule-based analysis
69
+
70
+ ## Completed
71
+
72
+ ✅ Command explanation engine
73
+ ✅ Command & flag database
74
+ ✅ Safety pattern detection system
75
+ ✅ Command parser & output formatting
76
+ ✅ Type hints for code maintainability
77
+
78
+ ## Coming Soon
79
+
80
+ 🔄 Interactive REPL mode
81
+ 🔄 Script file analysis
82
+ 🔄 Configuration support
83
+ 🔄 Extended command database
84
+
85
+ ## Authors
86
+
87
+ - Ashish Singh
88
+ - Ravi Poddar
@@ -0,0 +1,6 @@
1
+ bash_explain.py,sha256=rKpeJdHl0QjvN480JU5hge2vHBMYTlUxxeVYy9udtbc,25281
2
+ bashexplain-1.0.0.dist-info/METADATA,sha256=hqW1-vZxUhBDb_ff005BXSIrXxl_Hikoqmoo4dZHEic,3227
3
+ bashexplain-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
4
+ bashexplain-1.0.0.dist-info/entry_points.txt,sha256=1wAa28C8jn4-Uwe79ZIxCzTfeJOI0JDQiTpp5hdv-ek,50
5
+ bashexplain-1.0.0.dist-info/top_level.txt,sha256=PjxUnrt3_r5xPpUGKYjkf5ApTDsIKh4_j51GX3S9h6M,13
6
+ bashexplain-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ bashexplain = bash_explain:main
@@ -0,0 +1 @@
1
+ bash_explain