I want to create my own pipeline like in Unix terminal (just to practice). It should take applications to execute in quotes like that:
pipeline "ls -l" "grep" ....
I know that I should use fork(), execl() (exec*) and API to redirect stdin and stdout. But are there any alternatives for execl to execute app with arguments using just one argument which includes application path and arguments? Is there a way not to parse manually ls -l but pass it as one argument to execl?
If you have only a single command line instead of an argument vector, let the shell do the parsing for you:
execl("/bin/sh", "sh", "-c", the_command_line, NULL);
Of course, don't let untrusted remote user input into this command line. But if you are dealing with untrusted remote user input to begin with, you should try to arrange to pass actual a list of isolated arguments to the target application as per normal usage of exec[vl], not a command line.
Realistically, you can only really use execl() when the number of arguments to the command are known at compile time. In a shell, you'll normally use execv() or execvp() instead; these can handle an arbitrary number of arguments to the command to be executed. In theory, you use execv() when the path name of the command is given and execvp() (which does a PATH-based search for the command) when it isn't. However, execvp() handles the 'path given' case, so simply use execvp().
So, for your pipeline command, you'll end up with one child using something equivalent to:
char *args_1[] = { "ls", "-l", 0 };
execvp(args_1[0], args_1);
The other child will end up using something equivalent to:
char *args_2[] = { "grep", "pattern", 0 };
execvp(args_2[0], args_2);
Except, of course, that you'll have created those strings from the command line arguments instead of by initialization as shown. Note that grep requires a pattern to search for.
You've still got plumbing issues to resolve. Make sure you close enough pipe file descriptors. When you dup() or dup2() a pipe to standard input or standard output, you close both the file descriptors from the pipe() function.
Related
I am writing a zsh completion function for a shell script/program I wrote. At some point in the completion process I want to use the completion function of another command to handle the rest of the completion.
How can I find out what the completion function for a specific command is called? How can I look up the existing compdef assignments in my shell?
Background
My program wraps nvim and there is no _nvim function in my shell which I would have guessed would be the completion function. So I assume the completion function that nvim uses is actually _vim but this question is more general in order to learn.
Type the command plus a space and then press CtrlX followed by H to the run the _complete_help widget. For example:
% nvim # press ^Xh here
tags in context :completion::complete:nvim::
argument-rest options (_arguments _vim)
tags in context :completion::complete:nvim:argument-rest:
globbed-files (_files _vim_files _arguments _vim)
This tells you that for arguments to command nvim , the completion system will call _vim, which then calls _arguments, which adds argument-rest and options completions. Then, _arguments calls _vim_files, which calls _files, which adds globbed-files completions.
Alternatively, if you're interested in only the top-level completion function set for a particular command, do this:
% print $_comps[nvim]
_vim
Is there a way to access all the variables/arguments passed through the command line or variable file (-V option) during robotframework execution. I know in python the execution can access it with 'sys.args' feature.
The answer for getting the CLI arguments is inside your question - just look at the content of the sys.argv, you'll see everything that was passed to the executor:
${args}= Evaluate sys.argv sys
Log To Console ${args}
That'll return a list, where the executable itself (run.py) is the 1st member, and all arguments and their values present the in the order given during the execution:
['C:/my_directories/rf-venv/Lib/site-packages/robot/run.py', '--outputdir', 'logs', '--variable', 'USE_BROWSERSTACK:true', '--variable', 'IS_DEV_ENVIRONMENT:false', '--include', 'worky', 'suites\\test_file.robot']
You explicitly mention variable files; that one is a little bit trickier - the framework parses the files itself, and creates the variables according to its rules. You naturally can see them in the CLI args up there, and the other possibility is to use the built-in keyword Get Variables, which "Returns a dictionary containing all variables in the current scope." (quote from its documentation). Have in mind though that these are all variables - not only the passed on the command line, but also the ones defined in the suite/imported keywords etc.
You have Log Variables to see their names and values "at current scope".
There is no possibility to see the arguments passed to robot.
I am trying to create a FUSE filesystem called ordered-dirs using the Haskell wrapper over libfuse, HFuse. This filesystem is a "derived filesystem", i.e. it takes an existing directory (the "base directory") and produces a different view of it.
However, when I try to run my FUSE filesystem program, specifying the arguments in the ordinary mount way, I get an error:
$ ordered-dirs /home/robin/tasks/ /home/robin/to
fuse: invalid argument `/home/robin/to'
There is no way in HFuse (or in libfuse, it seems) to get the base directory (the first argument), so I had just written my own code to get it. But it's not this code that's failing - it's code within C libfuse itself - as the error message indicates.
So what is the correct way to pass the base directory to a fuse filesystem executable that uses libfuse to parse its arguments?
Surprisingly, it seems that the way to do this is to simply strip the base directory argument from the command-line arguments that are parsed to the libfuse parser, so that libfuse never sees it.
In the particular case of HFuse, this can be done by calling fuseRun instead, which allows the command-line arguments to be passed in explicitly. You can see how I've done this here - here is the relevant code (in which I've called the base directory source):
main :: IO ()
main = do
args <- getArgs
let (maybeSource, remainder) = extractSource args
source <- maybe (fail "source not specified") return maybeSource
fuseRun "ordered-dirs" remainder (orderedDirOps source) defaultExceptionHandler
I have tried
let _ = Unix.create_process "ls" [||] Unix.stdin Unix.stdout Unix.stderr
in utop, it will crash the whole thing.
If I write that into a .ml and compile and run, it will crash the terminal and my ubuntu will throw a system error.
But why?
The right way to call it is:
let pid = Unix.create_process "ls" [|"ls"|] Unix.stdin Unix.stdout Unix.stderr
The first element of the array must be the "command" name.
On some systems /bin/ls is a link to some bigger executable that will look at argv.(0) to know how to behave (c.f. Busybox); so you really need to provide that info.
(You see more often that with /usr/bin/vi which is now on many systems a sym-link to vim).
Unix.create_process actually calls fork and the does an execvpe, which itself calls the execv primitive (in the OCaml C implementation of the Unix module).
That function then calls cstringvect (a helper function in the C side of the module implementation), which translates the arg parameters into an array of C string, with last entry set to NULL. However, execve and the like expect by convention (see the execve(2) linux man page) the first entry of that array to be the name of the program:
argv is an array of argument strings passed to the new program. By
convention, the first of these strings should contain the filename
associated with the file being executed.
That first entry (or rather, the copy it receives) can actually be changed by the program receiving these args, and is displayed by ls, top, etc.
I'm trying to set an array in ZSH (configured using oh-my-zsh).
export AR=(localhost:1919 localhost:1918)
but I'm getting an error like such:
zsh: number expected
If I don't add the export command, it's just fine. I'm not typing the above in a *rc file, just in the zsh prompt. What could be the problem?
You can't export an array in zsh.
For more info: http://zsh.sourceforge.net/Guide/zshguide02.html
Note that you can't export arrays. If you export a parameter, then
assign an array to it, nothing will appear in the environment; you can
use the external command printenv VARNAME (again no $ because the
command needs to know the name, not the value) to check. There's a
more subtle problem with arrays, too. The export builtin is just a
special case of the builtin typeset, which defines a variable without
marking it for export to the environment. You might think you could do
typeset array=(this doesn\'t work)
but you can't --- the special
array syntax is only understood when the assignment does not follow a
command, not in normal arguments like the case here, so you have to
put the array assignment on the next line. This is a very easy mistake
to make. More uses of typeset will be described in chapter 3; they
include creating local parameters in functions, and defining special
attributes (of which the export attribute is just one) for
parameters.