shellescape

package module
v1.5.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 8, 2024 License: MIT Imports: 3 Imported by: 11

README

Build GoDoc sourcegraph codecov Coverage Go Report Card

shellescape

Escape arbitrary strings for safe use as command line arguments.

Contents of the package

This package provides the shellescape.Quote() function that returns a shell-escaped copy of a string. This functionality could be helpful in those cases where it is known that the output of a Go program will be appended to/used in the context of shell programs' command line arguments.

This work was inspired by the Python original package shellescape.

Usage

The following snippet shows a typical unsafe idiom:

package main

import (
	"fmt"
	"os"
)

func main() {
	fmt.Printf("ls -l %s\n", os.Args[1])
}

See in Go Playground

Especially when creating pipeline of commands which might end up being executed by a shell interpreter, it is particularly unsafe to not escape arguments.

shellescape.Quote() comes in handy and to safely escape strings:

package main

import (
        "fmt"
        "os"

        "al.essio.dev/pkg/shellescape"
)

func main() {
        fmt.Printf("ls -l %s\n", shellescape.Quote(os.Args[1]))
}

See in Go Playground

The escargs utility

escargs reads lines from the standard input and prints shell-escaped versions. Unlinke xargs, blank lines on the standard input are not discarded.

Documentation

Overview

Package shellescape provides the shellescape.Quote to escape arbitrary strings for a safe use as command line arguments in the most common POSIX shells.

The original Python package which this work was inspired by can be found at https://pypi.python.org/pypi/shellescape.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Quote

func Quote(s string) string

Quote returns a shell-escaped version of the string s. The returned value is a string that can safely be used as one token in a shell command line.

Example
package main

import (
	"fmt"
	"strings"

	"al.essio.dev/pkg/shellescape"
)

func main() {
	filename := "myfile; rm -rf /"
	prog := "/bin/ls -lh"
	unsafe := strings.Join([]string{prog, filename}, " ")
	safe := strings.Join([]string{prog, shellescape.Quote(filename)}, " ")

	fmt.Println("unsafe:", unsafe)
	fmt.Println("safe:", safe)

	for i, part := range strings.Split(unsafe, " ") {
		fmt.Printf("unsafe[%d] = %s\n", i, part)
	}

	for i, part := range strings.Split(safe, " ") {
		fmt.Printf("safe[%d] = %s\n", i, part)
	}
}
Output:

unsafe: /bin/ls -lh myfile; rm -rf /
safe: /bin/ls -lh 'myfile; rm -rf /'
unsafe[0] = /bin/ls
unsafe[1] = -lh
unsafe[2] = myfile;
unsafe[3] = rm
unsafe[4] = -rf
unsafe[5] = /
safe[0] = /bin/ls
safe[1] = -lh
safe[2] = 'myfile;
safe[3] = rm
safe[4] = -rf
safe[5] = /'

func QuoteCommand

func QuoteCommand(args []string) string

QuoteCommand returns a shell-escaped version of the slice of strings. The returned value is a string that can safely be used as shell command arguments.

Example
package main

import (
	"fmt"

	"al.essio.dev/pkg/shellescape"
	"github.com/google/shlex"
)

func main() {
	filename := "myfile; rm -rf /"
	unsafe := fmt.Sprintf("ls -l %s", filename)
	command := fmt.Sprintf("ls -l %s", shellescape.Quote(filename))
	splitCommand, _ := shlex.Split(command)

	fmt.Println("unsafe:", unsafe)
	fmt.Println("command:", command)
	fmt.Println("splitCommand:", splitCommand)

	remoteCommandUnsafe := fmt.Sprintf("ssh host.domain %s", command)
	remoteCommand := fmt.Sprintf("ssh host.domain %s", shellescape.Quote(command))
	splitRemoteCommand, _ := shlex.Split(remoteCommand)

	fmt.Println("remoteCommandUnsafe:", remoteCommandUnsafe)
	fmt.Println("remoteCommand:", remoteCommand)
	fmt.Println("splitRemoteCommand:", splitRemoteCommand)

	lastSplit, _ := shlex.Split(splitRemoteCommand[2])
	fmt.Println("lastSplit[0]:", lastSplit[0])
	fmt.Println("lastSplit[1]:", lastSplit[1])
	fmt.Println("lastSplit[2]:", lastSplit[2])

	// unsafe: ls -l myfile; rm -rf /
	// command: ls -l 'myfile; rm -rf /'
	// splitCommand: [ls -l myfile; rm -rf /]
	// remoteCommandUnsafe: ssh host.domain ls -l 'myfile; rm -rf /'
	// remoteCommand: ssh host.domain 'ls -l '"'"'myfile; rm -rf /'"'"''
	// splitRemoteCommand: [ssh host.domain ls -l 'myfile; rm -rf /']
	// lastSplit[0]: ls
	// lastSplit[1]: -l
	// lastSplit[2]: myfile; rm -rf /
}
Output:

Example (Simple)
package main

import (
	"fmt"
	"strings"

	"al.essio.dev/pkg/shellescape"
)

func main() {
	filename := "filename with space"
	prog := "/usr/bin/ls"
	args := "-lh"

	unsafe := strings.Join([]string{prog, args, filename}, " ")
	safe := strings.Join([]string{prog, shellescape.QuoteCommand([]string{args, filename})}, " ")

	fmt.Println("unsafe:", unsafe)
	fmt.Println("safe:", safe)
}
Output:

unsafe: /usr/bin/ls -lh filename with space
safe: /usr/bin/ls -lh 'filename with space'

func StripUnsafe

func StripUnsafe(s string) string

StripUnsafe remove non-printable runes, e.g. control characters in a string that is meant for consumption by terminals that support control characters.

Example
package main

import (
	"fmt"

	"al.essio.dev/pkg/shellescape"
)

func main() {
	safeString := `"printable!" #$%^characters '' 12321312"`
	unsafeString := "these runes shall be removed: \u0000\u0081\u001f"

	fmt.Println("safe:", shellescape.StripUnsafe(safeString))
	fmt.Println("unsafe:", shellescape.StripUnsafe(unsafeString))
}
Output:

safe: "printable!" #$%^characters '' 12321312"
unsafe: these runes shall be removed:

Types

This section is empty.

Directories

Path Synopsis
cmd
escargs
escargs reads lines from the standard input and prints shell-escaped versions.
escargs reads lines from the standard input and prints shell-escaped versions.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL