diff --git a/src/uu/lsmem/src/lsmem.rs b/src/uu/lsmem/src/lsmem.rs
index c0b051e..2e6bd72 100644
--- a/src/uu/lsmem/src/lsmem.rs
+++ b/src/uu/lsmem/src/lsmem.rs
@@ -5,7 +5,8 @@
 
 mod utils;
 
-use clap::{crate_version, Command};
+use clap::builder::{EnumValueParser, PossibleValue, PossibleValuesParser};
+use clap::{crate_version, Command, ValueEnum};
 use clap::{Arg, ArgAction};
 use serde::{Deserialize, Serialize};
 use std::borrow::Borrow;
@@ -21,10 +22,13 @@ const USAGE: &str = help_usage!("lsmem.md");
 mod options {
     pub const ALL: &str = "all";
     pub const BYTES: &str = "bytes";
-    pub const NOHEADINGS: &str = "noheadings";
     pub const JSON: &str = "json";
+    pub const NOHEADINGS: &str = "noheadings";
+    pub const OUTPUT: &str = "output";
+    pub const OUTPUT_ALL: &str = "output-all";
     pub const PAIRS: &str = "pairs";
     pub const RAW: &str = "raw";
+    pub const SPLIT: &str = "split";
 }
 
 // const BUFSIZ: usize = 1024;
@@ -36,8 +40,8 @@ const PATH_SUB_REMOVABLE: &str = "removable";
 const PATH_SUB_STATE: &str = "state";
 const NAME_MEMORY: &str = "memory";
 
-#[derive(Debug, PartialEq, Serialize, Deserialize)]
-enum Columns {
+#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
+enum Column {
     #[serde(rename = "RANGE")]
     Range,
     #[serde(rename = "SIZE")]
@@ -54,55 +58,84 @@ enum Columns {
     Zones,
 }
 
-const ALL_COLUMNS: &[Columns] = &[
-    Columns::Range,
-    Columns::Size,
-    Columns::State,
-    Columns::Removable,
-    Columns::Block,
-    Columns::Node,
-    Columns::Zones,
-];
-
-impl Columns {
-    fn get_name(&self) -> String {
-        serde_json::to_string(self)
-            .unwrap()
-            .trim_matches('"')
-            .to_string()
+impl ValueEnum for Column {
+    fn value_variants<'a>() -> &'a [Self] {
+        &[
+            Column::Range,
+            Column::Size,
+            Column::State,
+            Column::Removable,
+            Column::Block,
+            Column::Node,
+            Column::Zones,
+        ]
     }
 
-    #[allow(dead_code)]
-    fn get_float_direction(&self) -> &'static str {
+    fn to_possible_value(&self) -> Option<PossibleValue> {
+        Some(PossibleValue::new(self.get_name()))
+    }
+}
+
+/// Default columns to display if none are explicitly specified.
+const DEFAULT_COLUMNS: &[Column] = &[
+    Column::Range,
+    Column::Size,
+    Column::State,
+    Column::Removable,
+    Column::Block,
+];
+/// Which columns (attributes) are possible to split memory blocks to ranges on.
+const SPLIT_COLUMNS: &[Column] = &[
+    Column::State,
+    Column::Removable,
+    Column::Node,
+    Column::Zones,
+];
+
+impl Column {
+    fn get_name(&self) -> &'static str {
         match self {
-            Columns::Range => "<",
-            Columns::Size => ">",
-            Columns::State => ">",
-            Columns::Removable => ">",
-            Columns::Block => ">",
-            Columns::Node => ">",
-            Columns::Zones => ">",
+            Column::Range => "RANGE",
+            Column::Size => "SIZE",
+            Column::State => "STATE",
+            Column::Removable => "REMOVABLE",
+            Column::Block => "BLOCK",
+            Column::Node => "NODE",
+            Column::Zones => "ZONES",
         }
     }
 
     #[allow(dead_code)]
-    fn get_width_hint(&self) -> i8 {
-        if self == &Columns::Size {
+    fn get_float_right(&self) -> bool {
+        match self {
+            Column::Range => false,
+            Column::Size => true,
+            Column::State => true,
+            Column::Removable => true,
+            Column::Block => true,
+            Column::Node => true,
+            Column::Zones => true,
+        }
+    }
+
+    #[allow(dead_code)]
+    fn get_width_hint(&self) -> usize {
+        if self == &Column::Size {
             5
         } else {
-            self.get_name().len() as i8
+            self.get_name().len()
         }
     }
 
     fn get_help(&self) -> &'static str {
         match self {
-            Columns::Range => "start and end address of the memory range",
-            Columns::Size => "size of the memory range",
-            Columns::State => "online status of the memory range",
-            Columns::Removable => "memory is removable",
-            Columns::Block => "memory block number or blocks range",
-            Columns::Node => "numa node of memory",
-            Columns::Zones => "valid zones for the memory range",
+            Column::Range => "start and end address of the memory range",
+            Column::Size => "size of the memory range",
+            Column::State => "online status of the memory range",
+            Column::Removable => "memory is removable",
+            Column::Block => "memory block number or blocks range",
+            Column::Node => "numa node of memory",
+            Column::Zones => "valid zones for the memory range",
         }
     }
 }
@@ -200,6 +233,17 @@ struct TableRow {
 }
 
 impl TableRow {
+    fn get_value(&self, column: &Column) -> String {
+        match column {
+            Column::Range => self.range.clone(),
+            Column::Size => self.size.clone(),
+            Column::State => self.state.clone(),
+            Column::Removable => self.removable.clone(),
+            Column::Block => self.block.clone(),
+            Column::Node => self.node.clone(),
+            Column::Zones => self.zones.clone(),
+        }
+    }
     fn to_pairs_string(&self) -> String {
         format!(
             r#"RANGE="{}" SIZE="{}" STATE="{}" REMOVABLE="{}" BLOCK="{}""#,
@@ -220,19 +264,24 @@ struct TableRowJson {
 }
 
 struct Options {
+    // Set by command-line arguments
     all: bool,
     bytes: bool,
-    export: bool,
-    have_nodes: bool,
-    have_zones: bool,
-    json: bool,
-    list_all: bool,
+    columns: Vec<Column>,
     noheadings: bool,
+    json: bool,
+    pairs: bool,
     raw: bool,
     split_by_node: bool,
     split_by_removable: bool,
     split_by_state: bool,
     split_by_zones: bool,
+
+    // Set by read_info
+    have_nodes: bool,
+    have_zones: bool,
+
+    // Computed from flags above
     want_summary: bool,
     want_table: bool,
 }
@@ -265,20 +314,22 @@ impl Options {
     fn new() -> Options {
         Options {
             all: false,
-            have_nodes: false,
-            raw: false,
-            export: false,
-            json: false,
-            noheadings: false,
-            list_all: false,
             bytes: false,
+            columns: Vec::default(),
+            noheadings: false,
+            json: false,
+            pairs: false,
+            raw: false,
+            split_by_node: false,
+            split_by_removable: false,
+            split_by_state: false,
+            split_by_zones: false,
+
+            have_nodes: false,
+            have_zones: false,
+
             want_summary: true, // default true
             want_table: true,   // default true
-            split_by_node: false,
-            split_by_state: false,
-            split_by_removable: false,
-            split_by_zones: false,
-            have_zones: false,
         }
     }
 }
@@ -321,7 +372,7 @@ fn read_info(lsmem: &mut Lsmem, opts: &mut Options) {
         } else {
             lsmem.mem_offline += lsmem.block_size;
         }
-        if !opts.all && is_mergeable(lsmem, opts, &blk) {
+        if is_mergeable(lsmem, opts, &blk) {
             lsmem.blocks[lsmem.nblocks - 1].count += 1;
             continue;
         }
@@ -349,7 +400,7 @@ fn is_mergeable(lsmem: &Lsmem, opts: &Options, blk: &MemoryBlock) -> bool {
     }
 
     let curr_block = &lsmem.blocks[lsmem.nblocks - 1];
-    if opts.list_all {
+    if opts.all {
         return false;
     }
     if curr_block.index + curr_block.count != blk.index {
@@ -482,44 +533,53 @@ fn create_table_rows(lsmem: &Lsmem, opts: &Options) -> Vec<TableRow> {
 
 fn print_table(lsmem: &Lsmem, opts: &Options) {
     let table_rows = create_table_rows(lsmem, opts);
-    let mut col_widths = vec![5, 5, 6, 9, 5]; // Initialize with default minimum widths
+    let mut col_widths = vec![0; opts.columns.len()];
+
+    // Initialize column widths based on column names
+    for (i, column) in opts.columns.iter().enumerate() {
+        col_widths[i] = column.get_width_hint();
+    }
 
     // Calculate minimum column widths based on the actual data
     for row in &table_rows {
-        col_widths[0] = col_widths[0].max(row.range.len());
-        col_widths[1] = col_widths[1].max(row.size.len());
-        col_widths[2] = col_widths[2].max(row.state.len());
-        col_widths[3] = col_widths[3].max(row.removable.len());
-        col_widths[4] = col_widths[4].max(row.block.len());
+        for (i, column) in opts.columns.iter().enumerate() {
+            let width = match column {
+                Column::Range => row.range.len(),
+                Column::Size => row.size.len(),
+                Column::State => row.state.len(),
+                Column::Removable => row.removable.len(),
+                Column::Block => row.block.len(),
+                Column::Node => row.node.len(),
+                Column::Zones => row.zones.len(),
+            };
+            col_widths[i] = col_widths[i].max(width);
+        }
     }
 
     if !opts.noheadings {
-        println!(
-            "{:<col0$} {:>col1$} {:>col2$} {:>col3$} {:>col4$}",
-            "RANGE",
-            "SIZE",
-            "STATE",
-            "REMOVABLE",
-            "BLOCK",
-            col0 = col_widths[0],
-            col1 = col_widths[1],
-            col2 = col_widths[2],
-            col3 = col_widths[3],
-            col4 = col_widths[4],
-        );
+        let mut output = vec![];
+        for (i, column) in opts.columns.iter().enumerate() {
+            let formatted = if column.get_float_right() {
+                format!("{:>width$}", column.get_name(), width = col_widths[i])
+            } else {
+                format!("{:<width$}", column.get_name(), width = col_widths[i])
+            };
+            output.push(formatted);
+        }
+        println!("{}", output.join(" "));
     }
 
     for row in table_rows {
-        let mut columns = vec![];
-        columns.push(format!("{:<col0$}", row.range, col0 = col_widths[0]));
-        columns.push(format!("{:>col1$}", row.size, col1 = col_widths[1]));
-        columns.push(format!("{:>col2$}", row.state, col2 = col_widths[2]));
-        columns.push(format!("{:>col3$}", row.removable, col3 = col_widths[3]));
-        columns.push(format!("{:>col4$}", row.block, col4 = col_widths[4]));
-        // Default version skips NODE and ZONES
-        // columns.push(format!("{:>col5$}", row.node, col5 = col_widths[5]));
-        // columns.push(format!("{:>col6$}", row.zones, col6 = col_widths[6]));
-        println!("{}", columns.join(" "));
+        let mut output = vec![];
+        for (i, column) in opts.columns.iter().enumerate() {
+            let formatted = if column.get_float_right() {
+                format!("{:>width$}", row.get_value(column), width = col_widths[i])
+            } else {
+                format!("{:<width$}", row.get_value(column), width = col_widths[i])
+            };
+            output.push(formatted);
+        }
+        println!("{}", output.join(" "));
     }
 }
 
@@ -597,10 +657,35 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
     opts.bytes = matches.get_flag(options::BYTES);
     opts.noheadings = matches.get_flag(options::NOHEADINGS);
     opts.json = matches.get_flag(options::JSON);
-    opts.export = matches.get_flag(options::PAIRS);
+    opts.pairs = matches.get_flag(options::PAIRS);
     opts.raw = matches.get_flag(options::RAW);
+    opts.columns = matches
+        .get_many::<Column>(options::OUTPUT)
+        .unwrap_or_default()
+        .map(|c| c.to_owned())
+        .collect::<Vec<Column>>();
 
-    if opts.json || opts.export || opts.raw {
+    // Only respect --output-all if no column list were provided.
+    // --output takes priority over --output-all.
+    if opts.columns.is_empty() {
+        if matches.get_flag(options::OUTPUT_ALL) {
+            opts.columns = Column::value_variants().to_vec();
+        } else {
+            opts.columns = DEFAULT_COLUMNS.to_vec();
+        }
+    }
+
+    let split_columns = matches
+        .get_many::<String>(options::SPLIT)
+        .unwrap_or_default()
+        .map(|c| c.to_owned())
+        .collect::<Vec<String>>();
+    opts.split_by_node = split_columns.contains(&Column::Node.get_name().to_string());
+    opts.split_by_removable = split_columns.contains(&Column::Removable.get_name().to_string());
+    opts.split_by_state = split_columns.contains(&Column::State.get_name().to_string());
+    opts.split_by_zones = split_columns.contains(&Column::Zones.get_name().to_string());
+
+    if opts.json || opts.pairs || opts.raw {
         opts.want_summary = false;
     }
 
@@ -609,7 +694,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
     if opts.want_table {
         if opts.json {
             print_json(&lsmem, &opts);
-        } else if opts.export {
+        } else if opts.pairs {
             print_pairs(&lsmem, &opts);
         } else if opts.raw {
             print_raw(&lsmem, &opts);
@@ -636,27 +721,6 @@ pub fn uu_app() -> Command {
         .about(ABOUT)
         .override_usage(format_usage(USAGE))
         .infer_long_args(true)
-        .arg(
-            Arg::new(options::ALL)
-                .short('a')
-                .long("all")
-                .help("list each individual memory block")
-                .action(ArgAction::SetTrue),
-        )
-        .arg(
-            Arg::new(options::BYTES)
-                .short('b')
-                .long("bytes")
-                .help("print SIZE in bytes rather than in human readable format")
-                .action(ArgAction::SetTrue),
-        )
-        .arg(
-            Arg::new(options::NOHEADINGS)
-                .short('n')
-                .long("noheadings")
-                .help("don't print headings")
-                .action(ArgAction::SetTrue),
-        )
         .arg(
             Arg::new(options::JSON)
                 .short('J')
@@ -673,6 +737,44 @@ pub fn uu_app() -> Command {
                 .action(ArgAction::SetTrue)
                 .conflicts_with_all([options::JSON, options::RAW]),
         )
+        .arg(
+            Arg::new(options::ALL)
+                .short('a')
+                .long("all")
+                .help("list each individual memory block")
+                .action(ArgAction::SetTrue)
+                .conflicts_with(options::SPLIT),
+        )
+        .arg(
+            Arg::new(options::BYTES)
+                .short('b')
+                .long("bytes")
+                .help("print SIZE in bytes rather than in human readable format")
+                .action(ArgAction::SetTrue),
+        )
+        .arg(
+            Arg::new(options::NOHEADINGS)
+                .short('n')
+                .long("noheadings")
+                .help("don't print headings")
+                .action(ArgAction::SetTrue),
+        )
+        .arg(
+            Arg::new(options::OUTPUT)
+                .short('o')
+                .long("output")
+                .help("output columns")
+                .ignore_case(true)
+                .action(ArgAction::Set)
+                .value_delimiter(',')
+                .value_parser(EnumValueParser::<Column>::new()),
+        )
+        .arg(
+            Arg::new(options::OUTPUT_ALL)
+                .long("output-all")
+                .help("output all columns")
+                .action(ArgAction::SetTrue),
+        )
         .arg(
             Arg::new(options::RAW)
                 .short('r')
@@ -681,9 +783,25 @@ pub fn uu_app() -> Command {
                 .action(ArgAction::SetTrue)
                 .conflicts_with_all([options::JSON, options::PAIRS]),
         )
+        .arg(
+            Arg::new(options::SPLIT)
+                .short('S')
+                .long("split")
+                .help("split ranges by specified columns")
+                .conflicts_with(options::ALL)
+                .ignore_case(true)
+                .action(ArgAction::Set)
+                .value_delimiter(',')
+                .value_parser(PossibleValuesParser::new(
+                    SPLIT_COLUMNS
+                        .iter()
+                        .map(|col| col.to_possible_value().unwrap())
+                        .collect::<Vec<_>>(),
+                )),
+        )
         .after_help(&format!(
             "Available output columns:\n{}",
-            ALL_COLUMNS
+            Column::value_variants()
                 .iter()
                 .map(|col| format!("{:>11}  {}", col.get_name(), col.get_help()))
                 .collect::<Vec<_>>()