@@ -14,6 +14,7 @@ use std::rc::Rc;
1414use std:: rc:: Weak ;
1515
1616use anyhow:: Result ;
17+ use bitflags:: bitflags;
1718use futures:: future:: LocalBoxFuture ;
1819use tokio:: sync:: broadcast;
1920use tokio:: task:: JoinHandle ;
@@ -23,6 +24,27 @@ use crate::shell::child_process_tracker::ChildProcessTracker;
2324use super :: commands:: ShellCommand ;
2425use super :: commands:: builtin_commands;
2526
27+ bitflags ! {
28+ /// Shell options that can be set via `shopt`.
29+ #[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
30+ pub struct ShellOptions : u32 {
31+ /// When set, a glob pattern that matches no files expands to nothing
32+ /// (empty) rather than returning an error.
33+ const NULLGLOB = 1 << 0 ;
34+ /// When set, a glob pattern that matches no files causes an error.
35+ /// This is the default for deno_task_shell (differs from bash).
36+ /// When unset, unmatched globs are passed through literally (bash default).
37+ const FAILGLOB = 1 << 1 ;
38+ }
39+ }
40+
41+ impl Default for ShellOptions {
42+ fn default ( ) -> Self {
43+ // failglob is on by default to preserve existing deno_task_shell behavior
44+ ShellOptions :: FAILGLOB
45+ }
46+ }
47+
2648/// Exit code set when an async task fails or the main execution
2749/// line fail.
2850#[ derive( Debug , Default , Clone ) ]
@@ -57,6 +79,8 @@ pub struct ShellState {
5779 kill_signal : KillSignal ,
5880 process_tracker : ChildProcessTracker ,
5981 tree_exit_code_cell : TreeExitCodeCell ,
82+ /// Shell options set via `shopt`.
83+ shell_options : ShellOptions ,
6084}
6185
6286impl ShellState {
@@ -77,6 +101,7 @@ impl ShellState {
77101 kill_signal,
78102 process_tracker : ChildProcessTracker :: new ( ) ,
79103 tree_exit_code_cell : Default :: default ( ) ,
104+ shell_options : ShellOptions :: default ( ) ,
80105 } ;
81106 // ensure the data is normalized
82107 for ( name, value) in env_vars {
@@ -138,6 +163,9 @@ impl ShellState {
138163 EnvChange :: Cd ( new_dir) => {
139164 self . set_cwd ( new_dir. clone ( ) ) ;
140165 }
166+ EnvChange :: SetShellOption ( option, enabled) => {
167+ self . set_shell_option ( * option, * enabled) ;
168+ }
141169 }
142170 }
143171
@@ -169,6 +197,18 @@ impl ShellState {
169197 & self . kill_signal
170198 }
171199
200+ pub fn shell_options ( & self ) -> ShellOptions {
201+ self . shell_options
202+ }
203+
204+ pub fn set_shell_option ( & mut self , option : ShellOptions , enabled : bool ) {
205+ if enabled {
206+ self . shell_options . insert ( option) ;
207+ } else {
208+ self . shell_options . remove ( option) ;
209+ }
210+ }
211+
172212 pub fn track_child_process ( & self , child : & tokio:: process:: Child ) {
173213 self . process_tracker . track ( child) ;
174214 }
@@ -213,7 +253,7 @@ impl sys_traits::BaseEnvVar for ShellState {
213253 }
214254}
215255
216- #[ derive( Debug , PartialEq , Eq ) ]
256+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
217257pub enum EnvChange {
218258 // `export ENV_VAR=VALUE`
219259 SetEnvVar ( OsString , OsString ) ,
@@ -222,6 +262,8 @@ pub enum EnvChange {
222262 // `unset ENV_VAR`
223263 UnsetVar ( OsString ) ,
224264 Cd ( PathBuf ) ,
265+ // `shopt -s/-u option`
266+ SetShellOption ( ShellOptions , bool ) ,
225267}
226268
227269pub type FutureExecuteResult = LocalBoxFuture < ' static , ExecuteResult > ;
0 commit comments