Description
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).
touch
seems to receive the argumentBobby 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 toscript.ListFiles("*").ExecForEach("ls {{.}}").Stdout()
, you'll seels: Bobby Tables: No such file or directory
(without quotes of any kind) twice in the output. These results suggest that somethingscript
does (the templating, perhaps?) removes quotations from filenames.ExecForEach
seems to split the filenamehello world
into two arguments so that the program runstouch hello
andtouch 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.