Skip to content

Commit 1fa817f

Browse files
authored
Handle shebang more thoroughly in scripts plugin (#160)
* feat(scripts): allow whitespace after shebang `#!` characters * feat(scripts): allow shebang line to have its own args * fix(scripts): improve unwraps and extra allocations; use SHELL env variable
1 parent 92766af commit 1fa817f

File tree

1 file changed

+39
-5
lines changed

1 file changed

+39
-5
lines changed

plugins/src/scripts/mod.rs

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use pop_launcher::*;
66

77
use flume::Sender;
88
use futures::StreamExt;
9+
use regex::Regex;
910
use std::collections::VecDeque;
1011
use std::path::{Path, PathBuf};
1112
use std::process::Stdio;
@@ -54,11 +55,42 @@ impl App {
5455

5556
async fn activate(&mut self, id: u32) {
5657
if let Some(script) = self.scripts.get(id as usize) {
57-
let interpreter = script.interpreter.as_deref().unwrap_or("sh");
58+
let mut shell: String = Default::default();
59+
let mut args: Vec<&OsStr> = Vec::new();
60+
61+
let program = script
62+
.interpreter
63+
.as_deref()
64+
.and_then(|interpreter| {
65+
// split the shebang into parts, e.g. ["/bin/bash"], or a more complex ["/usr/bin/env", "bash"]
66+
let mut parts = interpreter.split_ascii_whitespace();
67+
68+
// first part must be the command to run, e.g. "/usr/bin/env"
69+
let command = parts.next()?;
70+
71+
for arg in parts {
72+
args.push(arg.as_ref());
73+
}
74+
75+
Some(command)
76+
})
77+
.or_else(|| {
78+
if let Ok(string) = std::env::var("SHELL") {
79+
shell = string;
80+
return Some(&shell);
81+
}
82+
83+
None
84+
})
85+
.unwrap_or("sh");
86+
87+
// add the script file itself as a final arg for the interpreter
88+
args.push(script.path.as_ref());
89+
5890
send(&mut self.out, PluginResponse::Close).await;
5991

60-
let _ = Command::new(interpreter)
61-
.arg(script.path.as_os_str())
92+
let _ = Command::new(program)
93+
.args(args)
6294
.stdin(Stdio::null())
6395
.stdout(Stdio::null())
6496
.stderr(Stdio::null())
@@ -156,6 +188,8 @@ async fn load_from(path: &Path, paths: &mut VecDeque<PathBuf>, tx: Sender<Script
156188
}
157189

158190
tokio::spawn(async move {
191+
let shebang_re = Regex::new(r"^!\s*").unwrap();
192+
159193
let mut file = match tokio::fs::File::open(&path).await {
160194
Ok(file) => tokio::io::BufReader::new(file).lines(),
161195
Err(why) => {
@@ -180,8 +214,8 @@ async fn load_from(path: &Path, paths: &mut VecDeque<PathBuf>, tx: Sender<Script
180214

181215
if first {
182216
first = false;
183-
if let Some(interpreter) = line.strip_prefix('!') {
184-
info.interpreter = Some(interpreter.to_owned());
217+
if shebang_re.is_match(line) {
218+
info.interpreter = Some(shebang_re.replace(line, "").to_string());
185219
continue;
186220
}
187221
}

0 commit comments

Comments
 (0)