Share your file-opener script - Printable Version
+- nixers (
+-- Forum: Development & Graphics (
+--- Forum: Programming On Unix (
+--- Thread: Share your file-opener script (/Thread-Share-your-file-opener-script)

Share your file-opener script - seninha - 30-05-2020

Most of us have an handy script for opening files and/or links given as arguments. This is, sometimes, one of our most used scripts. Use this thread to share your file-opener or link-opener scripts and steal ideas from others.

Here is mine, I call it xopen.

# xopen: open file with proper command; use file in $MIMEFILE as rules file
# this file in public domain

usage() {
    echo "usage: xopen [-c mimefile] filename" >&2
    exit 1

# get mimetype of a file using the file(1) utility
getfilemime() {
    file -ib "$@" | cut -d';' -f1

# get lowercase extension of a file
getextension() {
    echo "${1##*.}" | tr '[:upper:]' '[:lower:]'

# get mimetype of a url
geturlmime() {
    protocol=$(printf "%s" "$@" | sed 's/^\([a-zA-Z]\+\):.*/\1/')

    case "$protocol" in
        echo text/html
        echo application/x-bittorrent
        echo x-scheme-handler/irc

    unset protocol

# get command to open file or url
getcmd() {
    while read -r line
        line=${line%%#*}                # remove comments
        [ -z "$line" ] && continue      # ignore blank lines

        pattern="$(echo $line | cut -s -d: -f1 | sed -E 's/[     ]*$//; s/\|/ /g')"
        cmd="$(echo $line | cut -s -d: -f2 | sed -E 's/^[     ]*//')"
        cmd="$(eval echo $cmd)"

        # using case for matching patterns to avoid forking
        for i in $pattern
            case $1 in
            $i) echo "$cmd" && return ;;
    done < "$MIMEFILE"

# execute command $1 to open file or url $2
xopen() {
    trap "" 1 15
    echo xopen: opening $search with $1 >&2
    exec $1 "${2}" &

while getopts 'c:' c
    case "$c" in
shift $((OPTIND -1))

[ "$#" -ne 1 ] && usage

# test whether MIMEFILE is not set
[ -z "$MIMEFILE" ] && echo 'xopen: $MIMEFILE is not set' >&2 && exit 1

# get mimetype and file extension
if [ -e "$@" ];
then            # it's a file
    mime=$(getfilemime "$@")
    extension=$(getextension "$@")
else            # it's a url
    mime=$(geturlmime "$@")
[ -z "$extension$mime" ] && echo "xopen: unknown file or url type" >&2 && exit 1

# get command to open file or url
cmd=$(getcmd "$extension")              # try first to open by file extension
[ -z "$cmd" ] && cmd=$(getcmd "$mime")  # then try to open by mimetype
[ -z "$cmd" ] && echo "xopen: cannot find an command to open $@ ($mime)" >&2 && exit 1

# open file or url with $cmd
xopen "$cmd" "$@"

It gets its configuration from a file specified in the environment variable $MIMEFILE (or after the -c option). Each line in this file is composed by a mimetype or a file extension followed by a colon, followed by the command that opens a file with that mimetype or extension. Here is my $MIMEFILE:

# web
text/html:  palemoon

# text
text/*:                     $TERMCMD -e $EDITOR
application/x-empty:        $TERMCMD -e $EDITOR
application/x-shellscript:  $TERMCMD -e $EDITOR
inode/x-empty:              $TERMCMD -e $EDITOR
inode/directory:            $TERMCMD -e fm

# Docs
application/pdf:        zathura
application/postscript: zathura
application/epub*:      zathura
image/*djvu:            zathura

# media
application/octet-stream: mpv
video/*:                  mpv
audio/*:                  mpv

# image
image/*:                  sxiv

# Man pages
0|1|2|3|4|5|6|7: $TERMCMD -e man

Now share yours!

RE: Share your file/link-opener script - z3bra - 31-05-2020

Mine is self-contained, though I like your way to use the mailcap entry… I'll look this out later.
I cleaned up mine a bit. The cool stuff about it (I think) is that you can pipe data to it and open it. Not only URIs:

curl -s 'https://random.tld/image.png' | plumb -s

This would cache the file locally, and open it with the according mime entry.
Here is the full script:

# open things from uri


usage() {
    echo "usage: $(basename $0) [-h] [uri]" >&2

peekmime() {
    curl -sSfL "$1" | cut -b-16 | file -ib - | cut -d\; -f1

# return a uri to a local file (download if necessary)
localuri() {
    if [ ! -f "$1" ]; then
        curl -o $tmp -sSfL "$1"
        local=$(cachefile "$tmp" "${mime##*/}")
        rm $tmp
    printf '%s\n' "$local"

# copy file to cache
cachefile() {
    mkdir -p $cachedir
    hash=$(sha256sum "$1"|cut -d' ' -f1)
    cp "$1" $cachedir/${hash}.$2
    printf '%s/%s.%s\n' "$cachedir" "$hash" "$2"

# prompt for a command to open uri
prompt() {
    dmenu_path | dmenu -p "$1" -l 8

clip="$(xsel -o 2>/dev/null)"

while getopts "hs" OPT; do
    case $OPT in
    s) slurp=$(mktemp -p $cachedir) ;;
    h) usage; exit 0 ;;
    *) usage; exit 1 ;;
shift $((OPTIND - 1))

# fallback to clipboard when not uri given

# special arg "-" read uri(s) from stdin
if [ "$uri" = "-" ]; then
    cat | xargs -P$(nproc) -n1 $0

# can read file _content_ from stdin, then process it
if [ -n "$slurp" ]; then
    cat > $slurp

[ -z "$uri" ] && exit 1

if [ -f "$uri" ]; then
    mime=$(file -ib "$uri" | cut -d\; -f1)
    case $proto in
    mailto)     mime='application/x-mail' ;;
    magnet)     mime='application/x-bittorrent' ;;
    *)          mime="$(peekmime $uri)" ;;

if [ -n "$slurp" ]; then
    uri="$(cachefile $slurp ${mime##*/})"
    rm "$slurp"

case "$mime" in
    text/html) exec firefox "$uri" ;;
    video/*)   exec mplayer -cache 512 "$uri" ;;
    image/gif) exec mplayer -loop 0 $(localuri "$uri") ;;
    image/*)   exec meh $(localuri "$uri") ;;
    text/*)    exec st -e less $(localuri "$uri") ;;
    application/x-mail) exec st -e mutt $uri ;;
    application/x-bittorrent) exec st -e rtorrent $uri ;;
    *) exec $(prompt) "$uri" ;;

RE: Share your file/link-opener script - venam - 31-05-2020

I rely on the freedesktop utility xdg-open as it already has full integration with mime types and desktop files. I kind of pointed it a bit in this discussion about default programs.

So what I usually do is xdg-open, and if it doesn't call the program I want then I can switch it using mimeopen -a. In the case where my program isn't listed, I create a desktop file in ~/.local/share/applications, then update-desktop-database. Most of the time, this solves my issues, otherwise I can dive into adding my own mimetype in ~/.local/share/mime/application.

RE: Share your file-opener script - jkl - 10-02-2021

Almost on-topic: In a C++ software of mine (which I really should develop further... some day), I wrote a function to open a text file with the default editor. I would guess that, if one removes the VISUAL/EDITOR checks, it fulfills this thread’s needs as well.

Note that sanitizing the filename needs to be done beforehand. Better don’t make this function publicly callable. :)

inline bool openWithEditor(const char* filename) {
    // Opens <filename> in your default editor.
#ifdef _WIN32
    // Windows version, using the Windows API.
    return reinterpret_cast<int>(ShellExecuteA(NULL, "open", filename, NULL, NULL, SW_SHOW)) > 32;
    // Other system, other ways...
    string editor = "";

    if (getenv("VISUAL") != NULL) {
        editor = getenv("VISUAL");
    else if (getenv("EDITOR") != NULL) {
        editor = getenv("EDITOR");
    else {
#ifdef __linux__
        editor = "xdg-open";
        // No standard editor found. Sorry!
        return false;

    return system(string(editor + " \"" + filename + "\"").c_str()) == 0;

RE: Share your file-opener script - neeasade - 13-02-2021

Here is my current "go to what I'm looking at" emacs function:

The goal is to handle various fallbacks, through org links, files, urls, and code definitions.

RE: Share your file-opener script - venam - 07-03-2022

There's a discussion on lobsters related to this. An interesting custom solution is this one from vermaden.