mirror of
https://github.com/nushell/nushell.git
synced 2025-08-15 05:22:33 +02:00
Add file attribute handling flag to cp (#11491)
# Description This PR adds possibility to preserve/strip attributes from files when using `cp` (via uu_cp::Attributes). To achieve this a single `--preserve <list of attributes>` flag is added. This is different from how coreutils and uutils cp function, but I believe this is better for nushell. Coreutils cp has three options `-p`, `--preserve` and `--no-presevce`. The logic of these two options is not straightforward. As far as I understand it is: 1. By default only mode attributes are preserved 2. `--preserve` option adds to default preserved attributes specified ones (e.g. `--preserve=xattr,timestamps` will preserve mode, timestamps and xattr) 3. `-p` is the same as `--preserve=mode,ownership,timestamps` 4. `--no-preserve` option rejects specified attributes (having priority over `--preserve`) However (in my opinion) the `--no-preserve` option is not needed, because its only use seems to be rejecting attributes preserved by default. But there is no need for this in nushell, because `--preserve` can be specified with empty list as argument (whereas coreutils cp will display a `cp: ambiguous argument ‘’ for ‘--preserve’` error if `--preserve` is used with empty string as argument). So to simplify this command is suggest (and implemented) only the `--preserve` with the following logic: 1. By default mode attribute is preserved (as in coreutils cp) 2. `--preserve [ ... ]` will overwrite default with whatever is specified in list (empty list meaning preserve nothing) This way cp without `--preserve` behaves the same as coreutils `cp`, but instead of using combinations of `--preserve` and `--no-preserve` one needs to use `--preserve [ ... ]` with all attributes specified explicitly. This seems more user-friendly to me as it does not require remembering what the attributes preserved by default are and rejecting them manually. However I see the possible problem with behavior different from coreutils implementation, so some feedback is apprecieated! # User-Facing Changes Users can now preserve or reject file attributes when using `cp` # Tests + Formatting Added tests manipulating mode and timestamps attributes.
This commit is contained in:
@ -1027,3 +1027,76 @@ fn copies_files_with_glob_metachars(#[case] src_name: &str) {
|
||||
fn copies_files_with_glob_metachars_nw(#[case] src_name: &str) {
|
||||
copies_files_with_glob_metachars(src_name);
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[test]
|
||||
fn test_cp_preserve_timestamps() {
|
||||
// Preserve timestamp and mode
|
||||
|
||||
Playground::setup("ucp_test_35", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![EmptyFile("file.txt")]);
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(),
|
||||
"
|
||||
chmod +x file.txt
|
||||
cp --preserve [ mode timestamps ] file.txt other.txt
|
||||
|
||||
let old_attrs = ls -l file.txt | get 0 | select mode accessed modified
|
||||
let new_attrs = ls -l other.txt | get 0 | select mode accessed modified
|
||||
|
||||
$old_attrs == $new_attrs
|
||||
",
|
||||
);
|
||||
assert!(actual.err.is_empty());
|
||||
assert_eq!(actual.out, "true");
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[test]
|
||||
fn test_cp_preserve_only_timestamps() {
|
||||
// Preserve timestamps and discard all other attributes including mode
|
||||
|
||||
Playground::setup("ucp_test_35", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![EmptyFile("file.txt")]);
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(),
|
||||
"
|
||||
chmod +x file.txt
|
||||
cp --preserve [ timestamps ] file.txt other.txt
|
||||
|
||||
let old_attrs = ls -l file.txt | get 0 | select mode accessed modified
|
||||
let new_attrs = ls -l other.txt | get 0 | select mode accessed modified
|
||||
|
||||
print (($old_attrs | select mode) != ($new_attrs | select mode))
|
||||
print (($old_attrs | select accessed modified) == ($new_attrs | select accessed modified))
|
||||
",
|
||||
);
|
||||
assert!(actual.err.is_empty());
|
||||
assert_eq!(actual.out, "truetrue");
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
#[test]
|
||||
fn test_cp_preserve_nothing() {
|
||||
// Preserve no attributes
|
||||
|
||||
Playground::setup("ucp_test_35", |dirs, sandbox| {
|
||||
sandbox.with_files(vec![EmptyFile("file.txt")]);
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(),
|
||||
"
|
||||
chmod +x file.txt
|
||||
cp --preserve [] file.txt other.txt
|
||||
|
||||
let old_attrs = ls -l file.txt | get 0 | select mode accessed modified
|
||||
let new_attrs = ls -l other.txt | get 0 | select mode accessed modified
|
||||
|
||||
$old_attrs != $new_attrs
|
||||
",
|
||||
);
|
||||
assert!(actual.err.is_empty());
|
||||
assert_eq!(actual.out, "true");
|
||||
});
|
||||
}
|
||||
|
Reference in New Issue
Block a user