Add $Shell.execute()
This commit is contained in:
parent
928f3250b3
commit
9a62f8d6a6
117
docs/shell.md
Normal file
117
docs/shell.md
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
# Shell Scripting
|
||||||
|
|
||||||
|
Tomo comes with a built-in [lang](langs.md) called `Shell` for shell commands.
|
||||||
|
This lets you write and invoke shell commands in a more type-safe way.
|
||||||
|
|
||||||
|
```tomo
|
||||||
|
user_name := ask("What's your name? ")
|
||||||
|
_ := $Shell"echo Hello $user_name"
|
||||||
|
```
|
||||||
|
|
||||||
|
In the above example, there is no risk of code injection, because the
|
||||||
|
user-controlled string is automatically escaped when performing interpolation.
|
||||||
|
|
||||||
|
## Shell Methods
|
||||||
|
|
||||||
|
### `by_line`
|
||||||
|
|
||||||
|
**Description:**
|
||||||
|
Run a shell command and return an iterator over its output, line-by-line.
|
||||||
|
|
||||||
|
**Signature:**
|
||||||
|
```tomo
|
||||||
|
func by_line(command: Shell -> Void)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `command`: The command to run.
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
An optional iterator over the lines of the command's output. If the command fails
|
||||||
|
to run, `none` will be returned.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```tomo
|
||||||
|
i := 1
|
||||||
|
for line in $Shell"ping www.example.com":by_line()!:
|
||||||
|
stop if i > 5
|
||||||
|
i += 1
|
||||||
|
```
|
||||||
|
|
||||||
|
### `execute`
|
||||||
|
|
||||||
|
**Description:**
|
||||||
|
Execute a shell command without capturing its output and return its exit status.
|
||||||
|
|
||||||
|
**Signature:**
|
||||||
|
```tomo
|
||||||
|
func execute(command: Shell -> Int32?)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `command`: The command to execute.
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
If the command exits normally, return its exit status. Otherwise return `none`.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```tomo
|
||||||
|
>> $Shell"touch file.txt":execute()
|
||||||
|
= 0?
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `run`
|
||||||
|
|
||||||
|
**Description:**
|
||||||
|
Run a shell command and return the output text from `stdout`.
|
||||||
|
|
||||||
|
**Signature:**
|
||||||
|
```tomo
|
||||||
|
func run(command: Shell -> Text?)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `command`: The command to run.
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
If the program fails to run (e.g. a non-existent command), return `none`,
|
||||||
|
otherwise return the entire standard output of the command as text. **Note:**
|
||||||
|
if there is a trailing newline, it will be stripped.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```tomo
|
||||||
|
>> $Shell"seq 5":run()
|
||||||
|
= "1$\n2$\n3$\n4$\n5"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### `run_bytes`
|
||||||
|
|
||||||
|
**Description:**
|
||||||
|
Run a shell command and return the output in raw bytes from `stdout`.
|
||||||
|
|
||||||
|
**Signature:**
|
||||||
|
```tomo
|
||||||
|
func run(command: Shell -> [Byte]?)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
|
||||||
|
- `command`: The command to run.
|
||||||
|
|
||||||
|
**Returns:**
|
||||||
|
If the program fails to run (e.g. a non-existent command), return `none`,
|
||||||
|
otherwise return the entire standard output of the command as an array of
|
||||||
|
bytes.
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```tomo
|
||||||
|
>> $Shell"seq 5":run_bytes()
|
||||||
|
= [0x31, 0x0A, 0x32, 0x0A, 0x33, 0x0A, 0x34, 0x0A, 0x35, 0x0A]
|
||||||
|
```
|
@ -380,8 +380,8 @@ env_t *new_compilation_unit(CORD libname)
|
|||||||
{"escape_int", "Int$value_as_text", "func(i:Int -> Shell)"},
|
{"escape_int", "Int$value_as_text", "func(i:Int -> Shell)"},
|
||||||
{"escape_text", "Shell$escape_text", "func(text:Text -> Shell)"},
|
{"escape_text", "Shell$escape_text", "func(text:Text -> Shell)"},
|
||||||
{"escape_text_array", "Shell$escape_text_array", "func(texts:[Text] -> Shell)"},
|
{"escape_text_array", "Shell$escape_text_array", "func(texts:[Text] -> Shell)"},
|
||||||
{"execute", "Shell$execute", "func(command:Shell -> Int32)"},
|
{"execute", "Shell$execute", "func(command:Shell -> Int32?)"},
|
||||||
{"run_bytes", "Shell$run", "func(command:Shell -> [Byte]?)"},
|
{"run_bytes", "Shell$run_bytes", "func(command:Shell -> [Byte]?)"},
|
||||||
{"run", "Shell$run", "func(command:Shell -> Text?)"},
|
{"run", "Shell$run", "func(command:Shell -> Text?)"},
|
||||||
)},
|
)},
|
||||||
{"Text", TEXT_TYPE, "Text_t", "Text$info", TypedArray(ns_entry_t,
|
{"Text", TEXT_TYPE, "Text_t", "Text$info", TypedArray(ns_entry_t,
|
||||||
|
@ -50,7 +50,7 @@ public OptionalArray_t Shell$run_bytes(Shell_t command)
|
|||||||
if (len + (size_t)just_read >= capacity)
|
if (len + (size_t)just_read >= capacity)
|
||||||
content = GC_REALLOC(content, (capacity *= 2));
|
content = GC_REALLOC(content, (capacity *= 2));
|
||||||
|
|
||||||
memcpy(&content[len], chunk, (size_t)just_read);
|
memcpy(content + len, chunk, (size_t)just_read);
|
||||||
len += (size_t)just_read;
|
len += (size_t)just_read;
|
||||||
} while (just_read == sizeof(chunk));
|
} while (just_read == sizeof(chunk));
|
||||||
|
|
||||||
@ -75,10 +75,14 @@ public OptionalText_t Shell$run(Shell_t command)
|
|||||||
return Text$from_bytes(bytes);
|
return Text$from_bytes(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int32_t Shell$execute(Shell_t command)
|
public OptionalInt32_t Shell$execute(Shell_t command)
|
||||||
{
|
{
|
||||||
const char *cmd_str = Text$as_c_string(command);
|
const char *cmd_str = Text$as_c_string(command);
|
||||||
return system(cmd_str);
|
int status = system(cmd_str);
|
||||||
|
if (WIFEXITED(status))
|
||||||
|
return (OptionalInt32_t){.i=WEXITSTATUS(status)};
|
||||||
|
else
|
||||||
|
return (OptionalInt32_t){.is_none=true};
|
||||||
}
|
}
|
||||||
|
|
||||||
static void _line_reader_cleanup(FILE **f)
|
static void _line_reader_cleanup(FILE **f)
|
||||||
|
@ -21,7 +21,7 @@ Shell_t Shell$escape_text(Text_t text);
|
|||||||
Shell_t Shell$escape_text_array(Array_t texts);
|
Shell_t Shell$escape_text_array(Array_t texts);
|
||||||
OptionalArray_t Shell$run_bytes(Shell_t command);
|
OptionalArray_t Shell$run_bytes(Shell_t command);
|
||||||
OptionalText_t Shell$run(Shell_t command);
|
OptionalText_t Shell$run(Shell_t command);
|
||||||
int32_t Shell$execute(Shell_t command);
|
OptionalInt32_t Shell$execute(Shell_t command);
|
||||||
|
|
||||||
#define Shell$hash Text$hash
|
#define Shell$hash Text$hash
|
||||||
#define Shell$compare Text$compare
|
#define Shell$compare Text$compare
|
||||||
|
Loading…
Reference in New Issue
Block a user