-
Notifications
You must be signed in to change notification settings - Fork 1
/
README.Rmd
210 lines (160 loc) · 6.17 KB
/
README.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
---
title: "cmdfun"
output: github_document
---
<!-- badges: start -->
[![Lifecycle: maturing](https://img.shields.io/badge/lifecycle-maturing-blue.svg)](https://www.tidyverse.org/lifecycle/#maturing)
[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)
[![R build status](https://github.com/snystrom/cmdfun/workflows/R-CMD-check/badge.svg)](https://github.com/snystrom/cmdfun/actions)
[![Codecov test coverage](https://codecov.io/gh/snystrom/cmdfun/branch/master/graph/badge.svg)](https://codecov.io/gh/snystrom/cmdfun?branch=master)
<!-- badges: end -->
## A simple framework for building shell interfaces
The purpose of `cmdfun` is to significantly reduce the overhead involved in
wrapping shell programs in R. The tools are intended to be intuitive and
lightweight enough to use for data scientists trying to get things done quickly,
but robust and full-fledged enough for developers to extend them to more
advanced use cases.
## Installation
Install the development version of `cmdfun` with:
```{r, eval=F}
if (!requireNamespace("remotes"))
install.packages("remotes")
remotes::install_github("snystrom/cmdfun")
```
## Quick Overview
The `cmdfun` framework provides mechanisms for capturing function arguments:
- `cmd_args_dots()` captures all arguments passed to `...`
- `cmd_args_named()` captures all keyword arguments defined by the user
- `cmd_args_all()` captures both named + dot arguments
```{r}
library(cmdfun)
myFunction <- function(input, ...){
cmd_args_all()
}
(argsList <- myFunction(input = "test", boolean_flag = TRUE))
```
`cmd_list_interp` converts the captured argument list to a parsed list of
flag/value pairs.
```{r}
(flagsList <- cmd_list_interp(argsList))
```
`cmd_list_to_flags` converts a list to a vector of
commandline-style flags using the list names as flag names and the list values
as the flag values (empty values return only the flag). This output can be
directly fed to `system2` or `processx`.
```{r}
cmd_list_to_flags(flagsList)
```
`cmd_path_search()` allows package builders to search default locations for installed tools.
```{r}
bin_path <- cmd_path_search(default_path = "/bin", utils = c("ls", "cut"))
bin_path(util = "ls")
```
## Introduction
`cmdfun` attempts to solve the problem of wrapping external software in R.
Calling external software is done with `system2` or `processx`.
For example, calling `ls -l *.md` using `system2`.
```{r}
system2("ls", "-l *.md", stdout = TRUE)
```
However, when using multiple commandline flags each flag should be passed as a
member of a character vector as follows:
When calling `ls -l -i`
```{r}
system2("ls", c("-l", "-i", "*.md"), stdout = TRUE)
```
This becomes even more difficult if trying to support user input, as a
significant amount of overhead is required to parse user inputs and optional
flags into these vectors.
`cmdfun` provides utilities for converting **function arguments** into **lists**
which can easily convert to **character vectors** suitable for use with
`system2` or `processx`.
```{r}
library(cmdfun)
myFunction <- function(input, option1){
# Grabs named arguments as key/value pairs
cmd_args_named()
}
(argsList <- myFunction("myInput.txt", "foo"))
```
```{r}
# Converts list to character vector of flags & values
cmd_list_to_flags(argsList)
```
### Wrapping `ls` with cmdfun
These tools can be used to easily wrap `ls`
```{r}
library(magrittr)
shell_ls <- function(dir = ".", ...){
# grab arguments passed to "..." in a list
flags <- cmd_args_dots() %>%
# prepare list for conversion to vector
cmd_list_interp() %>%
# Convert the list to a flag vector
cmd_list_to_flags()
# Run ls shell command
system2("ls", c(flags, dir), stdout = TRUE)
}
```
```{r}
shell_ls("*.md")
```
#### Boolean flags are passed as bool operators
`ls -l` can be mimicked by passing `l = TRUE` to '...'.
```{r}
shell_ls("*.md", l = TRUE)
```
### Named vectors can be used to provide user-friendly aliases for single-letter flags
Commandline tools can have hundreds of arguments, many with uninformative, often
single-letter, names. To prevent developers from having to write aliased
function arguments for all, often conflicting flags, `cmd_list_interp` can
additionally use a lookup table to allow developers to provide informative
function argument names for unintuitive flags.
For example, allowing `long` to act as `-l` in `ls`.
```{r}
shell_ls_alias <- function(dir = ".", ...){
# Named vector acts as lookup table
# name = function argument
# value = flag name
names_arg_to_flag <- c("long" = "l")
flags <- cmd_args_dots() %>%
# Use lookup table to manage renames
cmd_list_interp(names_arg_to_flag) %>%
cmd_list_to_flags()
system2("ls", c(flags, dir), stdout = TRUE)
}
```
```{r}
shell_ls_alias("*.md", long = TRUE)
```
### Wrapping `cut` with cmdfun
Here is another example wrapping `cut` which separates text on a delimiter (set
with `-d`) and returns selected fields (set with `-f`) from the separation.
```{r}
shell_cut <- function(text, ...){
names_arg_to_flag <- c("sep" = "d",
"fields" = "f")
flags <- cmd_args_dots() %>%
cmd_list_interp(names_arg_to_flag) %>%
cmd_list_to_flags()
system2("cut", flags, stdout = T, input = text)
}
```
```{r}
shell_cut("hello_world", fields = 2, sep = "_")
```
#### Multiple values are passed as vectors
```{r}
shell_cut("hello_world_hello", fields = c(1,3), sep = "_")
```
Additionally, `cmdfun` provides utilites for searching & checking valid tool
installs, expecting system behavior, and helpful error handling to allow simple
construction of external tool wrappers (see
[vignette](https://snystrom.github.io/cmdfun/articles/cmdfun.html) for details).
## More Details
See [https://snystrom.github.io/cmdfun/articles/cmdfun.html](https://snystrom.github.io/cmdfun/articles/cmdfun.html)
for the most recent documentation and to learn about all `cmdfun` features.
To file bug reports, please visit
[https://github.com/snystrom/cmdfun/issues](https://github.com/snystrom/cmdfun/issues)
while providing a [reproducible example](https://reprex.tidyverse.org/) of your
issue.