Bash – Using Process Substitution to Trick Programs Expecting Files

bashfilenamesprocess-substitution

Here is my use case: the command line utility melt can accept a file name, with the extension .melt on the command line, and open it; as an example, this is a proper test_p.melt file:

colour:blue
out=24

colour:red
out=48

… which opens and plays with melt test_p.melt.

Now, the thing is that .melt files do not support comments, which I wish they did (you'll get error messages for any line that contains an unparsable argument, including those with, say, a #). So here is a commented test_c.melt file:

# master comment here

colour:blue  # this is blue!
out=24

colour:red
out=48

Opening this in melt directly gives:

$ melt test_c.melt
Failed to load "# master comment here"
...

… and there is no blue screen shown.

So I thought – well, I can put in comments anyway, and then use Bash process substitution to filter the file with sed, and simply provide that to the melt application. First, tried a test with cat, which is successful:

$ cat <(sed 's/#.*$//' test_c.melt)



colour:blue  
out=24

colour:red
out=48

… looks good; but, if I try that with melt, it sees through my trickery:

$ melt <(sed 's/#.*$//' test_c.melt)
Failed to load "/dev/fd/62"
Failed to load "/dev/fd/62"

Basically, melt got the filename of the pipe Bash provided for the process substitution – but unfortunately, what melt does is that it processes argv[i] directly; and in case of a file, it needs to see a .melt extension in the filename; if it doesn't – the process fails.

So my question is: how could I use process substitution – so the filename of the pipe has a specific extension, in this case .melt? Basically, as a result of the substitution, I'd want a pipe filename of /dev/fd/62.melt, which I think will pass.

NB: of course, I can always do:

sed 's/#.*$//' test_c.melt > test_c_temp.melt
melt test_c_temp.melt

… but first, there are two commands here – and I'd want a one-liner pipeline; and for another, it opens up another problem of me thinking about removing temporary files afterwards, which I don't like.

Is this possible with Bash process substitution – or somehow with standard Linux tools?

Best Answer

A possibility would be to point melt to a filesystem that shows modified copies of files. FUSE is a generic way to build filesystem driver implemented by an ordinary program and requiring no privileges. There are many FUSE filesystems around, and there's a good chance that one of them can help you. The idea is to provide a mount point where reading a .melt file reads the “real” file but with comments filtered out.

ScriptFS looks promising (but I've never used it). Something like this should work:

mkdir ~/uncommented-melt
scriptfs -p "$HOME/bin/uncomment-melt;&*.melt" ~/work ~/uncommented-melt

where ~/work is the root of the tree that contains your .melt files and ~/bin/uncomment-melt is

#!/bin/sh
sed 's/#.*$//' "$1"

Then if you have a file ~/work/test_c.melt with comments, you can run melt ~/uncommented-melt/test_c.melt.

Other potential helpful FUSE filesystems:

  • Execfuse — lets you build a simple FUSE driver with shell scripts
  • AVFS or other FUSE filesystems that transparently uncompress files: define the stripping of comments as an uncompression rule.