44

I wonder if there is a way to create a 'virtual file' from a bash output.

Example: Let's say I want to email the output of mysqldump as an attachment to an external email address. I can use Mutt to do so. The mutt option I need to use is -a <name of the file I want to attach>. I know I could use a temporary file:

mysqldump mysqldumpoptions > /tmp/tempfile && mutt -a /tmp/tempfile admin@example.org

But I would rather redirect the mysqldump output directly to Mutt instead. Mutt's -a option only accepts a file and not a stream, but maybe there is a way to pass it some kind of virtual file descriptor or something along those lines. Something like:

mutt -a $(mysqldump mysqldumpoptions) admin@example.org

Is it possible? If not, why?

This is maybe a silly example and there surely are easier ways to do this, but I hope it explains my question about creating a virtual file from the output of another command.

janos
  • 798
  • 1
  • 5
  • 22
ppbitb
  • 542
  • 1
  • 4
  • 7
  • 1
    I couldn't get <() to work until I realized I was using sh instead of bash. It is working great. I will upvote the helpful responses when (if) I reach the minimum needed reputation. Thank you for all the responses. – ppbitb Jul 14 '09 at 17:05

8 Answers8

71

This is the cleanest way to do what you want:

mutt admin@example.org -a <(mysqldump mysqldumpoptions)

The <() operator is what you were asking for; it creates a FIFO (or /dev/fd) and forks a process and connects stdout to the FIFO. >() does the same, except connects stdin to the FIFO instead. In other words, it does all the mknod stuff for you behind the scenes; or on a modern OS, does it in an even better way.

Except, of course, that doesn't work with mutt, it says:

/dev/fd/63: unable to attach file.

I suspect the problem is that mutt is trying to seek in the file, which you can't do on a pipe of any sort. The seeking is probably something like scanning the file to figure out what MIME type it is and what encodings might work (ie, whether the file is 7bit or 8bit), and then seeking to the beginning of the file to actually encode it into the message.

If what you want to send is plain text, you could always do something like this to make it the main contents of the email instead (not ideal, but it actually works):

mysqldump mysqldumpoptions | mutt -s "Here's that mysqldump" admin@example.org
freiheit
  • 14,334
  • 1
  • 46
  • 69
  • 2
    Awesome! I am seeing such redirection for first time. – Saurabh Barjatiya Jul 14 '09 at 11:12
  • Thank you for the good explanation! I have never seen this before, in any tutorials on I/O redirection. – Christian Mann Jul 21 '11 at 18:18
  • Leaving this here as is took me a while to track it down: [`<()` is called **process substitution**](https://stackoverflow.com/questions/31703243/what-is-the-name-for-operator-in-bash) ([archive](https://web.archive.org/web/20210403191230/https://stackoverflow.com/questions/31703243/what-is-the-name-for-operator-in-bash)). – toraritte Apr 03 '21 at 19:14
4

I think what you are looking for is a fifo using mknod

mknod /tmp/foo p

echo hello > /tmp/foo &

cat /tmp/foo

Note, the writing process will block if there is no reading process.

e.g.

mknod /tmp/foo p

mysqldump mysqldumpoptions > /tmp/foo &

mutt -a /tmp/foo
Falko
  • 109
  • 5
James
  • 2,212
  • 1
  • 13
  • 19
2

A FIFO is probably the best way. However you can use mkfifo /path/to/fifo instead

Amandasaurus
  • 30,211
  • 62
  • 184
  • 246
1

Does it need to be an attachment? If it can appear as the body of a message (7-bit clean ascii, doesn't contain '.' on a line by itself, etc.), then something like this would work:

mysqldump mysqldumpoptions | mutt -s "today's mysql dump" admin@example.org

pgs
  • 3,471
  • 18
  • 19
1

You may be able to use the filename /dev/stdin with mutt, which will read from its stdin.

mysqldump mysqldumpoptions | mutt -a /dev/stdin admin@example.org

Hm, I just tried this and mutt complains:

/dev/stdin isn't a regular file.
/dev/stdin: unable to attach file.

Oh well. I think this answers your question though, with the -a option mutt expects a regular file, which implies not a device or pipe.

Greg Hewgill
  • 6,749
  • 3
  • 29
  • 26
0

i may be missing something here, but why use mutt when this sounds more like a job for /usr/sbin/sendmail (or wherever it lives on your distro)?

mysqldump mysqldumpoptions | sendmail admin@example.org

most unix MTAs provide a /usr/sbin/sendmail command, and they all understand more-or-less the same options and work in more-or-less the same way. in almost all cases, you don't need to care which particular MTA/implementation of sendmail you're using.

there are several other alternatives too, including mail/mailx

cas
  • 6,653
  • 31
  • 34
  • 1
    then it's not an attachment, which seems likely to be harder to use for the recipient – freiheit Jul 14 '09 at 07:06
  • 1
    in that case, a better solution would be to use a tool like metamail(1) or mime-construct(1p) or writing your own using something like the perl MIME::Lite module (my personal favourite because it makes it easy to do the most common mime-related things). – cas Jul 14 '09 at 09:49
0

Mutt -a option specifically uses MIME types defined at /etc/mime.types. None of the virtual file types that you would think are actually a valid mime type registered, so I don't think mutt attachment would work. Just for the proof of the concept, you can try install mime-support package (which provides mime file access binaries such as see, edit, etc) and run see against a virtual file (say a pipe), you will see that it does not recognize it. The fundamental issue is that normally a virtual file holds data content in memory, not on disk, the file object is just a pointer. You can even try wrap a virtual file with tar/gz and make mutt happy but I doubt you will get anything useful out of the other end... :)

-1

I was hoping for a more direct bash-native option. I ended up with something like

#!/bin/bash
badly-made-program \
    -multiline-literal "$(cat <<-EOF
        magic
        text
        here
EOF
)"

edit later I recognized that bash literals could be broken over newlines and escaped properly anyway, this gives up on having the dedent removed by the <<-EOT block, but moving it outside of the call site to a variable was probably good fit for me here today

mutliline_literal="
magic
text
here
"
badly-made-program \
    -multiline-literal "${multiline_literal}"

If you know better, how do I get rid of :using cat to redirect a bash heredoc from stdin to a literal string"

edit2: i was looking for almost the exact opposite of the question's goal, idk google landed me here, left for the posterity or other googler's

ThorSummoner
  • 321
  • 4
  • 13
  • That's something completely different than what is asked in the question. If your intention was to ask a new question, you should use the `ask question` button. But your question seems to be more suited for [so] or [unix.se] anyway. – Gerald Schneider Feb 11 '22 at 06:14