mirror of
https://github.com/RGBCube/nu_scripts
synced 2025-08-01 06:37:46 +00:00
[stdlib-candidate] Clean up file bulk-rename
a little (#798)
[Generated diff](https://www.diffnow.com/report/xmq4f) for command because move broke it. - No `file` prefix similar to other [filesystem commands](https://www.nushell.sh/commands/categories/filesystem.html) - Input paths instead of directory param for filters or globbing - Record closure param with original path (`$in` is still stem) - `--verbose` table output - `--no-exectute` for dry run with `--verbose` - Shorthand flags - Parallel renaming for large directories - More tests @amtoine Request feedback 🙏🏼 Happy to revise ot revert anything! ```console ❯ bulk-rename -h Rename bulk input files in parallel using a closure. The reason behind this command is quite simple: - Sometimes one receives a bunch of files with integer ids: 1, 2, 3, ... - These ids come rarely with padding... i.e. 1 instead of 001 when there are 3-digit ids - This means that file with id 9 will be sorted way after file with id 1000 This command allows to do such a task! Examples: Rename `.mise.toml` files to `.mise.local.toml` recursively > glob **/.mise.toml | bulk-rename { str append .local } Rename files in `/foo` with a name that has an id to have 3 digits with 0-padding > ls /foo | bulk-rename { |path| if $path.input.type == file { $path.stem | parse "some_format_{id}" | get 0 | update id { fill --alignment r --character 0 --width 3 } | $"some_format_($in.id)" } # else skip dirs } Usage: > main {flags} <update_stem> Flags: -v, --verbose - Show which files were renamed, if any -n, --no-execute - Do not make any changes; add --verbose to see what would be made whitespace bug: nushell/nushell#12264 -h, --help - Display the help message for this command Parameters: update_stem <closure()>: The code to rename the file stem: receives the old stem as input and a record param with both `stem` and `input` keys Input/output types: ╭───┬───────────┬─────────────────────────────────╮ │ # │ input │ output │ ├───┼───────────┼─────────────────────────────────┤ │ 0 │ list<any> │ nothing │ │ 1 │ list<any> │ table<old: string, new: string> │ ╰───┴───────────┴─────────────────────────────────╯ ```
This commit is contained in:
parent
f39976902a
commit
268201e4ac
6 changed files with 146 additions and 87 deletions
49
stdlib-candidate/std-rfc/bulk-rename.nu
Normal file
49
stdlib-candidate/std-rfc/bulk-rename.nu
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
# Rename bulk input files in parallel using a closure.
|
||||||
|
#
|
||||||
|
# The reason behind this command is quite simple:
|
||||||
|
# - Sometimes one receives a bunch of files with integer ids: 1, 2, 3, ...
|
||||||
|
# - These ids come rarely with padding... i.e. 1 instead of 001 when there are 3-digit ids
|
||||||
|
# - This means that file with id 9 will be sorted way after file with id 1000
|
||||||
|
#
|
||||||
|
# This command allows to do such a task!
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
# Rename `.mise.toml` files to `.mise.local.toml` recursively
|
||||||
|
# > glob **/.mise.toml | bulk-rename { str append .local }
|
||||||
|
#
|
||||||
|
# Rename files in `/foo` with a name that has an id to have 3 digits with 0-padding
|
||||||
|
# > ls /foo | bulk-rename { |path|
|
||||||
|
# if $path.input.type == file {
|
||||||
|
# $path.stem | parse "some_format_{id}"
|
||||||
|
# | get 0
|
||||||
|
# | update id { fill --alignment r --character 0 --width 3 }
|
||||||
|
# | $"some_format_($in.id)"
|
||||||
|
# }
|
||||||
|
# # else skip dirs
|
||||||
|
# }
|
||||||
|
export def main [
|
||||||
|
update_stem: closure, # The code to rename the file stem: receives the old stem as input and a record param with both `stem` and `input` keys
|
||||||
|
--verbose (-v), # Show which files were renamed, if any
|
||||||
|
--no-execute (-n) # Do not make any changes; add --verbose to see what would be made
|
||||||
|
]: [list<any> -> nothing, list<any> -> table<old: path new: path>] {
|
||||||
|
let renamed = par-each --keep-order { |input|
|
||||||
|
let update_or_keep_stem = { |parts|
|
||||||
|
do $update_stem { stem: $in input: $input } | default $parts.stem
|
||||||
|
}
|
||||||
|
let old = if ($input | describe) == string {
|
||||||
|
$input
|
||||||
|
} else {
|
||||||
|
$input.name # convenience for ls
|
||||||
|
}
|
||||||
|
let new = $old | path parse | update stem $update_or_keep_stem | path join
|
||||||
|
if $new != $old {
|
||||||
|
if not $no_execute {
|
||||||
|
mv --force --verbose=$verbose $old $new
|
||||||
|
}
|
||||||
|
{ old: $old new: $new }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if $verbose {
|
||||||
|
$renamed
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,35 +0,0 @@
|
||||||
# rename a bulk of files in a directory using a closure
|
|
||||||
#
|
|
||||||
# the reason behind this command is quite simple
|
|
||||||
# - sometimes one receives a bunch of files with integer ids: 1, 2, 3, ...
|
|
||||||
# - these ids come rarely with padding... i.e. 1 instead of 001 when there are 3-digit ids
|
|
||||||
# - this means that file with id 9 will be sorted way after file with id 1000
|
|
||||||
#
|
|
||||||
# this command allows to do such a task!
|
|
||||||
#
|
|
||||||
# # Examples
|
|
||||||
# rename files in `/foo` with a name that has an id to have 3 digits with 0-padding
|
|
||||||
# > file bulk-rename /foo {
|
|
||||||
# parse "some_format_{id}"
|
|
||||||
# | get 0
|
|
||||||
# | update id { fill --alignment r --character 0 --width 3 }
|
|
||||||
# | $"some_format_($in.id)"
|
|
||||||
# }
|
|
||||||
export def "file bulk-rename" [
|
|
||||||
directory: path, # the path where files need to be renamed in bulk
|
|
||||||
stem_update: closure, # the code to run on the stem of the files: should start with parsing the format and end with reconstructing the same format
|
|
||||||
--verbose, # be verbose when moving the files around
|
|
||||||
]: nothing -> nothing {
|
|
||||||
ls --full-paths $directory | insert new {|row|
|
|
||||||
$row.name | path parse | update stem $stem_update | path join
|
|
||||||
}
|
|
||||||
| each {
|
|
||||||
if $verbose {
|
|
||||||
mv --force --verbose $in.name $in.new
|
|
||||||
} else {
|
|
||||||
mv --force $in.name $in.new
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
null
|
|
||||||
}
|
|
|
@ -2,5 +2,5 @@
|
||||||
export module record/
|
export module record/
|
||||||
export module str/
|
export module str/
|
||||||
# commands
|
# commands
|
||||||
export use fs.nu *
|
export use bulk-rename.nu *
|
||||||
export use set-env.nu *
|
export use set-env.nu *
|
||||||
|
|
95
stdlib-candidate/tests/bulk-rename.nu
Normal file
95
stdlib-candidate/tests/bulk-rename.nu
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
use std assert
|
||||||
|
use ../std-rfc 'bulk-rename'
|
||||||
|
|
||||||
|
const fixture = [
|
||||||
|
.gitignore
|
||||||
|
Cargo.toml
|
||||||
|
LICENSE
|
||||||
|
README.md
|
||||||
|
src
|
||||||
|
test.nu
|
||||||
|
]
|
||||||
|
|
||||||
|
export def 'test ls' [] {
|
||||||
|
let expects = [
|
||||||
|
.gitignore # hidden by ls
|
||||||
|
_Cargo.toml
|
||||||
|
_LICENSE
|
||||||
|
_README.md
|
||||||
|
_src
|
||||||
|
_test.nu
|
||||||
|
]
|
||||||
|
test $expects {
|
||||||
|
ls $in | bulk-rename { '_' + $in }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export def 'test --no-execute' [] {
|
||||||
|
test $fixture {
|
||||||
|
ls $in | bulk-rename --no-execute { '_' + $in }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export def 'test --verbose' [] {
|
||||||
|
let expects = [
|
||||||
|
# .gitignore unchanged
|
||||||
|
_Cargo.toml
|
||||||
|
_LICENSE
|
||||||
|
_README.md
|
||||||
|
_src
|
||||||
|
_test.nu
|
||||||
|
]
|
||||||
|
let renamed = test $fixture {
|
||||||
|
ls $in | bulk-rename --verbose --no-execute { '_' + $in }
|
||||||
|
}
|
||||||
|
assert equal ($renamed.new | each { path basename }) $expects
|
||||||
|
}
|
||||||
|
|
||||||
|
export def 'test skip-extensions' [] {
|
||||||
|
let expects = [
|
||||||
|
.gitignore
|
||||||
|
Cargo.toml
|
||||||
|
LICENSE.txt # changed
|
||||||
|
README.md
|
||||||
|
src.txt # changed
|
||||||
|
test.nu
|
||||||
|
]
|
||||||
|
test $expects {
|
||||||
|
ls $in | bulk-rename { |path|
|
||||||
|
if $path.input.name ends-with $path.stem {
|
||||||
|
$path.stem + .txt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export def 'test glob' [] {
|
||||||
|
let expects = [
|
||||||
|
LICENSE # skipped
|
||||||
|
_.gitignore
|
||||||
|
_Cargo.toml
|
||||||
|
_README.md
|
||||||
|
_test.nu
|
||||||
|
src # skipped
|
||||||
|
]
|
||||||
|
test $expects {
|
||||||
|
glob ($in | path join *.*) | bulk-rename { '_' + $in }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def test [expects: list<string> command: closure] {
|
||||||
|
let test_dir = $nu.temp-path | path join (random uuid)
|
||||||
|
def actual-files [] {
|
||||||
|
ls --all --short-names $test_dir | get name | sort
|
||||||
|
}
|
||||||
|
# before
|
||||||
|
mkdir $test_dir
|
||||||
|
$fixture | each { |name| touch ($test_dir | path join $name) }
|
||||||
|
assert equal (actual-files) $fixture
|
||||||
|
# test
|
||||||
|
let renamed = $test_dir | do $command
|
||||||
|
assert equal (actual-files) $expects
|
||||||
|
# after
|
||||||
|
rm --recursive --force $test_dir
|
||||||
|
$renamed
|
||||||
|
}
|
|
@ -1,50 +0,0 @@
|
||||||
use std assert
|
|
||||||
use ../std-rfc "file bulk-rename"
|
|
||||||
|
|
||||||
alias rename = file bulk-rename
|
|
||||||
|
|
||||||
export def "test file bulk-rename" [] {
|
|
||||||
let test_dir = $nu.temp-path | path join (random uuid)
|
|
||||||
|
|
||||||
mkdir $test_dir
|
|
||||||
seq 1 10 | each {|i| touch ($test_dir | path join $"some_($i)_format.txt") }
|
|
||||||
|
|
||||||
let expected = [
|
|
||||||
"some_10_format.txt",
|
|
||||||
"some_1_format.txt",
|
|
||||||
"some_2_format.txt",
|
|
||||||
"some_3_format.txt",
|
|
||||||
"some_4_format.txt",
|
|
||||||
"some_5_format.txt",
|
|
||||||
"some_6_format.txt",
|
|
||||||
"some_7_format.txt",
|
|
||||||
"some_8_format.txt",
|
|
||||||
"some_9_format.txt",
|
|
||||||
]
|
|
||||||
let actual = glob $"($test_dir)/*" | str replace $test_dir "" | str trim --left --char "/"
|
|
||||||
assert equal ($actual | sort) $expected
|
|
||||||
|
|
||||||
rename $test_dir {
|
|
||||||
parse "some_{i}_format"
|
|
||||||
| get 0
|
|
||||||
| update i { fill --alignment r --character 0 --width 3 }
|
|
||||||
| $"some_($in.i)_format"
|
|
||||||
}
|
|
||||||
|
|
||||||
let expected = [
|
|
||||||
"some_001_format.txt",
|
|
||||||
"some_002_format.txt",
|
|
||||||
"some_003_format.txt",
|
|
||||||
"some_004_format.txt",
|
|
||||||
"some_005_format.txt",
|
|
||||||
"some_006_format.txt",
|
|
||||||
"some_007_format.txt",
|
|
||||||
"some_008_format.txt",
|
|
||||||
"some_009_format.txt",
|
|
||||||
"some_010_format.txt",
|
|
||||||
]
|
|
||||||
let actual = glob $"($test_dir)/*" | str replace $test_dir "" | str trim --left --char "/"
|
|
||||||
assert equal ($actual | sort) $expected
|
|
||||||
|
|
||||||
rm -rf $test_dir
|
|
||||||
}
|
|
|
@ -1,3 +1,3 @@
|
||||||
export module fs.nu
|
export module bulk-rename.nu
|
||||||
export module record.nu
|
export module record.nu
|
||||||
export module str_xpend.nu
|
export module str_xpend.nu
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue