bashexplain 1.0.0__tar.gz
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.
- bashexplain-1.0.0/PKG-INFO +88 -0
- bashexplain-1.0.0/README.md +50 -0
- bashexplain-1.0.0/bash_explain.py +613 -0
- bashexplain-1.0.0/bashexplain.egg-info/PKG-INFO +88 -0
- bashexplain-1.0.0/bashexplain.egg-info/SOURCES.txt +10 -0
- bashexplain-1.0.0/bashexplain.egg-info/dependency_links.txt +1 -0
- bashexplain-1.0.0/bashexplain.egg-info/entry_points.txt +2 -0
- bashexplain-1.0.0/bashexplain.egg-info/requires.txt +7 -0
- bashexplain-1.0.0/bashexplain.egg-info/top_level.txt +1 -0
- bashexplain-1.0.0/pyproject.toml +58 -0
- bashexplain-1.0.0/setup.cfg +4 -0
- bashexplain-1.0.0/setup.py +8 -0
|
@@ -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,50 @@
|
|
|
1
|
+
# Bash Explain
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
## Features Implemented
|
|
6
|
+
|
|
7
|
+
### Command Explanation Engine
|
|
8
|
+
- Parses and explains bash commands with detailed breakdowns
|
|
9
|
+
- 60+ common commands with descriptions across multiple categories
|
|
10
|
+
|
|
11
|
+
### Flag & Option Recognition
|
|
12
|
+
- 40+ common flag patterns (e.g., `-a`, `-r`, `-v`, `--help`)
|
|
13
|
+
- Automatic meaning identification
|
|
14
|
+
|
|
15
|
+
### Security Analysis
|
|
16
|
+
- Detects 24+ dangerous command patterns
|
|
17
|
+
- Warns about destructive operations, unsafe downloads, permission changes
|
|
18
|
+
- Identifies fork bombs, system modifications, and more
|
|
19
|
+
|
|
20
|
+
### Command Parsing
|
|
21
|
+
- Extracts flags, arguments, pipes, and redirections
|
|
22
|
+
- Classifies argument types
|
|
23
|
+
- Handles custom commands
|
|
24
|
+
|
|
25
|
+
## Technology
|
|
26
|
+
|
|
27
|
+
- **Language**: Python 3
|
|
28
|
+
- **Design**: OOP with `CommandExplainer` class
|
|
29
|
+
- **Dependencies**: Standard library only
|
|
30
|
+
- **Method**: Pattern matching and rule-based analysis
|
|
31
|
+
|
|
32
|
+
## Completed
|
|
33
|
+
|
|
34
|
+
✅ Command explanation engine
|
|
35
|
+
✅ Command & flag database
|
|
36
|
+
✅ Safety pattern detection system
|
|
37
|
+
✅ Command parser & output formatting
|
|
38
|
+
✅ Type hints for code maintainability
|
|
39
|
+
|
|
40
|
+
## Coming Soon
|
|
41
|
+
|
|
42
|
+
🔄 Interactive REPL mode
|
|
43
|
+
🔄 Script file analysis
|
|
44
|
+
🔄 Configuration support
|
|
45
|
+
🔄 Extended command database
|
|
46
|
+
|
|
47
|
+
## Authors
|
|
48
|
+
|
|
49
|
+
- Ashish Singh
|
|
50
|
+
- Ravi Poddar
|
|
@@ -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,10 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
bash_explain.py
|
|
3
|
+
pyproject.toml
|
|
4
|
+
setup.py
|
|
5
|
+
bashexplain.egg-info/PKG-INFO
|
|
6
|
+
bashexplain.egg-info/SOURCES.txt
|
|
7
|
+
bashexplain.egg-info/dependency_links.txt
|
|
8
|
+
bashexplain.egg-info/entry_points.txt
|
|
9
|
+
bashexplain.egg-info/requires.txt
|
|
10
|
+
bashexplain.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
bash_explain
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "bashexplain"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "Ashish Singh", email = "ashishsingh.mail26@gmail.com" },
|
|
10
|
+
{ name = "Ravi Poddar", email = "ravipoddar2006@gmail.com" },
|
|
11
|
+
]
|
|
12
|
+
description = "A command-line tool to explain Bash commands, errors, and scripts"
|
|
13
|
+
readme = "README.md"
|
|
14
|
+
requires-python = ">=3.6"
|
|
15
|
+
keywords = ["bash", "shell", "terminal", "cli", "education", "learning", "command-line"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 4 - Beta",
|
|
18
|
+
"Intended Audience :: Developers",
|
|
19
|
+
"Intended Audience :: Education",
|
|
20
|
+
"Intended Audience :: System Administrators",
|
|
21
|
+
"Topic :: Education",
|
|
22
|
+
"Topic :: System :: Shells",
|
|
23
|
+
"Topic :: Utilities",
|
|
24
|
+
"License :: OSI Approved :: MIT License",
|
|
25
|
+
"Programming Language :: Python :: 3",
|
|
26
|
+
"Programming Language :: Python :: 3.6",
|
|
27
|
+
"Programming Language :: Python :: 3.7",
|
|
28
|
+
"Programming Language :: Python :: 3.8",
|
|
29
|
+
"Programming Language :: Python :: 3.9",
|
|
30
|
+
"Programming Language :: Python :: 3.10",
|
|
31
|
+
"Programming Language :: Python :: 3.11",
|
|
32
|
+
"Programming Language :: Python :: 3.12",
|
|
33
|
+
"Operating System :: POSIX :: Linux",
|
|
34
|
+
"Operating System :: MacOS",
|
|
35
|
+
"Operating System :: Microsoft :: Windows",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://github.com/singhashish12238-pixel/bash-explain"
|
|
40
|
+
"Bug Tracker" = "https://github.com/singhashish12238-pixel/bash-explain/issues"
|
|
41
|
+
"Source Code" = "https://github.com/singhashish12238-pixel/bash-explain"
|
|
42
|
+
Documentation = "https://github.com/singhashish12238-pixel/bash-explain#readme"
|
|
43
|
+
|
|
44
|
+
[project.scripts]
|
|
45
|
+
bashexplain = "bash_explain:main"
|
|
46
|
+
|
|
47
|
+
[project.optional-dependencies]
|
|
48
|
+
dev = [
|
|
49
|
+
"pytest>=6.0",
|
|
50
|
+
"black>=21.0",
|
|
51
|
+
"flake8>=3.9",
|
|
52
|
+
"twine>=4.0",
|
|
53
|
+
"build>=0.10",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
[tool.setuptools]
|
|
57
|
+
py-modules = ["bash_explain"]
|
|
58
|
+
license-files = []
|