mirror of
https://github.com/nushell/nushell.git
synced 2024-11-24 17:34:00 +01:00
Add sys
subcommands (#12747)
# Description Adds subcommands to `sys` corresponding to each column of the record returned by `sys`. This is to alleviate the fact that `sys` now returns a regular record, meaning that it must compute every column which might take a noticeable amount of time. The subcommands, on the other hand, only need to compute and return a subset of the data which should be much faster. In fact, it should be as fast as before, since this is how the lazy record worked (it would compute only each column as necessary). I choose to add subcommands instead of having an optional cell-path parameter on `sys`, since the cell-path parameter would: - increase the code complexity (can access any value at any row or nested column) - prevents discovery with tab-completion - hinders type checking and allows users to pass potentially invalid columns # User-Facing Changes Deprecates `sys` in favor of the new `sys` subcommands.
This commit is contained in:
parent
68adc4657f
commit
1038c64f80
@ -119,6 +119,12 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
|||||||
Exec,
|
Exec,
|
||||||
NuCheck,
|
NuCheck,
|
||||||
Sys,
|
Sys,
|
||||||
|
SysCpu,
|
||||||
|
SysDisks,
|
||||||
|
SysHost,
|
||||||
|
SysMem,
|
||||||
|
SysNet,
|
||||||
|
SysTemp,
|
||||||
UName,
|
UName,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -124,11 +124,6 @@ If multiple cell paths are given, this will produce a list of values."#
|
|||||||
example: "ls | get 2.name",
|
example: "ls | get 2.name",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
|
||||||
description: "Extract the cpu list from the sys information record",
|
|
||||||
example: "sys | get cpu",
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
Example {
|
Example {
|
||||||
description: "Getting Path/PATH in a case insensitive way",
|
description: "Getting Path/PATH in a case insensitive way",
|
||||||
example: "$env | get paTH",
|
example: "$env | get paTH",
|
||||||
|
@ -66,8 +66,8 @@ Each stage in the pipeline works together to load, parse, and display informatio
|
|||||||
List the files in the current directory, sorted by size:
|
List the files in the current directory, sorted by size:
|
||||||
ls | sort-by size
|
ls | sort-by size
|
||||||
|
|
||||||
Get information about the current system:
|
Get the current system host name:
|
||||||
sys | get host
|
sys host | get hostname
|
||||||
|
|
||||||
Get the processes on your system actively using CPU:
|
Get the processes on your system actively using CPU:
|
||||||
ps | where cpu > 0
|
ps | where cpu > 0
|
||||||
|
@ -30,6 +30,6 @@ pub use ps::Ps;
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub use registry_query::RegistryQuery;
|
pub use registry_query::RegistryQuery;
|
||||||
pub use run_external::{External, ExternalCommand};
|
pub use run_external::{External, ExternalCommand};
|
||||||
pub use sys::Sys;
|
pub use sys::*;
|
||||||
pub use uname::UName;
|
pub use uname::UName;
|
||||||
pub use which_::Which;
|
pub use which_::Which;
|
||||||
|
@ -1,269 +0,0 @@
|
|||||||
use chrono::{DateTime, Local};
|
|
||||||
use nu_engine::command_prelude::*;
|
|
||||||
use std::time::{Duration, UNIX_EPOCH};
|
|
||||||
use sysinfo::{
|
|
||||||
Components, CpuRefreshKind, Disks, Networks, System, Users, MINIMUM_CPU_UPDATE_INTERVAL,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Sys;
|
|
||||||
|
|
||||||
impl Command for Sys {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"sys"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("sys")
|
|
||||||
.filter()
|
|
||||||
.category(Category::System)
|
|
||||||
.input_output_types(vec![(Type::Nothing, Type::record())])
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"View information about the system."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
_engine_state: &EngineState,
|
|
||||||
_stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
Ok(all_columns(call.head).into_pipeline_data())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Show info about the system",
|
|
||||||
example: "sys",
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Show the os system name with get",
|
|
||||||
example: "(sys).host | get name",
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Show the os system name",
|
|
||||||
example: "(sys).host.name",
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct SysResult {
|
|
||||||
pub span: Span,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn all_columns(span: Span) -> Value {
|
|
||||||
Value::record(
|
|
||||||
record! {
|
|
||||||
"host" => host(span),
|
|
||||||
"cpu" => cpu(span),
|
|
||||||
"disks" => disks(span),
|
|
||||||
"mem" => mem(span),
|
|
||||||
"temp" => temp(span),
|
|
||||||
"net" => net(span),
|
|
||||||
},
|
|
||||||
span,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn trim_cstyle_null(s: String) -> String {
|
|
||||||
s.trim_matches(char::from(0)).to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn disks(span: Span) -> Value {
|
|
||||||
let disks = Disks::new_with_refreshed_list();
|
|
||||||
|
|
||||||
let mut output = vec![];
|
|
||||||
for disk in disks.list() {
|
|
||||||
let device = trim_cstyle_null(disk.name().to_string_lossy().to_string());
|
|
||||||
let typ = trim_cstyle_null(disk.file_system().to_string_lossy().to_string());
|
|
||||||
|
|
||||||
let record = record! {
|
|
||||||
"device" => Value::string(device, span),
|
|
||||||
"type" => Value::string(typ, span),
|
|
||||||
"mount" => Value::string(disk.mount_point().to_string_lossy(), span),
|
|
||||||
"total" => Value::filesize(disk.total_space() as i64, span),
|
|
||||||
"free" => Value::filesize(disk.available_space() as i64, span),
|
|
||||||
"removable" => Value::bool(disk.is_removable(), span),
|
|
||||||
"kind" => Value::string(format!("{:?}", disk.kind()), span),
|
|
||||||
};
|
|
||||||
|
|
||||||
output.push(Value::record(record, span));
|
|
||||||
}
|
|
||||||
Value::list(output, span)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn net(span: Span) -> Value {
|
|
||||||
let networks = Networks::new_with_refreshed_list();
|
|
||||||
|
|
||||||
let mut output = vec![];
|
|
||||||
for (iface, data) in networks.list() {
|
|
||||||
let record = record! {
|
|
||||||
"name" => Value::string(trim_cstyle_null(iface.to_string()), span),
|
|
||||||
"sent" => Value::filesize(data.total_transmitted() as i64, span),
|
|
||||||
"recv" => Value::filesize(data.total_received() as i64, span),
|
|
||||||
};
|
|
||||||
|
|
||||||
output.push(Value::record(record, span));
|
|
||||||
}
|
|
||||||
Value::list(output, span)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cpu(span: Span) -> Value {
|
|
||||||
let mut sys = System::new();
|
|
||||||
sys.refresh_cpu_specifics(CpuRefreshKind::everything());
|
|
||||||
// We must refresh the CPU twice a while apart to get valid usage data.
|
|
||||||
// In theory we could just sleep MINIMUM_CPU_UPDATE_INTERVAL, but I've noticed that
|
|
||||||
// that gives poor results (error of ~5%). Decided to wait 2x that long, somewhat arbitrarily
|
|
||||||
std::thread::sleep(MINIMUM_CPU_UPDATE_INTERVAL * 2);
|
|
||||||
sys.refresh_cpu_specifics(CpuRefreshKind::new().with_cpu_usage());
|
|
||||||
|
|
||||||
let mut output = vec![];
|
|
||||||
for cpu in sys.cpus() {
|
|
||||||
// sysinfo CPU usage numbers are not very precise unless you wait a long time between refreshes.
|
|
||||||
// Round to 1DP (chosen somewhat arbitrarily) so people aren't misled by high-precision floats.
|
|
||||||
let rounded_usage = (cpu.cpu_usage() * 10.0).round() / 10.0;
|
|
||||||
|
|
||||||
let load_avg = System::load_average();
|
|
||||||
let load_avg = trim_cstyle_null(format!(
|
|
||||||
"{:.2}, {:.2}, {:.2}",
|
|
||||||
load_avg.one, load_avg.five, load_avg.fifteen
|
|
||||||
));
|
|
||||||
|
|
||||||
let record = record! {
|
|
||||||
"name" => Value::string(trim_cstyle_null(cpu.name().to_string()), span),
|
|
||||||
"brand" => Value::string(trim_cstyle_null(cpu.brand().to_string()), span),
|
|
||||||
"freq" => Value::int(cpu.frequency() as i64, span),
|
|
||||||
"cpu_usage" => Value::float(rounded_usage as f64, span),
|
|
||||||
"load_average" => Value::string(load_avg, span),
|
|
||||||
"vendor_id" => Value::string(trim_cstyle_null(cpu.vendor_id().to_string()), span),
|
|
||||||
};
|
|
||||||
|
|
||||||
output.push(Value::record(record, span));
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::list(output, span)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mem(span: Span) -> Value {
|
|
||||||
let mut sys = System::new();
|
|
||||||
sys.refresh_memory();
|
|
||||||
|
|
||||||
let total_mem = sys.total_memory();
|
|
||||||
let free_mem = sys.free_memory();
|
|
||||||
let used_mem = sys.used_memory();
|
|
||||||
let avail_mem = sys.available_memory();
|
|
||||||
|
|
||||||
let total_swap = sys.total_swap();
|
|
||||||
let free_swap = sys.free_swap();
|
|
||||||
let used_swap = sys.used_swap();
|
|
||||||
|
|
||||||
let record = record! {
|
|
||||||
"total" => Value::filesize(total_mem as i64, span),
|
|
||||||
"free" => Value::filesize(free_mem as i64, span),
|
|
||||||
"used" => Value::filesize(used_mem as i64, span),
|
|
||||||
"available" => Value::filesize(avail_mem as i64, span),
|
|
||||||
"swap total" => Value::filesize(total_swap as i64, span),
|
|
||||||
"swap free" => Value::filesize(free_swap as i64, span),
|
|
||||||
"swap used" => Value::filesize(used_swap as i64, span),
|
|
||||||
};
|
|
||||||
|
|
||||||
Value::record(record, span)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn host(span: Span) -> Value {
|
|
||||||
let mut record = Record::new();
|
|
||||||
|
|
||||||
if let Some(name) = System::name() {
|
|
||||||
record.push("name", Value::string(trim_cstyle_null(name), span));
|
|
||||||
}
|
|
||||||
if let Some(version) = System::os_version() {
|
|
||||||
record.push("os_version", Value::string(trim_cstyle_null(version), span));
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(long_version) = System::long_os_version() {
|
|
||||||
record.push(
|
|
||||||
"long_os_version",
|
|
||||||
Value::string(trim_cstyle_null(long_version), span),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(version) = System::kernel_version() {
|
|
||||||
record.push(
|
|
||||||
"kernel_version",
|
|
||||||
Value::string(trim_cstyle_null(version), span),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some(hostname) = System::host_name() {
|
|
||||||
record.push("hostname", Value::string(trim_cstyle_null(hostname), span));
|
|
||||||
}
|
|
||||||
|
|
||||||
record.push(
|
|
||||||
"uptime",
|
|
||||||
Value::duration(1000000000 * System::uptime() as i64, span),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Creates a new SystemTime from the specified number of whole seconds
|
|
||||||
let d = UNIX_EPOCH + Duration::from_secs(System::boot_time());
|
|
||||||
// Create DateTime from SystemTime
|
|
||||||
let datetime = DateTime::<Local>::from(d);
|
|
||||||
// Convert to local time and then rfc3339
|
|
||||||
let timestamp_str = datetime.with_timezone(datetime.offset()).to_rfc3339();
|
|
||||||
|
|
||||||
record.push("boot_time", Value::string(timestamp_str, span));
|
|
||||||
|
|
||||||
let users = Users::new_with_refreshed_list();
|
|
||||||
|
|
||||||
let mut users_list = vec![];
|
|
||||||
for user in users.list() {
|
|
||||||
let mut groups = vec![];
|
|
||||||
for group in user.groups() {
|
|
||||||
groups.push(Value::string(
|
|
||||||
trim_cstyle_null(group.name().to_string()),
|
|
||||||
span,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let record = record! {
|
|
||||||
"name" => Value::string(trim_cstyle_null(user.name().to_string()), span),
|
|
||||||
"groups" => Value::list(groups, span),
|
|
||||||
};
|
|
||||||
|
|
||||||
users_list.push(Value::record(record, span));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !users.is_empty() {
|
|
||||||
record.push("sessions", Value::list(users_list, span));
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::record(record, span)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn temp(span: Span) -> Value {
|
|
||||||
let components = Components::new_with_refreshed_list();
|
|
||||||
|
|
||||||
let mut output = vec![];
|
|
||||||
|
|
||||||
for component in components.list() {
|
|
||||||
let mut record = record! {
|
|
||||||
"unit" => Value::string(component.label(), span),
|
|
||||||
"temp" => Value::float(component.temperature() as f64, span),
|
|
||||||
"high" => Value::float(component.max() as f64, span),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(critical) = component.critical() {
|
|
||||||
record.push("critical", Value::float(critical as f64, span));
|
|
||||||
}
|
|
||||||
output.push(Value::record(record, span));
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::list(output, span)
|
|
||||||
}
|
|
39
crates/nu-command/src/system/sys/cpu.rs
Normal file
39
crates/nu-command/src/system/sys/cpu.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SysCpu;
|
||||||
|
|
||||||
|
impl Command for SysCpu {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"sys cpu"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("sys cpu")
|
||||||
|
.filter()
|
||||||
|
.category(Category::System)
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::table())])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"View information about the system CPUs."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
Ok(super::cpu(call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Show info about the system CPUs",
|
||||||
|
example: "sys cpu",
|
||||||
|
result: None,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
39
crates/nu-command/src/system/sys/disks.rs
Normal file
39
crates/nu-command/src/system/sys/disks.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SysDisks;
|
||||||
|
|
||||||
|
impl Command for SysDisks {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"sys disks"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("sys disks")
|
||||||
|
.filter()
|
||||||
|
.category(Category::System)
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::table())])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"View information about the system disks."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
Ok(super::disks(call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Show info about the system disks",
|
||||||
|
example: "sys disks",
|
||||||
|
result: None,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
39
crates/nu-command/src/system/sys/host.rs
Normal file
39
crates/nu-command/src/system/sys/host.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SysHost;
|
||||||
|
|
||||||
|
impl Command for SysHost {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"sys host"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("sys host")
|
||||||
|
.filter()
|
||||||
|
.category(Category::System)
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::record())])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"View information about the system host."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
Ok(super::host(call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Show info about the system host",
|
||||||
|
example: "sys host",
|
||||||
|
result: None,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
39
crates/nu-command/src/system/sys/mem.rs
Normal file
39
crates/nu-command/src/system/sys/mem.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SysMem;
|
||||||
|
|
||||||
|
impl Command for SysMem {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"sys mem"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("sys mem")
|
||||||
|
.filter()
|
||||||
|
.category(Category::System)
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::record())])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"View information about the system memory."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
Ok(super::mem(call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Show info about the system memory",
|
||||||
|
example: "sys mem",
|
||||||
|
result: None,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
205
crates/nu-command/src/system/sys/mod.rs
Normal file
205
crates/nu-command/src/system/sys/mod.rs
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
mod cpu;
|
||||||
|
mod disks;
|
||||||
|
mod host;
|
||||||
|
mod mem;
|
||||||
|
mod net;
|
||||||
|
mod sys_;
|
||||||
|
mod temp;
|
||||||
|
|
||||||
|
pub use cpu::SysCpu;
|
||||||
|
pub use disks::SysDisks;
|
||||||
|
pub use host::SysHost;
|
||||||
|
pub use mem::SysMem;
|
||||||
|
pub use net::SysNet;
|
||||||
|
pub use sys_::Sys;
|
||||||
|
pub use temp::SysTemp;
|
||||||
|
|
||||||
|
use chrono::{DateTime, Local};
|
||||||
|
use nu_protocol::{record, Record, Span, Value};
|
||||||
|
use std::time::{Duration, UNIX_EPOCH};
|
||||||
|
use sysinfo::{
|
||||||
|
Components, CpuRefreshKind, Disks, Networks, System, Users, MINIMUM_CPU_UPDATE_INTERVAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn trim_cstyle_null(s: impl AsRef<str>) -> String {
|
||||||
|
s.as_ref().trim_matches('\0').into()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disks(span: Span) -> Value {
|
||||||
|
let disks = Disks::new_with_refreshed_list()
|
||||||
|
.iter()
|
||||||
|
.map(|disk| {
|
||||||
|
let device = trim_cstyle_null(disk.name().to_string_lossy());
|
||||||
|
let typ = trim_cstyle_null(disk.file_system().to_string_lossy());
|
||||||
|
|
||||||
|
let record = record! {
|
||||||
|
"device" => Value::string(device, span),
|
||||||
|
"type" => Value::string(typ, span),
|
||||||
|
"mount" => Value::string(disk.mount_point().to_string_lossy(), span),
|
||||||
|
"total" => Value::filesize(disk.total_space() as i64, span),
|
||||||
|
"free" => Value::filesize(disk.available_space() as i64, span),
|
||||||
|
"removable" => Value::bool(disk.is_removable(), span),
|
||||||
|
"kind" => Value::string(disk.kind().to_string(), span),
|
||||||
|
};
|
||||||
|
|
||||||
|
Value::record(record, span)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Value::list(disks, span)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn net(span: Span) -> Value {
|
||||||
|
let networks = Networks::new_with_refreshed_list()
|
||||||
|
.iter()
|
||||||
|
.map(|(iface, data)| {
|
||||||
|
let record = record! {
|
||||||
|
"name" => Value::string(trim_cstyle_null(iface), span),
|
||||||
|
"sent" => Value::filesize(data.total_transmitted() as i64, span),
|
||||||
|
"recv" => Value::filesize(data.total_received() as i64, span),
|
||||||
|
};
|
||||||
|
|
||||||
|
Value::record(record, span)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Value::list(networks, span)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cpu(span: Span) -> Value {
|
||||||
|
let mut sys = System::new();
|
||||||
|
sys.refresh_cpu_specifics(CpuRefreshKind::everything());
|
||||||
|
// We must refresh the CPU twice a while apart to get valid usage data.
|
||||||
|
// In theory we could just sleep MINIMUM_CPU_UPDATE_INTERVAL, but I've noticed that
|
||||||
|
// that gives poor results (error of ~5%). Decided to wait 2x that long, somewhat arbitrarily
|
||||||
|
std::thread::sleep(MINIMUM_CPU_UPDATE_INTERVAL * 2);
|
||||||
|
sys.refresh_cpu_specifics(CpuRefreshKind::new().with_cpu_usage());
|
||||||
|
|
||||||
|
let cpus = sys
|
||||||
|
.cpus()
|
||||||
|
.iter()
|
||||||
|
.map(|cpu| {
|
||||||
|
// sysinfo CPU usage numbers are not very precise unless you wait a long time between refreshes.
|
||||||
|
// Round to 1DP (chosen somewhat arbitrarily) so people aren't misled by high-precision floats.
|
||||||
|
let rounded_usage = (cpu.cpu_usage() * 10.0).round() / 10.0;
|
||||||
|
|
||||||
|
let load_avg = System::load_average();
|
||||||
|
let load_avg = format!(
|
||||||
|
"{:.2}, {:.2}, {:.2}",
|
||||||
|
load_avg.one, load_avg.five, load_avg.fifteen
|
||||||
|
);
|
||||||
|
|
||||||
|
let record = record! {
|
||||||
|
"name" => Value::string(trim_cstyle_null(cpu.name()), span),
|
||||||
|
"brand" => Value::string(trim_cstyle_null(cpu.brand()), span),
|
||||||
|
"freq" => Value::int(cpu.frequency() as i64, span),
|
||||||
|
"cpu_usage" => Value::float(rounded_usage.into(), span),
|
||||||
|
"load_average" => Value::string(load_avg, span),
|
||||||
|
"vendor_id" => Value::string(trim_cstyle_null(cpu.vendor_id()), span),
|
||||||
|
};
|
||||||
|
|
||||||
|
Value::record(record, span)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Value::list(cpus, span)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mem(span: Span) -> Value {
|
||||||
|
let mut sys = System::new();
|
||||||
|
sys.refresh_memory();
|
||||||
|
|
||||||
|
let record = record! {
|
||||||
|
"total" => Value::filesize(sys.total_memory() as i64, span),
|
||||||
|
"free" => Value::filesize(sys.free_memory() as i64, span),
|
||||||
|
"used" => Value::filesize(sys.used_memory() as i64, span),
|
||||||
|
"available" => Value::filesize(sys.available_memory() as i64, span),
|
||||||
|
"swap total" => Value::filesize(sys.total_swap() as i64, span),
|
||||||
|
"swap free" => Value::filesize(sys.free_swap() as i64, span),
|
||||||
|
"swap used" => Value::filesize(sys.used_swap() as i64, span),
|
||||||
|
};
|
||||||
|
|
||||||
|
Value::record(record, span)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn host(span: Span) -> Value {
|
||||||
|
let mut record = Record::new();
|
||||||
|
|
||||||
|
if let Some(name) = System::name() {
|
||||||
|
record.push("name", Value::string(trim_cstyle_null(name), span));
|
||||||
|
}
|
||||||
|
if let Some(version) = System::os_version() {
|
||||||
|
record.push("os_version", Value::string(trim_cstyle_null(version), span));
|
||||||
|
}
|
||||||
|
if let Some(long_version) = System::long_os_version() {
|
||||||
|
record.push(
|
||||||
|
"long_os_version",
|
||||||
|
Value::string(trim_cstyle_null(long_version), span),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(version) = System::kernel_version() {
|
||||||
|
record.push(
|
||||||
|
"kernel_version",
|
||||||
|
Value::string(trim_cstyle_null(version), span),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if let Some(hostname) = System::host_name() {
|
||||||
|
record.push("hostname", Value::string(trim_cstyle_null(hostname), span));
|
||||||
|
}
|
||||||
|
|
||||||
|
record.push(
|
||||||
|
"uptime",
|
||||||
|
Value::duration(1000000000 * System::uptime() as i64, span),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Creates a new SystemTime from the specified number of whole seconds
|
||||||
|
let d = UNIX_EPOCH + Duration::from_secs(System::boot_time());
|
||||||
|
// Create DateTime from SystemTime
|
||||||
|
let datetime = DateTime::<Local>::from(d);
|
||||||
|
// Convert to local time and then rfc3339
|
||||||
|
let timestamp_str = datetime.with_timezone(datetime.offset()).to_rfc3339();
|
||||||
|
record.push("boot_time", Value::string(timestamp_str, span));
|
||||||
|
|
||||||
|
let users = Users::new_with_refreshed_list()
|
||||||
|
.iter()
|
||||||
|
.map(|user| {
|
||||||
|
let groups = user
|
||||||
|
.groups()
|
||||||
|
.iter()
|
||||||
|
.map(|group| Value::string(trim_cstyle_null(group.name()), span))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let record = record! {
|
||||||
|
"name" => Value::string(trim_cstyle_null(user.name()), span),
|
||||||
|
"groups" => Value::list(groups, span),
|
||||||
|
};
|
||||||
|
|
||||||
|
Value::record(record, span)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
record.push("sessions", Value::list(users, span));
|
||||||
|
|
||||||
|
Value::record(record, span)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn temp(span: Span) -> Value {
|
||||||
|
let components = Components::new_with_refreshed_list()
|
||||||
|
.iter()
|
||||||
|
.map(|component| {
|
||||||
|
let mut record = record! {
|
||||||
|
"unit" => Value::string(component.label(), span),
|
||||||
|
"temp" => Value::float(component.temperature().into(), span),
|
||||||
|
"high" => Value::float(component.max().into(), span),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(critical) = component.critical() {
|
||||||
|
record.push("critical", Value::float(critical.into(), span));
|
||||||
|
}
|
||||||
|
|
||||||
|
Value::record(record, span)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Value::list(components, span)
|
||||||
|
}
|
39
crates/nu-command/src/system/sys/net.rs
Normal file
39
crates/nu-command/src/system/sys/net.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SysNet;
|
||||||
|
|
||||||
|
impl Command for SysNet {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"sys net"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("sys net")
|
||||||
|
.filter()
|
||||||
|
.category(Category::System)
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::table())])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"View information about the system network interfaces."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
Ok(super::net(call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Show info about the system network",
|
||||||
|
example: "sys net",
|
||||||
|
result: None,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
64
crates/nu-command/src/system/sys/sys_.rs
Normal file
64
crates/nu-command/src/system/sys/sys_.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Sys;
|
||||||
|
|
||||||
|
impl Command for Sys {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"sys"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("sys")
|
||||||
|
.filter()
|
||||||
|
.category(Category::System)
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::record())])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"View information about the system."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
"Note that this command may take a noticeable amount of time to run. To reduce the time taken, you can use the various `sys` sub commands to get the subset of information you are interested in."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
nu_protocol::report_error_new(
|
||||||
|
engine_state,
|
||||||
|
&ShellError::GenericError {
|
||||||
|
error: "Deprecated command".into(),
|
||||||
|
msg: "the `sys` command is deprecated, please use the new subcommands (`sys host`, `sys mem`, etc.)."
|
||||||
|
.into(),
|
||||||
|
span: Some(call.head),
|
||||||
|
help: None,
|
||||||
|
inner: vec![],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let head = call.head;
|
||||||
|
let record = record! {
|
||||||
|
"host" => super::host(head),
|
||||||
|
"cpu" => super::cpu(head),
|
||||||
|
"disks" => super::disks(head),
|
||||||
|
"mem" => super::mem(head),
|
||||||
|
"temp" => super::temp(head),
|
||||||
|
"net" => super::net(head),
|
||||||
|
};
|
||||||
|
Ok(Value::record(record, head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Show info about the system",
|
||||||
|
example: "sys",
|
||||||
|
result: None,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
43
crates/nu-command/src/system/sys/temp.rs
Normal file
43
crates/nu-command/src/system/sys/temp.rs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SysTemp;
|
||||||
|
|
||||||
|
impl Command for SysTemp {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"sys temp"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("sys temp")
|
||||||
|
.filter()
|
||||||
|
.category(Category::System)
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::table())])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"View the temperatures of system components."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
"Some system components do not support temperature readings, so this command may return an empty list if no components support temperature."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
Ok(super::temp(call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Show the system temperatures",
|
||||||
|
example: "sys temp",
|
||||||
|
result: None,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
@ -172,7 +172,7 @@ fn select_ignores_errors_successfully2() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn select_ignores_errors_successfully3() {
|
fn select_ignores_errors_successfully3() {
|
||||||
let actual = nu!("sys | select invalid_key? | to nuon");
|
let actual = nu!("{foo: bar} | select invalid_key? | to nuon");
|
||||||
|
|
||||||
assert_eq!(actual.out, "{invalid_key: null}".to_string());
|
assert_eq!(actual.out, "{invalid_key: null}".to_string());
|
||||||
assert!(actual.err.is_empty());
|
assert!(actual.err.is_empty());
|
||||||
|
@ -110,8 +110,8 @@ impl Command for Explore {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Explore the system information record",
|
description: "Explore the system host information record",
|
||||||
example: r#"sys | explore"#,
|
example: r#"sys host | explore"#,
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
|
@ -739,8 +739,8 @@ Each stage in the pipeline works together to load, parse, and display informatio
|
|||||||
List the files in the current directory, sorted by size
|
List the files in the current directory, sorted by size
|
||||||
> ('ls | sort-by size' | nu-highlight)
|
> ('ls | sort-by size' | nu-highlight)
|
||||||
|
|
||||||
Get information about the current system
|
Get the current system host name
|
||||||
> ('sys | get host' | nu-highlight)
|
> ('sys host | get hostname' | nu-highlight)
|
||||||
|
|
||||||
Get the processes on your system actively using CPU
|
Get the processes on your system actively using CPU
|
||||||
> ('ps | where cpu > 0' | nu-highlight)
|
> ('ps | where cpu > 0' | nu-highlight)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std log
|
use std log
|
||||||
|
|
||||||
def "nu-complete threads" [] {
|
def "nu-complete threads" [] {
|
||||||
seq 1 (sys|get cpu|length)
|
seq 1 (sys cpu | length)
|
||||||
}
|
}
|
||||||
|
|
||||||
# Here we store the map of annotations internal names and the annotation actually used during test creation
|
# Here we store the map of annotations internal names and the annotation actually used during test creation
|
||||||
|
Loading…
Reference in New Issue
Block a user