22

How can I send characters to a command as though they came from a file?

For example I tried:

wc < "apple pear orange"
-bash: apple pear orange: No such file or directory
32

In shells that support here strings, including bash, zsh and ksh93, you can use

wc <<< "apple pear orange"
  • @Kusalananda thanks - I edited your info in – steeldriver Jan 22 '17 at 22:28
18

Two other approaches (which allow multiple-line input with no extra effort):

  1. Use a "here document":

    $ wc << EOF
    apple pear orange
    EOF
      1       3      18
    $

    The EOF string is a delimiter.  You can use any string; EOF is just a conventional choice.

  2. Use the tty as the input:

    $ wc
    apple pear orange
    Ctrl+D
      1       3      18
    $

    This has the drawback that the program starts running, and starts reading the input, as soon as you type its name.  This can be disconcerting; for example:

    $ grep v
    The quick brown fox             (typed)
    jumps over                      (typed)
    jumps over                      (This is output from grep!)
    the lazy dog.                   (typed)
    Ctrl+D
                                    (No output here)
    $
  • For the record: The <<< form also allows multiple-line input with no extra effort, since the "-enclosed string can contain newlines. Of course the << EOF form (the original here-doc syntax) is easier to read if you have multi-line input. – alexis Jan 23 '17 at 9:47
  • The man page says that here string syntax is <<< word — of course, in the context of the shell, a word can be a quoted string, containing spaces and newlines! D’oh! That’s so obvious that it goes without saying (and, in fact, I don’t see it mentioned in the man page at all).      :-(      Thanks for pointing this out to me! – G-Man Says 'Reinstate Monica' Jan 31 '17 at 22:22
  • I wouldn't call it simple or obvious, really. A word is defined in the manpage as "A sequence of characters considered as a single unit by the shell" (aka "token"), and you need to know that quoted strings are treated as "a single unit" in the relevant sense (after backslash processing, variable expansion etc." But indeed that's the whole purpose of double-quoting in the shell. (Single quotes also protect from expansion.) The shell's processing model is very well thought out, and anything but simple. – alexis Feb 5 '17 at 9:14
  • @alexis:  When I go over the top like that and include an emoticon, one should consider the possibility that I’m being ironic. – G-Man Says 'Reinstate Monica' Feb 5 '17 at 23:35
10

Although there are several valid solutions here, another syntax that can be useful sometimes, is to run a command in <(). This would allow you to create more than 1 file-descriptor object on a command line.

This can be useful when you're doing something like comparing long strings of text, or if you want to diff some content that's not in a file.

For example, comparing the hosts files on two nodes without having to copy the hosts file to the localhost:

diff -Naur <(cat /etc/hosts) <(ssh -q otherhost 'cat /etc/hosts')

The < redirects a file to STDIN, and the () create a subshell to run the command between the parenthesis. It's the STDOUT from the subshell that is passed to STDIN of the command being run.

It's an easier way to create more than 1 input "file" to a command than trying to use multiple here docs, or trying to echo multiple commands to a pipeline to the final command.

  • <fileorpathname redirects stdin, but <(subcmd) does not; it substitutes a name that when/if opened by the program can read stdout from subcmd. < <(subcmd) (space required) does redirect stdin from that file, almost like subcmd |. Your diff could read one of its inputs from stdin by specifying an argument of - but not both. – dave_thompson_085 Jan 24 '17 at 5:46
  • That's process substitution which isn't supported, unlike the parts you claim it is made off (but it's not as dave explained). – phk Jan 24 '17 at 19:18
  • 1
    My diff works fine in bash on the Ubuntu 16.04, and Solaris 11.2 systems I have to test with. It's possible it may not work for all shells on all operating systems. It's actually creating file descriptors that can be used to read the output from the subprocess as though it were reading a file. Since diff takes two file arguments, it is able to read the output of both subprocesses through the file descriptors created, and compare them. – Tim Kennedy Jan 25 '17 at 2:34
  • You may wish to add to your answer the difference between cmd <(cmd2 ...) and cmd < <(cmd2 ...). The former allows derived data (the output of cmd2) to be used in place a filename. The latter is equivalent to cmd2 ... | cmd. Commands must be written to explicitly accept stdin input and many are not. This is especially true of shell scripts. – DocSalvager Jan 27 '17 at 8:44
8

you can use a pipe

echo "apple pear orange" | wc
  • 8
    A pipe ist not the same as "reading from a file". For example you can't seek backwards in a pipe, whereas you can in a file. – rbialon Jan 23 '17 at 7:27
0

You may want to use something similar to expect. Following is a simple example of opening a remote telnet session, waiting for the prompt, send some data, wait for a response, sleep and exit.

#!/usr/bin/expect
spawn telnet localhost 8555
expect "Escape character is '^]'."
send "Hello World\n"
expect "Connection closed by foreign host."
sleep 1

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.