The purpose ofcmdfun is to significantly reduce theoverhead involved in wrapping shell programs in R. The tools areintended to be intuitive and lightweight enough to use for datascientists trying to get things done quickly, but robust andfull-fledged enough for developers to extend them to more advanced usecases.
Install the development version ofcmdfun with:
if (!requireNamespace("remotes"))install.packages("remotes")remotes::install_github("snystrom/cmdfun")Thecmdfun framework provides mechanisms for capturingfunction arguments:
cmd_args_dots() captures all arguments passed to...cmd_args_named() captures all keyword arguments definedby the usercmd_args_all() captures both named + dot argumentslibrary(cmdfun)myFunction<-function(input, ...){cmd_args_all()}(argsList<-myFunction(input ="test",boolean_flag =TRUE))## $input## [1] "test"## ## $boolean_flag## [1] TRUEcmd_list_interp converts the captured argument list to aparsed list of flag/value pairs.
(flagsList<-cmd_list_interp(argsList))## $input## [1] "test"## ## $boolean_flag## [1] ""cmd_list_to_flags converts a list to a vector ofcommandline-style flags using the list names as flag names and the listvalues as the flag values (empty values return only the flag). Thisoutput can be directly fed tosystem2 orprocessx.
cmd_list_to_flags(flagsList)## [1] "-input" "test" "-boolean_flag"cmd_path_search() allows package builders to searchdefault locations for installed tools.
bin_path<-cmd_path_search(default_path ="/bin",utils =c("ls","cut"))bin_path(util ="ls")## [1] "//bin/ls"cmdfun attempts to solve the problem of wrappingexternal software in R. Calling external software is done withsystem2 orprocessx.
For example, callingls -l *.md usingsystem2.
system2("ls","-l *.md",stdout =TRUE)## [1] "-rw-r--r-- 1 snystrom its_employee_psx 1077 Aug 20 19:20 LICENSE.md" ## [2] "-rw-r--r-- 1 snystrom its_employee_psx 837 Aug 26 21:51 NEWS.md" ## [3] "-rw-r--r-- 1 snystrom its_employee_psx 7718 Aug 26 20:14 README.md" ## [4] "-rw-r--r-- 1 snystrom its_employee_psx 744 Aug 26 21:35 cran-comments.md"However, when using multiple commandline flags each flag should bepassed as a member of a character vector as follows:
When callingls -l -i
system2("ls",c("-l","-i","*.md"),stdout =TRUE)## [1] "1163031755 -rw-r--r-- 1 snystrom its_employee_psx 1077 Aug 20 19:20 LICENSE.md" ## [2] "1163031757 -rw-r--r-- 1 snystrom its_employee_psx 837 Aug 26 21:51 NEWS.md" ## [3] "1163031758 -rw-r--r-- 1 snystrom its_employee_psx 7718 Aug 26 20:14 README.md" ## [4] "1163031762 -rw-r--r-- 1 snystrom its_employee_psx 744 Aug 26 21:35 cran-comments.md"This becomes even more difficult if trying to support user input, asa significant amount of overhead is required to parse user inputs andoptional flags into these vectors.
cmdfun provides utilities for convertingfunction arguments intolists whichcan easily convert tocharacter vectors suitable foruse withsystem2 orprocessx.
library(cmdfun)myFunction<-function(input, option1){# Grabs named arguments as key/value pairscmd_args_named()}(argsList<-myFunction("myInput.txt","foo"))## $input## [1] "myInput.txt"## ## $option1## [1] "foo"# Converts list to character vector of flags & valuescmd_list_to_flags(argsList)## [1] "-input" "myInput.txt" "-option1" "foo"ls withcmdfunThese tools can be used to easily wrapls
library(magrittr)shell_ls<-function(dir =".", ...){# grab arguments passed to "..." in a list flags<-cmd_args_dots()%>%# prepare list for conversion to vectorcmd_list_interp()%>%# Convert the list to a flag vectorcmd_list_to_flags()# Run ls shell commandsystem2("ls",c(flags, dir),stdout =TRUE)}shell_ls("*.md")## [1] "LICENSE.md" "NEWS.md" "README.md" "cran-comments.md"ls -l can be mimicked by passingl = TRUEto ‘…’.
shell_ls("*.md",l =TRUE)## [1] "-rw-r--r-- 1 snystrom its_employee_psx 1077 Aug 20 19:20 LICENSE.md" ## [2] "-rw-r--r-- 1 snystrom its_employee_psx 837 Aug 26 21:51 NEWS.md" ## [3] "-rw-r--r-- 1 snystrom its_employee_psx 7718 Aug 26 20:14 README.md" ## [4] "-rw-r--r-- 1 snystrom its_employee_psx 744 Aug 26 21:35 cran-comments.md"Commandline tools can have hundreds of arguments, many withuninformative, often single-letter, names. To prevent developers fromhaving to write aliased function arguments for all, often conflictingflags,cmd_list_interp can additionally use a lookup tableto allow developers to provide informative function argument names forunintuitive flags.
For example, allowinglong to act as-l inls.
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 renamescmd_list_interp(names_arg_to_flag)%>%cmd_list_to_flags()system2("ls",c(flags, dir),stdout =TRUE)}shell_ls_alias("*.md",long =TRUE)## [1] "-rw-r--r-- 1 snystrom its_employee_psx 1077 Aug 20 19:20 LICENSE.md" ## [2] "-rw-r--r-- 1 snystrom its_employee_psx 837 Aug 26 21:51 NEWS.md" ## [3] "-rw-r--r-- 1 snystrom its_employee_psx 7718 Aug 26 20:14 README.md" ## [4] "-rw-r--r-- 1 snystrom its_employee_psx 744 Aug 26 21:35 cran-comments.md"cut withcmdfunHere is another example wrappingcut which separatestext on a delimiter (set with-d) and returns selectedfields (set with-f) from the separation.
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)}shell_cut("hello_world",fields =2,sep ="_")## [1] "world"shell_cut("hello_world_hello",fields =c(1,3),sep ="_")## [1] "hello_hello"Additionally,cmdfun provides utilites for searching& checking valid tool installs, expecting system behavior, andhelpful error handling to allow simple construction of external toolwrappers (seevignettefor details).
Seehttps://snystrom.github.io/cmdfun/articles/cmdfun.htmlfor the most recent documentation and to learn about allcmdfun features.
To file bug reports, please visithttps://github.com/snystrom/cmdfun/issues whileproviding areproducibleexample of your issue.