Add glob support to utouch (issue #13623) (#14674)

# 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:
Henry Jetmundsen
2025-01-01 17:38:15 -08:00
committed by GitHub
parent 62bd6fe08b
commit c46ca36bcd
2 changed files with 104 additions and 2 deletions

View File

@ -1,6 +1,7 @@
use chrono::{DateTime, FixedOffset};
use filetime::FileTime;
use nu_engine::command_prelude::*;
use nu_glob::{glob, is_glob};
use nu_path::expand_path_with;
use nu_protocol::NuGlob;
use std::{io::ErrorKind, path::PathBuf};
@ -149,9 +150,52 @@ impl Command for UTouch {
if file_glob.item.as_ref() == "-" {
input_files.push(InputFile::Stdout);
} else {
let path =
let file_path =
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",
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 {
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"#,