mirror of
https://github.com/nushell/nushell.git
synced 2025-01-21 13:50:11 +01:00
# Description These changes resolve #13623 where globs are not handled by `utouch`. # User-Facing Changes - Glob patterns passed to `utouch` will be resolved to all individual files that match the pattern. For example, running `utouch *.txt` in a directory that already has `file1.txt` and `file2.txt` is the same thing as running `utouch file1.txt file2.txt`. All flags such as `-a`, `-m` and `-c` will be respected. - If a glob pattern is provided to `utouch` and doesn't match any files, a file will be created with the literal name of the glob pattern. This only applies to Linux/MacOS because Windows forbids creating file names with restricted characters (see [naming a file docs](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file)) --------- Co-authored-by: Henry Jetmundsen <hjetmundsen@atlassian.com>
This commit is contained in:
parent
62bd6fe08b
commit
c46ca36bcd
@ -1,6 +1,7 @@
|
|||||||
use chrono::{DateTime, FixedOffset};
|
use chrono::{DateTime, FixedOffset};
|
||||||
use filetime::FileTime;
|
use filetime::FileTime;
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
use nu_glob::{glob, is_glob};
|
||||||
use nu_path::expand_path_with;
|
use nu_path::expand_path_with;
|
||||||
use nu_protocol::NuGlob;
|
use nu_protocol::NuGlob;
|
||||||
use std::{io::ErrorKind, path::PathBuf};
|
use std::{io::ErrorKind, path::PathBuf};
|
||||||
@ -149,9 +150,52 @@ impl Command for UTouch {
|
|||||||
if file_glob.item.as_ref() == "-" {
|
if file_glob.item.as_ref() == "-" {
|
||||||
input_files.push(InputFile::Stdout);
|
input_files.push(InputFile::Stdout);
|
||||||
} else {
|
} else {
|
||||||
let path =
|
let file_path =
|
||||||
expand_path_with(file_glob.item.as_ref(), &cwd, file_glob.item.is_expand());
|
expand_path_with(file_glob.item.as_ref(), &cwd, file_glob.item.is_expand());
|
||||||
input_files.push(InputFile::Path(path));
|
|
||||||
|
if !file_glob.item.is_expand() {
|
||||||
|
input_files.push(InputFile::Path(file_path));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut expanded_globs = glob(&file_path.to_string_lossy())
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
panic!(
|
||||||
|
"Failed to process file path: {}",
|
||||||
|
&file_path.to_string_lossy()
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.peekable();
|
||||||
|
|
||||||
|
if expanded_globs.peek().is_none() {
|
||||||
|
let file_name = file_path.file_name().unwrap_or_else(|| {
|
||||||
|
panic!(
|
||||||
|
"Failed to process file path: {}",
|
||||||
|
&file_path.to_string_lossy()
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
if is_glob(&file_name.to_string_lossy()) {
|
||||||
|
return Err(ShellError::GenericError {
|
||||||
|
error: format!(
|
||||||
|
"No matches found for glob {}",
|
||||||
|
file_name.to_string_lossy()
|
||||||
|
),
|
||||||
|
msg: "No matches found for glob".into(),
|
||||||
|
span: Some(file_glob.span),
|
||||||
|
help: Some(format!(
|
||||||
|
"Use quotes if you want to create a file named {}",
|
||||||
|
file_name.to_string_lossy()
|
||||||
|
)),
|
||||||
|
inner: vec![],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
input_files.push(InputFile::Path(file_path));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
input_files.extend(expanded_globs.filter_map(Result::ok).map(InputFile::Path));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,6 +272,11 @@ impl Command for UTouch {
|
|||||||
example: "utouch -m fixture.json",
|
example: "utouch -m fixture.json",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: r#"Changes the last modified and accessed time of all files with the .json extension to today's date"#,
|
||||||
|
example: "utouch *.json",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Changes the last accessed and modified times of files a, b and c to the current time but yesterday",
|
description: "Changes the last accessed and modified times of files a, b and c to the current time but yesterday",
|
||||||
example: r#"utouch -d "yesterday" a b c"#,
|
example: r#"utouch -d "yesterday" a b c"#,
|
||||||
|
@ -83,6 +83,34 @@ fn creates_two_files() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Windows forbids file names with reserved characters
|
||||||
|
// https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
|
||||||
|
#[test]
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
fn creates_a_file_when_glob_is_quoted() {
|
||||||
|
Playground::setup("create_test_glob", |dirs, _sandbox| {
|
||||||
|
nu!(
|
||||||
|
cwd: dirs.test(),
|
||||||
|
"utouch '*.txt'"
|
||||||
|
);
|
||||||
|
|
||||||
|
let path = dirs.test().join("*.txt");
|
||||||
|
assert!(path.exists());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fails_when_glob_has_no_matches() {
|
||||||
|
Playground::setup("create_test_glob_no_matches", |dirs, _sandbox| {
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: dirs.test(),
|
||||||
|
"utouch *.txt"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(actual.err.contains("No matches found for glob *.txt"));
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn change_modified_time_of_file_to_today() {
|
fn change_modified_time_of_file_to_today() {
|
||||||
Playground::setup("change_time_test_9", |dirs, sandbox| {
|
Playground::setup("change_time_test_9", |dirs, sandbox| {
|
||||||
@ -168,6 +196,31 @@ fn change_modified_and_access_time_of_file_to_today() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn change_modified_and_access_time_of_files_matching_glob_to_today() {
|
||||||
|
Playground::setup("change_mtime_atime_test_glob", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(&[Stub::EmptyFile("file.txt")]);
|
||||||
|
|
||||||
|
let path = dirs.test().join("file.txt");
|
||||||
|
filetime::set_file_times(&path, TIME_ONE, TIME_ONE).unwrap();
|
||||||
|
|
||||||
|
nu!(
|
||||||
|
cwd: dirs.test(),
|
||||||
|
"utouch *.txt"
|
||||||
|
);
|
||||||
|
|
||||||
|
let metadata = path.metadata().unwrap();
|
||||||
|
|
||||||
|
// Check only the date since the time may not match exactly
|
||||||
|
let today = Local::now().date_naive();
|
||||||
|
let mtime_day = DateTime::<Local>::from(metadata.modified().unwrap()).date_naive();
|
||||||
|
let atime_day = DateTime::<Local>::from(metadata.accessed().unwrap()).date_naive();
|
||||||
|
|
||||||
|
assert_eq!(today, mtime_day);
|
||||||
|
assert_eq!(today, atime_day);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn not_create_file_if_it_not_exists() {
|
fn not_create_file_if_it_not_exists() {
|
||||||
Playground::setup("change_time_test_28", |dirs, _sandbox| {
|
Playground::setup("change_time_test_28", |dirs, _sandbox| {
|
||||||
|
Loading…
Reference in New Issue
Block a user