Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion brush-builtins/src/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ use brush_core::{
ExecutionExitCode, ExecutionResult, builtins,
env::{EnvironmentLookup, EnvironmentScope},
parser::ast,
variables,
variables::{self, ShellValue, ShellValueUnsetType, ShellVariable},
};

/// Add or update exported shell variables.
#[derive(Parser)]
pub(crate) struct ExportCommand {
/// Mark names as indexed arrays (combined with the export attribute).
#[arg(short = 'a')]
make_indexed_array: bool,

/// Names are treated as function names.
#[arg(short = 'f')]
names_are_functions: bool,
Expand Down Expand Up @@ -88,12 +92,30 @@ impl ExportCommand {
// Try to find the variable already present; if we find it, then mark it
// exported.
else if let Some((_, variable)) = context.shell.env_mut().get_mut(s) {
if self.make_indexed_array {
variable.convert_to_indexed_array()?;
}
if self.unexport {
variable.unexport();
} else {
variable.export();
}
}
// If `-a` was passed and the name doesn't yet exist, create it as an unset
// indexed array with the export attribute (mirrors `declare -ax NAME`). This
// is what bash does for `export -a NAME` and what `mise activate bash` relies
// on when seeding `chpwd_functions`.
else if self.make_indexed_array {
let mut var =
ShellVariable::new(ShellValue::Unset(ShellValueUnsetType::IndexedArray));
if !self.unexport {
var.export();
}
context
.shell
.env_mut()
.add(s.clone(), var, EnvironmentScope::Global)?;
}
Comment on lines +104 to +118
}
brush_core::CommandArg::Assignment(assignment) => {
let name = match &assignment.name {
Expand All @@ -118,10 +140,16 @@ impl ExportCommand {
};

// Update the variable with the provided value and then mark it exported.
// When `-a` is set, ensure the variable is converted to an indexed array
// before the assignment lands (so a scalar assignment like `export -a foo=x`
// ends up as `foo=([0]="x")`, matching bash's `declare -ax foo=([0]="x")`).
Comment on lines +143 to +145
context.shell.env_mut().update_or_add(
name,
value,
|var| {
if self.make_indexed_array {
var.convert_to_indexed_array()?;
}
if self.unexport {
var.unexport();
} else {
Expand Down
30 changes: 30 additions & 0 deletions brush-shell/tests/cases/compat/builtins/export.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,33 @@ cases:
export ref="exported_value"
echo "MY_EXPORT_VAR: $MY_EXPORT_VAR"
env | grep MY_EXPORT_VAR

- name: "export -a marks indexed array and exports (with array element assignment)"
stdin: |
export -a arr
arr[0]=hello
arr[1]=world
declare -p arr

- name: "export -a with scalar assignment auto-promotes to array"
stdin: |
export -a arr=hello
declare -p arr

- name: "export -a with array literal assignment"
stdin: |
export -a arr=(a b c)
declare -p arr

- name: "export -a converts an existing scalar to an indexed array"
stdin: |
foo=hello
export -a foo
foo[1]=world
declare -p foo

- name: "export -an unmarks an indexed array's export attribute"
stdin: |
export -a arr=(x y z)
export -n arr
declare -p arr
Loading