Open File (open)

Open a file with a preferred handler

Description

open is a smart tool for reading files:

  1. It will read a file from disk or a HTTP(S) endpoints
  2. Detect the file type via file extension or HTTP header Content-Type
  3. It intelligently writes to stdout
  1. If there are no open handlers then it will fallback to the systems default. eg open (on macOS, Linux), open-xdg (X11), etc.

Usage

open filename[.gz]|uri -> <stdout>

Examples

» open https://api.github.com/repos/lmorg/murex/issues -> foreach issue { out "$issue[number]: $issue[title]" }

Detail

File Extensions

Supported file extensions are listed in config under the app and key names of shell, extensions.

Unsupported file extensions are defaulted to generic, *.

Files with a .gz extension are assumed to be gzipped and thus are are automatically expanded.

MIME Types

The Content-Type HTTP header is compared against a list of MIME types, which are stored in config under the app and key names of shell, mime-types.

There is a little bit of additional logic to determine the Murex data-type to use should the MIME type not appear in config, as seen in the following code:

package lang

import (
    "path"
    "regexp"
    "strings"

    "github.com/lmorg/murex/lang/types"
)

var rxMimePrefix = regexp.MustCompile(`^([-0-9a-zA-Z]+)/.*$`)

// MimeToMurex gets the murex data type for a corresponding MIME
func MimeToMurex(mimeType string) string {
    return mimeToMurex(normalizeMime(mimeType))
}
func mimeToMurex(mime string) string {
    // Check suffix
    for suffix := range mimeSuffixes {
        if strings.HasSuffix(mime, suffix) {
            return mimeSuffixes[suffix]
        }
    }

    // Find a direct match
    dt := mimes[mime]
    if dt != "" {
        return dt
    }

    // No matches found. Fall back to prefix
    prefix := rxMimePrefix.FindStringSubmatch(mime)
    if len(prefix) != 2 {
        return types.Generic
    }

    switch prefix[1] {
    case "text", "i-world", "message":
        return types.String

    case "audio", "music", "video", "image", "model":
        return types.Binary

    case "application":
        return types.Generic

    default:
        // Mime type not recognized so lets just make it a generic
        return types.Generic
    }
}

// RequestToMurex attempts to pick a data type for the content of
// a downloaded file using the value of the Content-Type header
// or, if not, the filename. If neither can be used to infer a
// usable data type, types.Generic is returned.
func RequestMetadataToMurex(contentType string, filename string) string {
    contentType = normalizeMime(contentType)

    if !(contentType == "" || contentType == "text/plain") {
        foundType := mimeToMurex(contentType)

        // MimeToMurex will return Generic when it can't find
        // any other matches, but in that case we still want
        // to check the filename ext if possible
        if foundType != types.Generic {
            return foundType
        }
    }

    if filename != "" {
        ext := strings.ToLower(strings.TrimPrefix(path.Ext(filename), "."))
        knownType, foundType := GetFileExts()[ext]

        if foundType {
            return knownType
        }
    }

    return types.Generic
}

// NormalizeMime prepares a mime type for processing by removing charset
// information, trimming leading/trailing spaces, and making it lower case
func normalizeMime(rawMimeType string) string {
    mime := strings.Split(rawMimeType, ";")[0]
    mime = strings.TrimSpace(mime)
    mime = strings.ToLower(mime)

    return mime
}

// MurexToMime returns the default MIME for a given Murex data type.
// The intended use case for this is for GET and POST requests where the body
// is STDIN.
func MurexToMime(dataType string) string {
    return defaultMimes[dataType]
}

HTTP User Agent

open’s user agent is the same as get and post and is configurable via config under they app http

» config -> [http]
{
    "cookies": {
        "Data-Type": "json",
        "Default": {
            "example.com": {
                "name": "value"
            },
            "www.example.com": {
                "name": "value"
            }
        },
        "Description": "Defined cookies to send, ordered by domain.",
        "Dynamic": false,
        "Global": false,
        "Value": {
            "example.com": {
                "name": "value"
            },
            "www.example.com": {
                "name": "value"
            }
        }
    },
    "default-https": {
        "Data-Type": "bool",
        "Default": false,
        "Description": "If true then when no protocol is specified (`http://` nor `https://`) then default to `https://`.",
        "Dynamic": false,
        "Global": false,
        "Value": false
    },
    "headers": {
        "Data-Type": "json",
        "Default": {
            "example.com": {
                "name": "value"
            },
            "www.example.com": {
                "name": "value"
            }
        },
        "Description": "Defined HTTP request headers to send, ordered by domain.",
        "Dynamic": false,
        "Global": false,
        "Value": {
            "example.com": {
                "name": "value"
            },
            "www.example.com": {
                "name": "value"
            }
        }
    },
    "insecure": {
        "Data-Type": "bool",
        "Default": false,
        "Description": "Ignore certificate errors.",
        "Dynamic": false,
        "Global": false,
        "Value": false
    },
    "redirect": {
        "Data-Type": "bool",
        "Default": true,
        "Description": "Automatically follow redirects.",
        "Dynamic": false,
        "Global": false,
        "Value": true
    },
    "timeout": {
        "Data-Type": "int",
        "Default": 10,
        "Description": "Timeout in seconds for `get` and `getfile`.",
        "Dynamic": false,
        "Global": false,
        "Value": 10
    },
    "user-agent": {
        "Data-Type": "str",
        "Default": "murex/1.7.0000 BETA",
        "Description": "User agent string for `get` and `getfile`.",
        "Dynamic": false,
        "Global": false,
        "Value": "murex/1.7.0000 BETA"
    }
}

Open Flags

If the open builtin falls back to using the systems default (like open-xdg) then the only thing that gets passed is the path being opened. If the path is stdin then a temporary file will be created. If you want to pass command line flags to open-xdg (for example), then you need to call that command directly. In the case of macOS and some Linux systems, that might look like:

exec open --flags filename

Synonyms

See Also


This document was generated from builtins/core/open/open_doc.yaml.

This site's content is rebuilt automatically from murex's source code after each merge to the master branch. Downloadable murex binaries are also built with the website.

Last built on Wed Jul 2 22:12:32 UTC 2025 against commit bb72b6fbb72b6fdd502f835172d7d06207ba4ec2c70886c.

Current version is 7.0.2107 (develop) which has been verified against tests cases.