35

Related to a question I asked on Stack Overflow: Passing multiple lines of code to wsadmin.sh?.

I have, as a string, a few lines of input that I need to feed into a command. The command, however, only accepts a file full of input.

Is there some way I could store the lines in a temporary file or pipe which never actually gets written to disk and then feed that directly into the program?

How do I go about acquiring the pipe, feeding the input into it, passing that pipe to the command, and then getting rid of the pipe?

  • 2
    Can you add an example of the command you're using, and how it's supposed to be invoked? – Breakthrough Jul 13 '15 at 7:27
  • 2
    Does that mean the program does not accept stdin? – Léo Lam Jul 13 '15 at 11:47
  • I agree with both commenters above. It's impossible to suggest anything that may work without knowing the program's specs. – Larssend Jul 13 '15 at 13:04
32

Another solution is to use Process Substitution in Bash. If your system has named pipes, you can use this feature.

It is used like this:

program_expecting_a_file <(list)

where list is a sequence of shell commands (actually pipelines; the full details are here). list will be executed, and its output will be connected to the pipe. The name of the pipe is then passed to your program.

NOTE: spaces are not allowed between < and (.

(The other substitution >(list) also works as you'd expect.)

In your specific case, you could use

program_expecting_a_file <(echo "$your_strings")

although you may find @meuh's solution to be more elegant.

Update: A great many usage examples are to be found over at the Advanced Bash Scripting Guide.

| improve this answer | |
  • The other two did not work in even the most trivial of cases. Yours passes the tests I've thrown at it so far. I have upvoted yours for now. I will come back and mark this as accepted if it does everything I need. – ArtOfWarfare Jul 13 '15 at 14:29
  • Yours works perfectly. If you'd like, you can add it to the related question on StackOverflow and I can accept it over there, too. stackoverflow.com/questions/31374202/… – ArtOfWarfare Jul 13 '15 at 15:22
  • Hi @ArtOfWarfare, glad to be of help. There is a comment over there already with the same proposed solution, so it wouldn't be right for me to post an answer there. – Edward Jul 14 '15 at 8:50
  • You posted your solution before he did. Anyways, I've asked him to change his comment to an answer so I can accept it. If neither of you post answers within ~24 hours on that question, I'll just post the answer myself so I can accept it (I don't like leaving my questions as appearing unanswered once I've found what the answer is. It's just bad manners for future readers / potential answerers of the question.) – ArtOfWarfare Jul 14 '15 at 12:07
  • Hi @ArtOfWarfare, I'm absolutely fine with what you propose! – Edward Jul 15 '15 at 7:47
17

You can use a bash here-doc. For example,

$ cat -n <<<'a
> b'
 1  a
 2  b

If you have something in a bash variable you can also interpolate it: eg <<<"path is $PATH".


If your command insists on a filename you can give it /dev/stdin. Eg:

$ sed -f /dev/stdin <<<'s/a/b/'

produces the output: s/b/b/.

| improve this answer | |
  • It's not clear from the question whether the command accepts input on stdin, and if not, this isn't going to work. I hope the OP clarifies. – David Z Jul 13 '15 at 12:34
  • 1
    :( - This seemed super promising, but when I tried it, I got the error message "-f" option must be followed by a file name. – ArtOfWarfare Jul 13 '15 at 14:19
  • A here-doc is for stdin. If your command absolutely needs -f and a filename, then provide the filename /dev/stdin. See my edit. – meuh Jul 13 '15 at 14:46
  • I think after the edits it's absolutely a viable option! – Edward Jul 14 '15 at 8:54
  • 1
    +1 for the /dev/stdin suggestion. The program I'm trying to call only accepts a filename, so I've combined /dev/stdin with a heredoc. – Huw Walters Jan 15 '19 at 14:41
7

Depending on what is in the script, you may be able to use the special filename - (minus) which stands for stdin

$ mycmd -
Line 1
Line 2
^D

Another method which may work for you is to open the file /dev/fd/0 which again stands for stdin.

A third option may be to create a FIFO (First In First Out). That is like a file, but it has two ends to it - you write to one end and read from the other.

-- Process 1 --                  -- Process 2 --
$ mkfifo foo
$ cat foo
<waits>                          $ echo hello > foo
hello                            $
$ rm foo
| improve this answer | |
  • 3
    It may be worth noting that the program has to be written to interpret - this way - it's not a feature of the shell or the operating system. Though of course most standard Linux utilities do follow this convention, as far as I know. – David Z Jul 13 '15 at 12:32
  • 1
    Nope. This does not work for the program. There is an interactive mode, but I want to pass the commands to execute to this program from another program, and I don't want to get into rabbit-hole that is expect. – ArtOfWarfare Jul 13 '15 at 14:16
  • @ArtOfWarfare I have just added a third option that may work. – Majenko Jul 13 '15 at 15:06
  • Nice answer! The second & third options are basically like the <() solution, but more explicit - which may be very useful! – Edward Jul 14 '15 at 8:53
4

There's another option similar to the mycmd - solution, but even for programs which don't handle - specifically:

$ mycmd /dev/stdin
Line 1
Line 2
^D

/dev/stdin seems to be OS-related and it works for me on Debian GNU/Linux, but I'm not sure where else it will work, too.

| improve this answer | |
  • 1
    This seems like it might work, but what is ^D? – ArtOfWarfare Jul 13 '15 at 17:04
  • ^D is a common notation for pressing Ctrl-D. ^D has the semantics of "end of input stream" here. It's probably more often seen in ^C or ^Z which stand for pressing Ctrl-C respectively for pressing Ctrl-Z. – Axel Beckert Jul 13 '15 at 18:04
  • 1
    That does seem to work, but how would I programmatically put stuff into /dev/stdin and then close it? – ArtOfWarfare Jul 13 '15 at 19:15
  • 1
    Just pipe something into that command and it should work, i.e. cat content | mycmd /dev/stdin. Just imagine different commands than cat here. – Axel Beckert Jul 13 '15 at 19:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.