diff --git a/brush-core/src/shell.rs b/brush-core/src/shell.rs index a8f33c1d..460474ff 100644 --- a/brush-core/src/shell.rs +++ b/brush-core/src/shell.rs @@ -79,11 +79,17 @@ pub struct Shell { impl Clone for Shell { fn clone(&self) -> Self { + let mut env = self.env.clone(); + + if !self.options.sh_mode { + increment_level_var(&mut env, "BASH_SUBSHELL", 1, false).unwrap(); + } + Self { traps: self.traps.clone(), open_files: self.open_files.clone(), working_dir: self.working_dir.clone(), - env: self.env.clone(), + env, funcs: self.funcs.clone(), options: self.options.clone(), jobs: jobs::JobManager::new(), @@ -279,7 +285,13 @@ impl Shell { )?; } + increment_level_var(&mut env, "SHLVL", 1, true)?; + if !options.sh_mode { + let mut lvl = ShellVariable::new("0".into()); + lvl.export(); + env.set_global("BASH_SUBSHELL", lvl)?; + if let Some(shell_name) = &options.shell_name { env.set_global("BASH", ShellVariable::new(shell_name.into()))?; } @@ -1182,3 +1194,21 @@ fn parse_string_impl( tracing::debug!(target: trace_categories::PARSE, "Parsing string as program..."); parser.parse(true) } + +// parse a numeric variable from a string, increment it by 1, then re-export +fn increment_level_var( + env: &mut ShellEnvironment, + var: &str, + default: usize, + export: bool, +) -> Result<(), error::Error> { + let lvl = env + .get_str(var) + .and_then(|s| s.parse::().ok().map(|n| n + 1)) + .unwrap_or(default); + let mut lvl = ShellVariable::new(ShellValue::String(lvl.to_string())); + if export { + lvl.export(); + } + env.set_global(var, lvl) +} diff --git a/brush-shell/tests/cases/compound_cmds/subshell.yaml b/brush-shell/tests/cases/compound_cmds/subshell.yaml index f202e1eb..ff2181b4 100644 --- a/brush-shell/tests/cases/compound_cmds/subshell.yaml +++ b/brush-shell/tests/cases/compound_cmds/subshell.yaml @@ -23,3 +23,37 @@ cases: - name: "Piped subshell usage" stdin: | (echo hi) | wc -l + + - name: "SHLVL subshell" + stdin: | + initial="$SHLVL" + function rec_subshell { + if [ "$1" -ge "$2" ]; then + exit + else + ( + test $initial -eq $SHLVL; echo $? + rec_subshell $(($1 + 1)) $2 + ) + fi + } + rec_subshell 1 10 + + + - name: "BASH_SUBSHELL" + stdin: | + initial="$BASH_SUBSHELL" + + function rec_subshell { + if [ "$1" -ge "$2" ]; then + exit + else + ( + test $initial -ne $BASH_SUBSHELL; echo $? + echo $BASH_SUBSHELL + initial=$BASH_SUBSHELL + rec_subshell $(($1 + 1)) $2 + ) + fi + } + rec_subshell 1 10