Skip to content

Should ExecForEach sanitize or escape arguments differently? #184

Open
@telemachus

Description

@telemachus

After a recent conversation about script on Lobste.rs, I'm concerned about how ExecForEach handles arguments (especially filenames?) with tricky characters (e.g., spaces, single quotes, and double quotes).

The current example for ExecForEach is ListFiles("*").ExecForEach("touch {{.}}").Wait(). I'll take that as a baseline, and start with the following minimal script:

package main

import "github.com/bitfield/script"

func main() {
	script.ListFiles("*").ExecForEach("touch {{.}}").Stdout()
}

Imagine that lives in a folder with the following files:

$ ls -l
total 24
-rw-r--r--  1 telemachus  wheel     0 Aug 21 07:14 Bobby" Tables"
-rw-r--r--  1 telemachus  wheel     0 Aug 21 07:14 Bobby' Tables'
-rw-r--r--  1 telemachus  wheel   210 Aug 21 07:17 go.mod
-rw-r--r--  1 telemachus  wheel  3221 Aug 21 07:17 go.sum
-rw-r--r--  1 telemachus  wheel     0 Aug 21 07:19 hello world
-rw-r--r--  1 telemachus  wheel   125 Aug 21 07:16 main.go

Here's the result of running the script:

$ ls -l
total 24
-rw-r--r--  1 telemachus  wheel     0 Aug 21 09:32 Bobby Tables
-rw-r--r--  1 telemachus  wheel     0 Aug 21 07:14 Bobby" Tables"
-rw-r--r--  1 telemachus  wheel     0 Aug 21 07:14 Bobby' Tables'
-rw-r--r--  1 telemachus  wheel   210 Aug 21 09:32 go.mod
-rw-r--r--  1 telemachus  wheel  3221 Aug 21 09:32 go.sum
-rw-r--r--  1 telemachus  wheel     0 Aug 21 09:32 hello
-rw-r--r--  1 telemachus  wheel     0 Aug 21 07:19 hello world
-rw-r--r--  1 telemachus  wheel   124 Aug 21 09:32 main.go
-rw-r--r--  1 telemachus  wheel     0 Aug 21 09:32 world

I would expect (hope?) that script would sanitize the filenames, so that touch ran once with each filename (including the ones with tricky characters).

Instead, here are some things that go wrong (I think).

  1. touch seems to receive the argument Bobby Tables twice. In a way, this is a good result: a single name with a space! On the other hand, all quotes seem to be stripped away from the two original filenames, one of which has single quotes and one of which has double quotes. If you change the code to script.ListFiles("*").ExecForEach("ls {{.}}").Stdout(), you'll see ls: Bobby Tables: No such file or directory (without quotes of any kind) twice in the output. These results suggest that something script does (the templating, perhaps?) removes quotations from filenames.
  2. ExecForEach seems to split the filename hello world into two arguments so that the program runs touch hello and touch world.

Normally, in actual shell scripting, you could fix problems like these with proper quoting.

for file in *
do
	touch "$file"
done

But I can't see any way to do the equivalent using script. Ideally, script itself would handle this for the user. (E.g., to protect users who don't know about such dangers from themselves.)

I noodled around with this a bit last night using https://bitbucket.org/creachadair/shell and https://github.com/mvdan/sh, but nothing I did helped. I'd be interested in working on this, but at the moment I can't see a way forward.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions