How to create a virtual command-backed file in Linux?

7.6k views Asked by At

What is the most straightforward way to create a "virtual" file in Linux, that would allow the read operation on it, always returning the output of some particular command (run everytime the file is being read from)? So, every read operation would cause an execution of a command, catching its output and passing it as a "content" of the file.

4

There are 4 answers

3
marbu On BEST ANSWER

There is no way to create such so called "virtual file". On the other hand, you would be able to achieve this behaviour by implementing simple synthetic filesystem in userspace via FUSE. Moreover you don't have to use c, there are bindings even for scripting languages such as python.

Edit: And chances are that something like this already exists: see for example scriptfs.

0
lxg On

I'm afraid this is not easily possible. When a process reads from a file, it uses system calls like open, fstat, read. You would need to intercept these calls and output something different from what they would return. This would require writing some sort of kernel module, and even then it may turn out to be impossible.

However, if you simply need to trigger something whenever a certain file is accessed, you could play with inotifywait:

#!/bin/bash

while inotifywait -qq -e access /path/to/file; do
        echo "$(date +%s)" >> /tmp/access.txt
done

Run this as a background process, and you will get an entry in /tmp/access.txt each time your file is being read.

0
knarf On

No one mentioned this but if you can choose the path to the file you can use the standard input /dev/stdin.

Everytime the cat program runs, it ends up reading the output of the program writing to the pipe which is simply echo my input here:

for i in 1 2 3; do
    echo my input | cat /dev/stdin
done

outputs:

my input
my input
my input
0
Michael Cole On

This is a great answer I copied below.

Basically, named pipes let you do this in scripting, and Fuse let's you do it easily in Python.


You may be looking for a named pipe.

mkfifo f
{
  echo 'V cebqhpr bhgchg.'
  sleep 2
  echo 'Urer vf zber bhgchg.'
} >f
rot13 < f

Writing to the pipe doesn't start the listening program. If you want to process input in a loop, you need to keep a listening program running.

while true; do rot13 <f >decoded-output-$(date +%s.%N); done

Note that all data written to the pipe is merged, even if there are multiple processes writing. If multiple processes are reading, only one gets the data. So a pipe may not be suitable for concurrent situations.

A named socket can handle concurrent connections, but this is beyond the capabilities for basic shell scripts.

At the most complex end of the scale are custom filesystems, which lets you design and mount a filesystem where each open, write, etc., triggers a function in a program. The minimum investment is tens of lines of nontrivial coding, for example in Python. If you only want to execute commands when reading files, you can use scriptfs or fuseflt.