just-bash 0.1.5__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.
- just_bash/__init__.py +55 -0
- just_bash/ast/__init__.py +213 -0
- just_bash/ast/factory.py +320 -0
- just_bash/ast/types.py +953 -0
- just_bash/bash.py +220 -0
- just_bash/commands/__init__.py +23 -0
- just_bash/commands/argv/__init__.py +5 -0
- just_bash/commands/argv/argv.py +21 -0
- just_bash/commands/awk/__init__.py +5 -0
- just_bash/commands/awk/awk.py +1168 -0
- just_bash/commands/base64/__init__.py +5 -0
- just_bash/commands/base64/base64.py +138 -0
- just_bash/commands/basename/__init__.py +5 -0
- just_bash/commands/basename/basename.py +72 -0
- just_bash/commands/bash/__init__.py +5 -0
- just_bash/commands/bash/bash.py +188 -0
- just_bash/commands/cat/__init__.py +5 -0
- just_bash/commands/cat/cat.py +173 -0
- just_bash/commands/checksum/__init__.py +5 -0
- just_bash/commands/checksum/checksum.py +179 -0
- just_bash/commands/chmod/__init__.py +5 -0
- just_bash/commands/chmod/chmod.py +216 -0
- just_bash/commands/column/__init__.py +5 -0
- just_bash/commands/column/column.py +180 -0
- just_bash/commands/comm/__init__.py +5 -0
- just_bash/commands/comm/comm.py +150 -0
- just_bash/commands/compression/__init__.py +5 -0
- just_bash/commands/compression/compression.py +298 -0
- just_bash/commands/cp/__init__.py +5 -0
- just_bash/commands/cp/cp.py +149 -0
- just_bash/commands/curl/__init__.py +5 -0
- just_bash/commands/curl/curl.py +801 -0
- just_bash/commands/cut/__init__.py +5 -0
- just_bash/commands/cut/cut.py +327 -0
- just_bash/commands/date/__init__.py +5 -0
- just_bash/commands/date/date.py +258 -0
- just_bash/commands/diff/__init__.py +5 -0
- just_bash/commands/diff/diff.py +118 -0
- just_bash/commands/dirname/__init__.py +5 -0
- just_bash/commands/dirname/dirname.py +56 -0
- just_bash/commands/du/__init__.py +5 -0
- just_bash/commands/du/du.py +150 -0
- just_bash/commands/echo/__init__.py +5 -0
- just_bash/commands/echo/echo.py +125 -0
- just_bash/commands/env/__init__.py +5 -0
- just_bash/commands/env/env.py +163 -0
- just_bash/commands/expand/__init__.py +5 -0
- just_bash/commands/expand/expand.py +299 -0
- just_bash/commands/expr/__init__.py +5 -0
- just_bash/commands/expr/expr.py +273 -0
- just_bash/commands/file/__init__.py +5 -0
- just_bash/commands/file/file.py +274 -0
- just_bash/commands/find/__init__.py +5 -0
- just_bash/commands/find/find.py +623 -0
- just_bash/commands/fold/__init__.py +5 -0
- just_bash/commands/fold/fold.py +160 -0
- just_bash/commands/grep/__init__.py +5 -0
- just_bash/commands/grep/grep.py +418 -0
- just_bash/commands/head/__init__.py +5 -0
- just_bash/commands/head/head.py +167 -0
- just_bash/commands/help/__init__.py +5 -0
- just_bash/commands/help/help.py +67 -0
- just_bash/commands/hostname/__init__.py +5 -0
- just_bash/commands/hostname/hostname.py +21 -0
- just_bash/commands/html_to_markdown/__init__.py +5 -0
- just_bash/commands/html_to_markdown/html_to_markdown.py +191 -0
- just_bash/commands/join/__init__.py +5 -0
- just_bash/commands/join/join.py +252 -0
- just_bash/commands/jq/__init__.py +5 -0
- just_bash/commands/jq/jq.py +280 -0
- just_bash/commands/ln/__init__.py +5 -0
- just_bash/commands/ln/ln.py +127 -0
- just_bash/commands/ls/__init__.py +5 -0
- just_bash/commands/ls/ls.py +280 -0
- just_bash/commands/mkdir/__init__.py +5 -0
- just_bash/commands/mkdir/mkdir.py +92 -0
- just_bash/commands/mv/__init__.py +5 -0
- just_bash/commands/mv/mv.py +142 -0
- just_bash/commands/nl/__init__.py +5 -0
- just_bash/commands/nl/nl.py +180 -0
- just_bash/commands/od/__init__.py +5 -0
- just_bash/commands/od/od.py +157 -0
- just_bash/commands/paste/__init__.py +5 -0
- just_bash/commands/paste/paste.py +100 -0
- just_bash/commands/printf/__init__.py +5 -0
- just_bash/commands/printf/printf.py +157 -0
- just_bash/commands/pwd/__init__.py +5 -0
- just_bash/commands/pwd/pwd.py +23 -0
- just_bash/commands/read/__init__.py +5 -0
- just_bash/commands/read/read.py +185 -0
- just_bash/commands/readlink/__init__.py +5 -0
- just_bash/commands/readlink/readlink.py +86 -0
- just_bash/commands/registry.py +844 -0
- just_bash/commands/rev/__init__.py +5 -0
- just_bash/commands/rev/rev.py +74 -0
- just_bash/commands/rg/__init__.py +5 -0
- just_bash/commands/rg/rg.py +1048 -0
- just_bash/commands/rm/__init__.py +5 -0
- just_bash/commands/rm/rm.py +106 -0
- just_bash/commands/search_engine/__init__.py +13 -0
- just_bash/commands/search_engine/matcher.py +170 -0
- just_bash/commands/search_engine/regex.py +159 -0
- just_bash/commands/sed/__init__.py +5 -0
- just_bash/commands/sed/sed.py +863 -0
- just_bash/commands/seq/__init__.py +5 -0
- just_bash/commands/seq/seq.py +190 -0
- just_bash/commands/shell/__init__.py +5 -0
- just_bash/commands/shell/shell.py +206 -0
- just_bash/commands/sleep/__init__.py +5 -0
- just_bash/commands/sleep/sleep.py +62 -0
- just_bash/commands/sort/__init__.py +5 -0
- just_bash/commands/sort/sort.py +411 -0
- just_bash/commands/split/__init__.py +5 -0
- just_bash/commands/split/split.py +237 -0
- just_bash/commands/sqlite3/__init__.py +5 -0
- just_bash/commands/sqlite3/sqlite3_cmd.py +505 -0
- just_bash/commands/stat/__init__.py +5 -0
- just_bash/commands/stat/stat.py +150 -0
- just_bash/commands/strings/__init__.py +5 -0
- just_bash/commands/strings/strings.py +150 -0
- just_bash/commands/tac/__init__.py +5 -0
- just_bash/commands/tac/tac.py +158 -0
- just_bash/commands/tail/__init__.py +5 -0
- just_bash/commands/tail/tail.py +180 -0
- just_bash/commands/tar/__init__.py +5 -0
- just_bash/commands/tar/tar.py +1067 -0
- just_bash/commands/tee/__init__.py +5 -0
- just_bash/commands/tee/tee.py +63 -0
- just_bash/commands/timeout/__init__.py +5 -0
- just_bash/commands/timeout/timeout.py +188 -0
- just_bash/commands/touch/__init__.py +5 -0
- just_bash/commands/touch/touch.py +91 -0
- just_bash/commands/tr/__init__.py +5 -0
- just_bash/commands/tr/tr.py +297 -0
- just_bash/commands/tree/__init__.py +5 -0
- just_bash/commands/tree/tree.py +139 -0
- just_bash/commands/true/__init__.py +5 -0
- just_bash/commands/true/true.py +32 -0
- just_bash/commands/uniq/__init__.py +5 -0
- just_bash/commands/uniq/uniq.py +323 -0
- just_bash/commands/wc/__init__.py +5 -0
- just_bash/commands/wc/wc.py +169 -0
- just_bash/commands/which/__init__.py +5 -0
- just_bash/commands/which/which.py +52 -0
- just_bash/commands/xan/__init__.py +5 -0
- just_bash/commands/xan/xan.py +1663 -0
- just_bash/commands/xargs/__init__.py +5 -0
- just_bash/commands/xargs/xargs.py +136 -0
- just_bash/commands/yq/__init__.py +5 -0
- just_bash/commands/yq/yq.py +848 -0
- just_bash/fs/__init__.py +29 -0
- just_bash/fs/in_memory_fs.py +621 -0
- just_bash/fs/mountable_fs.py +504 -0
- just_bash/fs/overlay_fs.py +894 -0
- just_bash/fs/read_write_fs.py +455 -0
- just_bash/interpreter/__init__.py +37 -0
- just_bash/interpreter/builtins/__init__.py +92 -0
- just_bash/interpreter/builtins/alias.py +154 -0
- just_bash/interpreter/builtins/cd.py +76 -0
- just_bash/interpreter/builtins/control.py +127 -0
- just_bash/interpreter/builtins/declare.py +336 -0
- just_bash/interpreter/builtins/export.py +56 -0
- just_bash/interpreter/builtins/let.py +44 -0
- just_bash/interpreter/builtins/local.py +57 -0
- just_bash/interpreter/builtins/mapfile.py +152 -0
- just_bash/interpreter/builtins/misc.py +378 -0
- just_bash/interpreter/builtins/readonly.py +80 -0
- just_bash/interpreter/builtins/set.py +234 -0
- just_bash/interpreter/builtins/shopt.py +201 -0
- just_bash/interpreter/builtins/source.py +136 -0
- just_bash/interpreter/builtins/test.py +290 -0
- just_bash/interpreter/builtins/unset.py +53 -0
- just_bash/interpreter/conditionals.py +387 -0
- just_bash/interpreter/control_flow.py +381 -0
- just_bash/interpreter/errors.py +116 -0
- just_bash/interpreter/expansion.py +1156 -0
- just_bash/interpreter/interpreter.py +813 -0
- just_bash/interpreter/types.py +134 -0
- just_bash/network/__init__.py +1 -0
- just_bash/parser/__init__.py +39 -0
- just_bash/parser/lexer.py +948 -0
- just_bash/parser/parser.py +2162 -0
- just_bash/py.typed +0 -0
- just_bash/query_engine/__init__.py +83 -0
- just_bash/query_engine/builtins/__init__.py +1283 -0
- just_bash/query_engine/evaluator.py +578 -0
- just_bash/query_engine/parser.py +525 -0
- just_bash/query_engine/tokenizer.py +329 -0
- just_bash/query_engine/types.py +373 -0
- just_bash/types.py +180 -0
- just_bash-0.1.5.dist-info/METADATA +410 -0
- just_bash-0.1.5.dist-info/RECORD +193 -0
- just_bash-0.1.5.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Read command implementation.
|
|
2
|
+
|
|
3
|
+
Usage: read [-r] [-d delim] [-n nchars] [-p prompt] [-t timeout] [name ...]
|
|
4
|
+
|
|
5
|
+
Read a line from stdin and split it into fields.
|
|
6
|
+
|
|
7
|
+
Options:
|
|
8
|
+
-r Do not treat backslash as escape character
|
|
9
|
+
-d delim Use delim as line delimiter instead of newline
|
|
10
|
+
-n nchars Read only nchars characters
|
|
11
|
+
-p prompt Output the string prompt before reading
|
|
12
|
+
-t timeout Time out after timeout seconds
|
|
13
|
+
|
|
14
|
+
If no names are given, the line is stored in REPLY.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from ...types import CommandContext, ExecResult
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ReadCommand:
|
|
21
|
+
"""The read builtin command."""
|
|
22
|
+
|
|
23
|
+
name = "read"
|
|
24
|
+
|
|
25
|
+
async def execute(self, args: list[str], ctx: CommandContext) -> ExecResult:
|
|
26
|
+
"""Execute the read command."""
|
|
27
|
+
# Parse options
|
|
28
|
+
raw_mode = False
|
|
29
|
+
delimiter = "\n"
|
|
30
|
+
nchars = None
|
|
31
|
+
array_name = None # -a option
|
|
32
|
+
var_names = []
|
|
33
|
+
|
|
34
|
+
i = 0
|
|
35
|
+
while i < len(args):
|
|
36
|
+
arg = args[i]
|
|
37
|
+
if arg == "-r":
|
|
38
|
+
raw_mode = True
|
|
39
|
+
elif arg == "-a" and i + 1 < len(args):
|
|
40
|
+
i += 1
|
|
41
|
+
array_name = args[i]
|
|
42
|
+
elif arg == "-d" and i + 1 < len(args):
|
|
43
|
+
i += 1
|
|
44
|
+
delimiter = args[i]
|
|
45
|
+
elif arg == "-n" and i + 1 < len(args):
|
|
46
|
+
i += 1
|
|
47
|
+
try:
|
|
48
|
+
nchars = int(args[i])
|
|
49
|
+
except ValueError:
|
|
50
|
+
return ExecResult(
|
|
51
|
+
stdout="",
|
|
52
|
+
stderr=f"bash: read: {args[i]}: invalid number\n",
|
|
53
|
+
exit_code=1,
|
|
54
|
+
)
|
|
55
|
+
elif arg == "-p" and i + 1 < len(args):
|
|
56
|
+
# Prompt option - we ignore it since we can't prompt
|
|
57
|
+
i += 1
|
|
58
|
+
elif arg == "-t" and i + 1 < len(args):
|
|
59
|
+
# Timeout option - we ignore it
|
|
60
|
+
i += 1
|
|
61
|
+
elif arg.startswith("-"):
|
|
62
|
+
# Unknown option - ignore for compatibility
|
|
63
|
+
pass
|
|
64
|
+
else:
|
|
65
|
+
var_names.append(arg)
|
|
66
|
+
i += 1
|
|
67
|
+
|
|
68
|
+
# Default variable is REPLY
|
|
69
|
+
if not var_names:
|
|
70
|
+
var_names = ["REPLY"]
|
|
71
|
+
|
|
72
|
+
# Get input from stdin
|
|
73
|
+
stdin = ctx.stdin or ""
|
|
74
|
+
|
|
75
|
+
# Find the line to read
|
|
76
|
+
if delimiter == "\n":
|
|
77
|
+
# Standard line reading
|
|
78
|
+
lines = stdin.split("\n")
|
|
79
|
+
line = lines[0] if lines else ""
|
|
80
|
+
else:
|
|
81
|
+
# Custom delimiter
|
|
82
|
+
parts = stdin.split(delimiter)
|
|
83
|
+
line = parts[0] if parts else ""
|
|
84
|
+
|
|
85
|
+
# Apply nchars limit
|
|
86
|
+
if nchars is not None:
|
|
87
|
+
line = line[:nchars]
|
|
88
|
+
|
|
89
|
+
# Process backslash escapes if not in raw mode
|
|
90
|
+
if not raw_mode:
|
|
91
|
+
# Handle backslash-newline continuation (remove them)
|
|
92
|
+
line = line.replace("\\\n", "")
|
|
93
|
+
# Handle other escapes
|
|
94
|
+
result = []
|
|
95
|
+
i = 0
|
|
96
|
+
while i < len(line):
|
|
97
|
+
if line[i] == "\\" and i + 1 < len(line):
|
|
98
|
+
# Escape the next character
|
|
99
|
+
result.append(line[i + 1])
|
|
100
|
+
i += 2
|
|
101
|
+
else:
|
|
102
|
+
result.append(line[i])
|
|
103
|
+
i += 1
|
|
104
|
+
line = "".join(result)
|
|
105
|
+
|
|
106
|
+
# Split on IFS
|
|
107
|
+
ifs = ctx.env.get("IFS", " \t\n")
|
|
108
|
+
if ifs:
|
|
109
|
+
# Split on IFS characters
|
|
110
|
+
words = self._split_on_ifs(line, ifs)
|
|
111
|
+
else:
|
|
112
|
+
# Empty IFS - no splitting
|
|
113
|
+
words = [line] if line else []
|
|
114
|
+
|
|
115
|
+
# Handle -a option (read into array)
|
|
116
|
+
if array_name:
|
|
117
|
+
# Clear existing array elements
|
|
118
|
+
prefix = f"{array_name}_"
|
|
119
|
+
to_remove = [k for k in ctx.env if k.startswith(prefix) and not k.startswith(f"{array_name}__")]
|
|
120
|
+
for k in to_remove:
|
|
121
|
+
del ctx.env[k]
|
|
122
|
+
|
|
123
|
+
# Mark as array
|
|
124
|
+
ctx.env[f"{array_name}__is_array"] = "indexed"
|
|
125
|
+
|
|
126
|
+
# Store each word as array element
|
|
127
|
+
for idx, word in enumerate(words):
|
|
128
|
+
ctx.env[f"{array_name}_{idx}"] = word
|
|
129
|
+
|
|
130
|
+
exit_code = 0 if stdin else 1
|
|
131
|
+
return ExecResult(stdout="", stderr="", exit_code=exit_code)
|
|
132
|
+
|
|
133
|
+
# Assign to variables
|
|
134
|
+
for i, var in enumerate(var_names):
|
|
135
|
+
if i < len(words):
|
|
136
|
+
if i == len(var_names) - 1:
|
|
137
|
+
# Last variable gets all remaining words
|
|
138
|
+
ctx.env[var] = " ".join(words[i:])
|
|
139
|
+
else:
|
|
140
|
+
ctx.env[var] = words[i]
|
|
141
|
+
else:
|
|
142
|
+
ctx.env[var] = ""
|
|
143
|
+
|
|
144
|
+
# Return success if we read something, failure if EOF
|
|
145
|
+
exit_code = 0 if stdin else 1
|
|
146
|
+
return ExecResult(stdout="", stderr="", exit_code=exit_code)
|
|
147
|
+
|
|
148
|
+
def _split_on_ifs(self, value: str, ifs: str) -> list[str]:
|
|
149
|
+
"""Split a string on IFS characters."""
|
|
150
|
+
if not value:
|
|
151
|
+
return []
|
|
152
|
+
|
|
153
|
+
# Identify IFS whitespace vs non-whitespace
|
|
154
|
+
ifs_whitespace = "".join(c for c in ifs if c in " \t\n")
|
|
155
|
+
|
|
156
|
+
# Simple split for whitespace-only IFS
|
|
157
|
+
if ifs == ifs_whitespace:
|
|
158
|
+
return value.split()
|
|
159
|
+
|
|
160
|
+
# Complex case with non-whitespace delimiters
|
|
161
|
+
result = []
|
|
162
|
+
current = []
|
|
163
|
+
i = 0
|
|
164
|
+
while i < len(value):
|
|
165
|
+
c = value[i]
|
|
166
|
+
if c in ifs_whitespace:
|
|
167
|
+
if current:
|
|
168
|
+
result.append("".join(current))
|
|
169
|
+
current = []
|
|
170
|
+
# Skip consecutive whitespace
|
|
171
|
+
while i < len(value) and value[i] in ifs_whitespace:
|
|
172
|
+
i += 1
|
|
173
|
+
elif c in ifs:
|
|
174
|
+
# Non-whitespace delimiter
|
|
175
|
+
result.append("".join(current))
|
|
176
|
+
current = []
|
|
177
|
+
i += 1
|
|
178
|
+
else:
|
|
179
|
+
current.append(c)
|
|
180
|
+
i += 1
|
|
181
|
+
|
|
182
|
+
if current:
|
|
183
|
+
result.append("".join(current))
|
|
184
|
+
|
|
185
|
+
return result
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Readlink command implementation."""
|
|
2
|
+
|
|
3
|
+
from ...types import CommandContext, ExecResult
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ReadlinkCommand:
|
|
7
|
+
"""The readlink command."""
|
|
8
|
+
|
|
9
|
+
name = "readlink"
|
|
10
|
+
|
|
11
|
+
async def execute(self, args: list[str], ctx: CommandContext) -> ExecResult:
|
|
12
|
+
"""Execute the readlink command."""
|
|
13
|
+
canonicalize = False
|
|
14
|
+
paths: list[str] = []
|
|
15
|
+
|
|
16
|
+
i = 0
|
|
17
|
+
while i < len(args):
|
|
18
|
+
arg = args[i]
|
|
19
|
+
if arg in ("-f", "--canonicalize", "-e", "--canonicalize-existing"):
|
|
20
|
+
canonicalize = True
|
|
21
|
+
elif arg == "--help":
|
|
22
|
+
return ExecResult(
|
|
23
|
+
stdout="Usage: readlink [OPTION]... FILE...\n",
|
|
24
|
+
stderr="",
|
|
25
|
+
exit_code=0,
|
|
26
|
+
)
|
|
27
|
+
elif arg == "--":
|
|
28
|
+
paths.extend(args[i + 1:])
|
|
29
|
+
break
|
|
30
|
+
elif arg.startswith("-") and len(arg) > 1:
|
|
31
|
+
return ExecResult(
|
|
32
|
+
stdout="",
|
|
33
|
+
stderr=f"readlink: invalid option -- '{arg[1]}'\n",
|
|
34
|
+
exit_code=1,
|
|
35
|
+
)
|
|
36
|
+
else:
|
|
37
|
+
paths.append(arg)
|
|
38
|
+
i += 1
|
|
39
|
+
|
|
40
|
+
if not paths:
|
|
41
|
+
return ExecResult(
|
|
42
|
+
stdout="",
|
|
43
|
+
stderr="readlink: missing operand\n",
|
|
44
|
+
exit_code=1,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
stdout_parts = []
|
|
48
|
+
stderr = ""
|
|
49
|
+
exit_code = 0
|
|
50
|
+
|
|
51
|
+
for path in paths:
|
|
52
|
+
try:
|
|
53
|
+
resolved = ctx.fs.resolve_path(ctx.cwd, path)
|
|
54
|
+
# Use lstat to not follow symlinks
|
|
55
|
+
stat = await ctx.fs.lstat(resolved)
|
|
56
|
+
|
|
57
|
+
if canonicalize:
|
|
58
|
+
# Return the canonical path
|
|
59
|
+
if stat.is_symbolic_link:
|
|
60
|
+
target = await ctx.fs.readlink(resolved)
|
|
61
|
+
if not target.startswith("/"):
|
|
62
|
+
import os
|
|
63
|
+
target = ctx.fs.resolve_path(os.path.dirname(resolved), target)
|
|
64
|
+
stdout_parts.append(target)
|
|
65
|
+
else:
|
|
66
|
+
stdout_parts.append(resolved)
|
|
67
|
+
else:
|
|
68
|
+
# Only works on symlinks
|
|
69
|
+
if stat.is_symbolic_link:
|
|
70
|
+
target = await ctx.fs.readlink(resolved)
|
|
71
|
+
stdout_parts.append(target)
|
|
72
|
+
else:
|
|
73
|
+
stderr += f"readlink: {path}: Not a symbolic link\n"
|
|
74
|
+
exit_code = 1
|
|
75
|
+
except FileNotFoundError:
|
|
76
|
+
stderr += f"readlink: {path}: No such file or directory\n"
|
|
77
|
+
exit_code = 1
|
|
78
|
+
except Exception as e:
|
|
79
|
+
stderr += f"readlink: {path}: {e}\n"
|
|
80
|
+
exit_code = 1
|
|
81
|
+
|
|
82
|
+
stdout = "\n".join(stdout_parts)
|
|
83
|
+
if stdout:
|
|
84
|
+
stdout += "\n"
|
|
85
|
+
|
|
86
|
+
return ExecResult(stdout=stdout, stderr=stderr, exit_code=exit_code)
|