mirror of
https://github.com/nushell/nu_scripts.git
synced 2024-10-05 17:48:11 +03: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
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 str/
|
||||
# commands
|
||||
export use fs.nu *
|
||||
export use bulk-rename.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 str_xpend.nu
|
||||
|
Loading…
Reference in New Issue
Block a user