Commit 08ef572e08665ec3a06ea6ac315f3dd6d0eac5d1

Parents: 1db74a9ba74350776dee5f2384744357ad3aace5

From: Robin Jarry <robin@jarry.cc>
Date: Sun Sep 24 21:42:59 2023 +0700

pipe: run commands with sh -c
Change the Cmd argument to a plain string that preserves shell quoting.
Use this for sh -c instead of a list of arguments.

Changelog-changed: `:pipe` commands are now executed with `sh -c`.
Requested-by: Vitaly Ovchinnikov <v@postbox.nz>
Signed-off-by: Robin Jarry <robin@jarry.cc>
Reviewed-by: Koni Marti <koni.marti@gmail.com>
Tested-by: Moritz Poldrack <moritz@poldrack.dev>
Tested-by: Inwit <inwit@sindominio.net>

Stats

CHANGELOG.md +1/-0
commands/msg/pipe.go +14/-11
doc/aerc.1.scd +4/-4

Changeset

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4551909e9c44a868ce22021ca75ba5cacd9923d3..8426331e0c38b0d9c5f284c5d9ff6688416d3ef0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -36,6 +36,7 @@ - `:archive` now works on servers using a different delimiter
 - `:save -a` now works with multiple attachments with the same filename
 - `:open` uses the attachment extension for temporary files, if possible
 - memory leak when using notmuch with threading
+- `:pipe <cmd>` now executes `sh -c "<cmd>"` as indicated in the man page.
 
 ### Changed
 
diff --git a/commands/msg/pipe.go b/commands/msg/pipe.go
index 75b63b10698c34a33665d09f3ba4704171d6f06b..b984752bafa2122012e9f121e92e198428b38055 100644
--- a/commands/msg/pipe.go
+++ b/commands/msg/pipe.go
@@ -7,6 +7,7 @@ 	"io"
 	"os/exec"
 	"regexp"
 	"sort"
+	"strings"
 	"time"
 
 	"git.sr.ht/~rjarry/aerc/app"
@@ -17,10 +18,10 @@ 	"git.sr.ht/~rjarry/aerc/worker/types"
 )
 
 type Pipe struct {
-	Background bool     `opt:"-b"`
-	Full       bool     `opt:"-m"`
-	Part       bool     `opt:"-p"`
-	Command    []string `opt:"..."`
+	Background bool   `opt:"-b"`
+	Full       bool   `opt:"-m"`
+	Part       bool   `opt:"-p"`
+	Command    string `opt:"..."`
 }
 
 func init() {
@@ -39,6 +40,7 @@ func (p Pipe) Execute(args []string) error {
 	if p.Full && p.Part {
 		return errors.New("-m and -p are mutually exclusive")
 	}
+	name, _, _ := strings.Cut(p.Command, " ")
 
 	provider := app.SelectedTabContent().(app.ProvidesMessage)
 	if !p.Full && !p.Part {
@@ -53,7 +55,8 @@ 		}
 	}
 
 	doTerm := func(reader io.Reader, name string) {
-		term, err := commands.QuickTerm(p.Command, reader)
+		cmd := []string{"sh", "-c", p.Command}
+		term, err := commands.QuickTerm(cmd, reader)
 		if err != nil {
 			app.PushError(err.Error())
 			return
@@ -62,7 +65,7 @@ 		app.NewTab(term, name)
 	}
 
 	doExec := func(reader io.Reader) {
-		ecmd := exec.Command(p.Command[0], p.Command[1:]...)
+		ecmd := exec.Command("sh", "-c", p.Command)
 		pipe, err := ecmd.StdinPipe()
 		if err != nil {
 			return
@@ -82,11 +85,11 @@ 			app.PushError(err.Error())
 		} else {
 			if ecmd.ProcessState.ExitCode() != 0 {
 				app.PushError(fmt.Sprintf(
-					"%s: completed with status %d", p.Command[0],
+					"%s: completed with status %d", name,
 					ecmd.ProcessState.ExitCode()))
 			} else {
 				app.PushStatus(fmt.Sprintf(
-					"%s: completed with status %d", p.Command[0],
+					"%s: completed with status %d", name,
 					ecmd.ProcessState.ExitCode()), 10*time.Second)
 			}
 		}
@@ -108,7 +111,7 @@ 						doExec(reader)
 					} else {
 						doTerm(reader,
 							fmt.Sprintf("%s <%s",
-								p.Command[0], title))
+								name, title))
 					}
 				})
 				return nil
@@ -183,7 +186,7 @@ 			reader := newMessagesReader(messages, len(messages) > 1)
 			if p.Background {
 				doExec(reader)
 			} else {
-				doTerm(reader, fmt.Sprintf("%s <%s", p.Command[0], title))
+				doTerm(reader, fmt.Sprintf("%s <%s", name, title))
 			}
 		}()
 	} else if p.Part {
@@ -200,7 +203,7 @@ 			if p.Background {
 				doExec(reader)
 			} else {
 				name := fmt.Sprintf("%s <%s/[%d]",
-					p.Command[0], part.Msg.Envelope.Subject, part.Index)
+					name, part.Msg.Envelope.Subject, part.Index)
 				doTerm(reader, name)
 			}
 		})
diff --git a/doc/aerc.1.scd b/doc/aerc.1.scd
index 2c49948fa470cf14aecc02b66856fc172e3ff161..2d53dca33ad8d043b5c26217b38f8fbb8845427f 100644
--- a/doc/aerc.1.scd
+++ b/doc/aerc.1.scd
@@ -277,10 +277,10 @@
 	*-p*: Create the _<target>_ folder if it does not exist.
 
 *:pipe* [*-bmp*] _<cmd>_
-	Downloads and pipes the selected message into the given shell command, and
-	opens a new terminal tab to show the result. By default, the selected
-	message part is used in the message viewer and the full message is used in
-	the message list.
+	Downloads and pipes the selected message into the given shell command
+	(executed with _sh -c "<cmd>"_), and opens a new terminal tab to show
+	the result. By default, the selected message part is used in the message
+	viewer and the full message is used in the message list.
 
 	Operates on multiple messages when they are marked. When piping multiple
 	messages, aerc will write them with mbox format separators.