'How to pipe input to sublimetext on linux?

How do I receive text from stdin into sublimetext editor? With vim it works like this:

echo "potato potato potato" | vim -

The same thing works with gedit, creating a new document with the contents.

Sublimetext seems to just start editing a file called "-" in a new tab, is there some other shell trick which could work?



Solution 1:[1]

Assuming Sublime still doesn't support opening STDIN, a straightforward solution is to dump the output to a temporary file, open the file in sublime, and then delete the file. Say you create a script called tosubl in your bin directory as follows:

#!/bin/bash

TMPDIR=${TMPDIR:-/tmp}  # default to /tmp if TMPDIR isn't set
F=$(mktemp $TMPDIR/tosubl-XXXXXXXX)
cat >| $F  # use >| instead of > if you set noclobber in bash
subl $F
sleep .3  # give subl a little time to open the file
rm -f $F  # file will be deleted as soon as subl closes it

Then you could use it by doing something like this:

$ ps | tosubl

But a more efficient and reliable solution would be to use to use Process Substitution if your shell supports it (it probably does). This does away with the temporary files. Here it is with bash:

tosubl() {
    subl <(cat)
}

echo "foo" | tosubl

I'm pretty sure you can somehow remove the use of cat in that function where it's redundantly shoveling bits from stdin to stdout, but the syntax to do so in that context eludes me at the moment.

Solution 2:[2]

I don't know Sublime Text, but your problem should be generic in that it applies to any program that does accept a filename as argument, but refuses to read from stdin.

Fortunately, Bash allows you to pipe stdout from one process into some kind of temporary file, then pass the name of that file to another process.

From man bash:

Process substitution is supported on systems that support named pipes (FIFOs) or the /dev/fd method of naming open files. It takes the form of <(list) or >(list). The process list is run with its input or output connected to a FIFO or some file in /dev/fd. The name of this file is passed as an argument to the current command as the result of the expansion. If the >(list) form is used, writing to the file will provide input for list. If the <(list) form is used, the file passed as an argument should be read to obtain the output of list.

Assuming SomeProcess produces output that you would like to capture in your editor:

sublimetext <(SomeProcess)

or:

SomeProcess | sublimetext <(cat)

If you think you will be typing this in by hand a lot, then you may want to put sublimetext <(cat) into a shell script or alias.

Just in case your OS does not support process substitution, then you can always specify a temporary file yourself of course:

SomeProcess > /tmp/myoutput
sublimetext /tmp/myoutput

Solution 3:[3]

You might find vipe from moreutils useful. If you've set EDITOR='subl --wait', you can simply:

echo 'potato potato potato' | vipe

Solution 4:[4]

Starting from version 4, SublimeText supports piping in from stdin.

From the changelog:

Build 4063

Command Line: subl - can now be used to read from stdin on all platforms

Update !only! on MacOS: As of build 4109

subl can now be used to edit stdin, eg: echo test | subl | cat

Solution 5:[5]

Piggybacking on https://stackoverflow.com/a/31035834/2429912 a bit, since for me it does 90% but not all of it:

Using a temporary file is a way that can be used with virtually any editor. Even better if the editor supports waiting until the file is closed(sublime -w for Sublime Text) you can actually edit it on the fly, which makes it more versatile. To do that you need to alter the script @tylerl provided - script named tosubl in your PATH:

#!/bin/bash

TMPDIR=${TMPDIR:-/tmp} # default to /tmp if TMPDIR isn't set
F=$(mktemp $TMPDIR/tosubl-XXXXXXXX)
cat >| $F # use >| instead of > if you set noclobber in bash
subl -w $F # waits until the file is closed
cat $F # prints out the contents of the file to stdout
rm -f $F # clean up the file

Now running echo "hello" | tosubl > local.file would open the output of the first script in Sublime and once you have closed it, save it to local.file.

Solution 6:[6]

If you want to display colored text with ANSI escape sequences (a terminal buffer for example), you can install the package ANSIescape and use the following command:

F=$(mktemp); cat > $F; subl $F; subl --command ansi; sleep .5; rm -f $F

Solution 7:[7]

Well, it's 2020 and sublime still can't (won't?) read from stdin. None of the answers in this thread were working satisfactorily for me due to the program I was having pass input to sublime.

In my case, I wanted to take advantage of the "paste current selection to program" capability of the kitty terminal emulator. That functionality takes the current selection in the terminal and pipes it into a program for you when you press the keyboard shortcut. For some reason, the developer of kitty decided to pass the selection as an argument rather than via stdin.

To remedy this, I wrote a small C script that can handle both scenarios. In order to run the script, you'll need to install TCC. You may want to check your repo first. I know on debian you can install it with apt: sudo apt install tcc. Alternatively, you can compile the following code with GCC, just remove the first line.

#!/usr/bin/tcc -run

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    uint64_t randVal = 0;
    FILE *fp = fopen("/dev/urandom", "rb");
    fread((char *) &randVal, 8, 1, fp);
    fclose(fp);
    
    char tmpfile[512] = {0};
    sprintf(tmpfile, "/tmp/piped_input_%llu.txt", randVal);
    
    fp = fopen(tmpfile, "wb");
    
    if(!fp)
    {
        fprintf(stderr, "Could not open temporary file: '%s'\n", tmpfile);
        return -1;
    }
    
    if(argc == 2)
        fprintf(fp, "%s", argv[1]);
        
    if(argc == 1)
    {
        uint8_t tmpBuf[4096] = {0};
        memset(tmpBuf, 0, 4096); // Old habit... ¯\_(?)_/¯
        
        while(!feof(stdin))
        {
            int32_t numRead = fread((char *) tmpBuf, 1, 4096, stdin);
            
            if(numRead > 0)
                fwrite((char *) tmpBuf, numRead, 1, fp);
            else
                break;
        }
    }
    
    fflush(fp);
    fclose(fp);
    
    char cmdStr[512] = {0};
    sprintf(cmdStr, "subl %s", tmpfile);
    system(cmdStr);
    
    // Give sublime some time to open the file
    usleep(1000 * 250);
    unlink(tmpfile);
    
    return 0;
}

The above script will read from stdin unless an argument is passed to the program. In either case, it simply reads the input (stdin or argv[1]) into a temporary file, opens the file with sublime, waits ~250ms to give time for sublime to open the file, and deletes the temporary file.

You can copy the above code to a location in your path (like /usr/local/bin/subl_pipe). Make sure set the permissions: chmod 755 /usr/local/bin/subl_pipe.

Anyway, I hope this is useful to somebody else eventually... ^_^

Solution 8:[8]

There is a straightforward way to do this: you can pipe input to Sublime Text (build 4121 and up) as it supports reading from STDIN:

cat file.txt | subl - 

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Ruud Helderman
Solution 3 ændrük
Solution 4
Solution 5 Rauno56
Solution 6 danmou
Solution 7 Gogeta70
Solution 8 Henry Ecker