I have tried to write mix task or even generate escript binary file, but it looks like that none of those ways supports some useful shell input methods. I tried to inspect arguments simply with IO.inspect(args)
here-document (<< operator)
A here document is a special-purpose code block. It uses a form of I/O redirection to feed a command list to an interactive program or a command, such as ftp, cat, or the ex text editor.
# Escript example:
$ ./example --test <<EOL
line 1
line 2
line 3
EOL
["--test"]
# Mix task example:
mix example.demo --test <<EOL
line 1
line 2
line 3
EOL
["--test"]
here-string (<<< operator)
A here string can be considered as a stripped-down form of a here document .
It consists of nothing more than COMMAND <<< $WORD ,
where $WORD is expanded and fed to the stdin of COMMAND .
Here-docs are handled by your shell. As NobbZ explained, the content of a here-doc is passed on the standard input stream to the program. Try this example:
$ elixir -e 'IO.puts IO.read(:stdio, :all)' <<EOL
> hey
> there
> EOL
hey
there
In my opinion expected behavior (for app - not for IO.read/2 ) here would be raise describing problem, but I’m not sure how I could make a check for using << operator … I do not even see an option for setting timeout in documentation (at least not for IO.read/2 which means it will collect input until user would terminate app, right?
Note: Of course I could use \n, but it’s extra requirement for end-user - not really problem for me.
My code snippet was meant to demonstrate how a here-document’s content is passed on the standard input to the program. I wasn’t proposing a solution because I don’t know the requirements of what you’re trying to achieve.
If you want your program to take user input which may span more than one line, you may want to read the input one line at a time and stop upon encountering an empty line. Alternatively, using IO.read(:stdio, :all) is also fine, in my opinion. It reads everything up to the end of input which can be signaled in several different ways.
When a program is invoked in a shell, users should know that they can signal the end of input by pressing Ctrl-D on an empty line. This is just a way to let the shell know there won’t be any more input from the user, so that it can close the input pipe connected to the running program at which point the running program will encounter the EOF/end-of-input if it’s reading from stdin.
The same outcome can be achieved by using shell pipes:
$ echo "This is my input." | elixir -e 'IO.puts IO.read(:stdio, :all)'
This is my input.
or by using shell’s stream redirection to read from a file:
$ echo "This is my input." >foo
$ elixir -e 'IO.puts IO.read(:stdio, :all)' <foo
This is my input.
If you’re still looking for help, then I’d like to ask you to rephrase your question because the way it’s currently formulated doesn’t explain what the actual problem you’re trying to solve is.
I was just looking if there is any way to pass multi line argument value instead of using \n or \ character at end of each line which are not handy and requires to pass/edit data manually. I have found such here-* syntactic sugar in BASH
./example --test << EOF
multi line content goes here …
EOF
Notice that here we have only prefix --test << EOF and suffix EOF for such value i.e. value is not changed and it could be just copied. Single line have only prefix (--test), but it’s much easier that other solutions. Simple example of use case here is multi line JSON parsing, so end-user could use such option by simply copying JSON data - without any need to replace new line with \n or adding \ at end of each data line (just imagine huge prettified JSON).
Ahh, really sorry - just reminded something. With "…" notation you can’t copy and paste strings with not escaped " character. I was interested in << EOL … EOL syntactic sugar, because you can set prefix and suffix on yourself.
The user has to choose the appropriate quotes anyway. If they don’t use bash but eg fish the shell might behave different from your documentation/examples. Therefore it’s best to only document the meaning of the named/positional argument in an executable.
I see. So the question is not about Elixir at all, it’s about passing multiple lines as a single positional argument to a program when running it in a shell.
The common practice is to use quotes like NobbZ has suggested. You could use shell substitution but that would look rather weird:
$ elixir -e "IO.inspect System.argv" -- --test $(cat <<EOL
bar
baz)
EOL
)
["--test", "bar", "baz)"]
# Must use quotes around the shell substitution
$ elixir -e "IO.inspect System.argv" -- --test "$(cat <<EOL
"bar"
baz)
EOL
)"
["--test", "\"bar\"\nbaz)"]
It’s definitely best answer! With it everything could be passed and automatically escaped without any worry. End-user could decide when to stop adding text pretty easy and there is no need to change code.
I just wonder why it’s not possible to do it directly (not inside $() - command substitution) like cat could.
Yeah, but it also does not freezes when there is nothing on stdin, right? I do not see any equivalent in Elixir documentation (maybe I have missed something). I though about doing it in with Task timeout, but then I was not sure what timeout should I use (assuming that you don’t know how much data is in stdin), so everything goes to point where I would like to check
It will wait for input until it reads EOF. Try cat as a single command without anything else. It will not exit, but wait for you to provide input on stdin.
You will never know if stdin was empty or not before you received an end of file, and after that you can check for emptyness by simply counting bytes. That works in any language.