mirror of
https://github.com/nushell/nushell.git
synced 2024-12-22 15:13:01 +01:00
Add mktemp
command (#11005)
closes #10845 I've opened this a little prematurely to get some questions answered before I cleanup the code. As I started trying to better understand GNUs `mktemp` I've realized its kind of peculiar and we might want to change its behavior to introduce it to nushell. #### quiet and dry run Does it make sense to keep the `quiet` and `dry_run` flags? I don't think so. The GNU documentation says this about the dry run flag "Using the output of this command to create a new file is inherently unsafe, as there is a window of time between generating the name and using it where another process can create an object by the same name." So yeah why keep it? As far as quiet goes, does it make sense to silence the errors in nushell? #### other confusing flags According to the [gnu docs](https://www.gnu.org/software/coreutils/manual/html_node/mktemp-invocation.html), the `-t` flag is deprecated and the `-p`/ `--tempdir` are the same flag with the only difference being `--tempdir` takes an optional path, Given that, I've broken the `-p` away from `--tempdir`. Now there is one switch `--tmpdir`/`-t` and one named param `--tmpdir-path`/`-p`. GNU mktemp ``` -p DIR, --tmpdir[=DIR] interpret TEMPLATE relative to DIR; if DIR is not specified, use $TMPDIR if set, else /tmp. With this option, TEMPLATE must not be an absolute name; unlike with -t, TEMPLATE may contain slashes, but mktemp creates only the final component -t interpret TEMPLATE as a single file name component, relative to a directory: $TMPDIR, if set; else the directory specified via -p; else /tmp [deprecated] ``` to nushell mktemp ``` -p, --tmpdir-path <Filepath> # named param, must provide a path -t, --tmpdir # a switch ``` Is this a terrible idea? What should I do? --------- Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
This commit is contained in:
parent
f41c93b2d3
commit
494a5a5286
666
Cargo.lock
generated
666
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -88,9 +88,10 @@ toml = "0.8"
|
||||
unicode-segmentation = "1.10"
|
||||
ureq = { version = "2.8", default-features = false, features = ["charset", "gzip", "json", "native-tls"] }
|
||||
url = "2.2"
|
||||
uu_cp = "0.0.22"
|
||||
uu_whoami = "0.0.22"
|
||||
uu_mkdir = "0.0.22"
|
||||
uu_cp = "0.0.23"
|
||||
uu_whoami = "0.0.23"
|
||||
uu_mkdir = "0.0.23"
|
||||
uu_mktemp = "0.0.23"
|
||||
uuid = { version = "1.5", features = ["v4"] }
|
||||
wax = { version = "0.6" }
|
||||
which = { version = "5.0", optional = true }
|
||||
|
@ -201,7 +201,8 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
||||
Cd,
|
||||
Ls,
|
||||
Mkdir,
|
||||
UMkdir,
|
||||
UMkdir,
|
||||
Mktemp,
|
||||
Mv,
|
||||
Cp,
|
||||
UCp,
|
||||
|
129
crates/nu-command/src/filesystem/mktemp.rs
Normal file
129
crates/nu-command/src/filesystem/mktemp.rs
Normal file
@ -0,0 +1,129 @@
|
||||
use nu_engine::env::current_dir;
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Type, Value,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Mktemp;
|
||||
|
||||
impl Command for Mktemp {
|
||||
fn name(&self) -> &str {
|
||||
"mktemp"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Create temporary files or directories using uutils/coreutils mktemp."
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec![
|
||||
"coreutils",
|
||||
"create",
|
||||
"directory",
|
||||
"file",
|
||||
"folder",
|
||||
"temporary",
|
||||
]
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("mktemp")
|
||||
.input_output_types(vec![(Type::Nothing, Type::String)])
|
||||
.optional(
|
||||
"template",
|
||||
SyntaxShape::String,
|
||||
"Optional pattern from which the name of the file or directory is derived. Must contain at least three 'X's in last component.",
|
||||
)
|
||||
.named("suffix", SyntaxShape::String, "Append suffix to template; must not contain a slash.", None)
|
||||
.named("tmpdir-path", SyntaxShape::Filepath, "Interpret TEMPLATE relative to tmpdir-path. If tmpdir-path is not set use $TMPDIR", Some('p'))
|
||||
.switch("tmpdir", "Interpret TEMPLATE relative to the system temporary directory.", Some('t'))
|
||||
.switch("directory", "Create a directory instead of a file.", Some('d'))
|
||||
.category(Category::FileSystem)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Make a temporary file with the given suffix in the current working directory.",
|
||||
example: "mktemp --suffix .txt",
|
||||
result: Some(Value::test_string("<WORKING_DIR>/tmp.lekjbhelyx.txt")),
|
||||
},
|
||||
Example {
|
||||
description: "Make a temporary file named testfile.XXX with the 'X's as random characters in the current working directory.",
|
||||
example: "mktemp testfile.XXX",
|
||||
result: Some(Value::test_string("<WORKING_DIR>/testfile.4kh")),
|
||||
},
|
||||
Example {
|
||||
description: "Make a temporary file with a template in the system temp directory.",
|
||||
example: "mktemp -t testfile.XXX",
|
||||
result: Some(Value::test_string("/tmp/testfile.4kh")),
|
||||
},
|
||||
Example {
|
||||
description: "Make a temporary directory with randomly generated name in the temporary directory.",
|
||||
example: "mktemp -d",
|
||||
result: Some(Value::test_string("/tmp/tmp.NMw9fJr8K0")),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let span = call.head;
|
||||
let template = call
|
||||
.rest(engine_state, stack, 0)?
|
||||
.first()
|
||||
.cloned()
|
||||
.map(|i: Spanned<String>| i.item)
|
||||
.unwrap_or("tmp.XXXXXXXXXX".to_string()); // same as default in coreutils
|
||||
let directory = call.has_flag("directory");
|
||||
let suffix = call.get_flag(engine_state, stack, "suffix")?;
|
||||
let tmpdir = call.has_flag("tmpdir");
|
||||
let tmpdir_path = call
|
||||
.get_flag(engine_state, stack, "tmpdir-path")?
|
||||
.map(|i: Spanned<PathBuf>| i.item);
|
||||
|
||||
let tmpdir = if tmpdir_path.is_some() {
|
||||
tmpdir_path
|
||||
} else if directory || tmpdir {
|
||||
Some(std::env::temp_dir())
|
||||
} else {
|
||||
Some(current_dir(engine_state, stack)?)
|
||||
};
|
||||
|
||||
let options = uu_mktemp::Options {
|
||||
directory,
|
||||
dry_run: false,
|
||||
quiet: false,
|
||||
suffix,
|
||||
template,
|
||||
tmpdir,
|
||||
treat_as_template: true,
|
||||
};
|
||||
|
||||
let res = match uu_mktemp::mktemp(&options) {
|
||||
Ok(res) => res
|
||||
.into_os_string()
|
||||
.into_string()
|
||||
.map_err(|e| ShellError::IOErrorSpanned(e.to_string_lossy().to_string(), span))?,
|
||||
Err(e) => {
|
||||
return Err(ShellError::GenericError(
|
||||
format!("{}", e),
|
||||
format!("{}", e),
|
||||
None,
|
||||
None,
|
||||
Vec::new(),
|
||||
));
|
||||
}
|
||||
};
|
||||
Ok(PipelineData::Value(Value::string(res, span), None))
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ mod cp;
|
||||
mod glob;
|
||||
mod ls;
|
||||
mod mkdir;
|
||||
mod mktemp;
|
||||
mod mv;
|
||||
mod open;
|
||||
mod rm;
|
||||
@ -20,6 +21,7 @@ pub use cp::Cp;
|
||||
pub use glob::Glob;
|
||||
pub use ls::Ls;
|
||||
pub use mkdir::Mkdir;
|
||||
pub use mktemp::Mktemp;
|
||||
pub use mv::Mv;
|
||||
pub use rm::Rm;
|
||||
pub use save::Save;
|
||||
|
45
crates/nu-command/tests/commands/mktemp.rs
Normal file
45
crates/nu-command/tests/commands/mktemp.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use nu_test_support::nu;
|
||||
use nu_test_support::playground::Playground;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[test]
|
||||
fn creates_temp_file() {
|
||||
Playground::setup("mktemp_test_1", |dirs, _| {
|
||||
let output = nu!(
|
||||
cwd: dirs.test(),
|
||||
"mktemp"
|
||||
);
|
||||
let loc = PathBuf::from(output.out.clone());
|
||||
println!("{:?}", loc);
|
||||
assert!(loc.exists());
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn creates_temp_file_with_suffix() {
|
||||
Playground::setup("mktemp_test_2", |dirs, _| {
|
||||
let output = nu!(
|
||||
cwd: dirs.test(),
|
||||
"mktemp --suffix .txt tempfileXXX"
|
||||
);
|
||||
let loc = PathBuf::from(output.out.clone());
|
||||
assert!(loc.exists());
|
||||
assert!(loc.is_file());
|
||||
assert!(output.out.ends_with(".txt"));
|
||||
assert!(output.out.starts_with(dirs.test().to_str().unwrap()));
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn creates_temp_directory() {
|
||||
Playground::setup("mktemp_test_3", |dirs, _| {
|
||||
let output = nu!(
|
||||
cwd: dirs.test(),
|
||||
"mktemp -d"
|
||||
);
|
||||
|
||||
let loc = PathBuf::from(output.out);
|
||||
assert!(loc.exists());
|
||||
assert!(loc.is_dir());
|
||||
})
|
||||
}
|
@ -57,6 +57,7 @@ mod match_;
|
||||
mod math;
|
||||
mod merge;
|
||||
mod mkdir;
|
||||
mod mktemp;
|
||||
mod move_;
|
||||
mod mut_;
|
||||
mod network;
|
||||
|
Loading…
Reference in New Issue
Block a user