0

Say I have a standard input stream of file contents and a command that expects a file name as an argument and I want to run that command on a file made up of the standard input stream’s file contents.

For instance, instead of the command

imageviewer mouse.jpg

I want some magical line magic that makes this equivalent to

cat mouse.jpg | magic

What would this magic look like in zsh or bash?

Preferably, I would like it to look like

cat mouse.jpg | submagic | xargs imageviewer

that is, I would like submagic to create a temporary file from the standard input stream and output the created file’s file name.

The lines magic or submagic should be shell pipelines using only bash or zsh commands, their builtins, GNU coreutils and the likes.


This is the entire question. Below is merely clarification and background because people kept misinterpreting what I meant.


Okay, so I thought I made it clear, but it seems like I didn’t: I really want to have a pipeline doing the equivalent of

imageviewer mouse.jpg

that begins exactly with cat mouse.jpg | …. That is, I really want exactly a line magic or submagic such that exactly the pipelines above work – I meant my question literally.

This means in particular that the following suggested solutions won’t do:

  • beginning the pipeline with tmpfile = $(mktemp); cat mouse.jpg | …
  • rewriting it as imageviewer =(cat mouse.jpg)
  • rewriting it as echo mouse.jpg | xargs imageviewer
  • using a function or a binary to solve it, say by defining function magic () { … }

It can be said that what I’m interested in is whether it’s possible to write an alias alias magic='…' or alias submagic='…' such that one of the above pipelines work and such that definition of these aliases contains nothing more than bash or zsh commands, GNU coreutils and the likes – nothing self-written.

Another comment. You make take imageviewer to be feh or sxiv or something.

Background. This question came to me when I tried opening several image files attached to a mail using my mail client neomutt. Neomutt offers to tag attached files and then to pipe the file contents to a command line you may type in. So here, I can only give a command line that performs something on a given standard input stream. That’s where the question came from. But I’m not interested in the original problem, but only in this very question.

  • The answers you have provide all the pieces to come up with a function: function magic (){ tmpfile=$(mktemp); cat - >"$tmpfile"; printf '%s\n' "$tmpfile"; }. This will work exactly the way you are asking for: cat mouse.jpg | magic | xargs gwenview (gwenview is the image viewer I have and which I'm testing with). If this falls short of your expectations then I fear you'll have to clarify even more... – fra-san Jan 18 at 17:15
  • @fra-san Yeah, it’s a function not a pipeline of bash or zsh commands. : ( I’ve added further clarification. I was very precise in what I’ve written, but it obviously hasn’t been clear at all that I really am only interested in that and nothing else. – k.stm Jan 18 at 21:38
  • With your edits you add more and more restrictions to rule out all proposed solutions but you don't explain the reason for your strange requirements. – Bodo Jan 21 at 15:27
  • @Bodo I don’t add restrictions. I can leave out all the edits and the question would still be the same. The edits are merely clarifications because people kept interpreting my question loosely as “I want to do roughly this.”, whereas I meant “I want to do exactly this”. I’ll add a small background for an explanation for these requirements. – k.stm Jan 22 at 13:50
  • And what's the reason for the restriction "nothing self-written"? It would be simple to create a shell script that writes the data from stdin to a temporary file, runs imageviewer (or a command specified as cmdline arg) and removes the temporary file when imageviewer has terminated. – Bodo Jan 22 at 14:06
1

To meet the exact requirements of your question, you can do:

cat mouse.jpg | { tf=$(mktemp); cat >"$tf"; echo "$tf"; } | xargs imageviewer
5

In bash at least, with the use of a process substitution:

utility <( some_other_utility )

This would present utility with a file name. When utility opens the file and reads from it, it will read the standard output of some_other_utility.


Your example:

cat mouse.jpg | submagic | xargs imageviewer

I'm assuming that this is meant to send the file name mouse.jpg to imageviewer, as it doesn't make sense to send the contents of a JPEG file through xargs.

That could be done through just

echo 'mouse.jpg' | xargs imageviewer

or

xargs imageviewer <<<'mouse.jpg'

in bash.

Sending the contents of mouse.jpg to imageviewer can be done with

cat mouse.jpg | imageviewer /dev/stdin

or, using the process substitution, with

imageviewer <( cat mouse.jpg )

Using a temporary file:

tmpfile=$(mktemp)
cat mouse.jpg >"$tmpfile"

imageviewer "$tmpfile"

rm "$tmpfile"

Possibly,

tmpfile=$(mktemp)
cat mouse.jpg | { cat >"$tmpfile"; echo "$tmpfile"; } | xargs imageviewer
rm "$tmpfile"

This pipeline relies on the fact that xargs would wait with executing imageviewer until it had read the filename from the middle part of the pipeline. The filename would not be outputted by the middle command until the temporary file had been created.

  • There might be an issue if imageviewer wants to seek the file – Torin Jan 18 at 10:48
  • Thanks – that doesn’t answer the question, though. That I start with a standard input stream of some process is a restriction. In your answer, the output from some_other_utility isn’t handled as a standard input stream. I specifically need some command that I can put in lieu of magic or submagic. Maybe think of the question as: “Write an alias alias magic=… such that the above works”. – k.stm Jan 18 at 10:49
  • @k.stm Your example does not make much sense to me. Could you clarify please? – Kusalananda Jan 18 at 10:50
  • @Torin This is a restriction set out by the question. If the data is a stream, then it's likely not seekable to start with. – Kusalananda Jan 18 at 10:52
  • 1
    @k.stm Since all parts of a pipeline runs concurrently, you submagic command would possibly start after imageviewer, which means the file that it created would not be available for imageviewer. This is regardless of whether it's implemented in the shell or in C. You would have to first create the file, then invoke imageviewer. – Kusalananda Jan 18 at 10:57
3

After a clarification of submagic in the question I edited the answer.

In zsh you can create a temporary file from the output of a command with =( ... ). In contrast to <( ... ) which may use a device file like /dev/fd... or a named pipe the form =( ... ) creates a seekable temporary file. See http://zsh.sourceforge.net/Doc/Release/Expansion.html#Process-Substitution

Assuming that cat mouse.jpg is only an example for any command that sends image data to stdout, you can use

imageviewer =(cat mouse.jpg)

If you need the temporary file created by zsh to have a particular extension, you can set the TMPSUFFIX special variable):

(TMPSUFFIX=.jpg; imageviewer =(cat mouse.jpg))

second update

If you insist on the syntax

cat mouse.jpg | submagic | xargs imageviewer

it would be possible to create a script submagic similar to the commands already proposed in Kusalananda's answer.

#! /bin/sh

# A fixed name allows multiple runs without creating lots of files and
# allows a simple script to remove the file afterwards.
TMPFILE=/tmp/submagic.tmp.jpg
rm -f "$TMPFILE" && cat > "$TMPFILE" && echo "$TMPFILE"

In contrast to shell mechanisms like =( cat mouse.jpg ) this would not remove $TMPFILE after running imageviewer.

If you can run a second script after running imageviewer this could remove the file if it knows the name.

Script submagic-cleanup

#! /bin/sh

# A fixed name allows a simple script for cleaning up.
TMPFILE=/tmp/submagic.tmp.jpg
rm -f "$TMPFILE"

Then you could run

cat mouse.jpg | submagic | xargs imageviewer
submagic-cleanup
  • Thanks, that still doesn’t answer my question. I need an alias for submagic such that exactly cat mouse.jpg | submagic | xargs imageviewer works. – k.stm Jan 18 at 15:28
  • @k.stm Why do you have this requirement? This looks like hou have a specific (difficult) solution in mind while there might be other solutions for your real problem. – Bodo Jan 21 at 12:24

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.