These are my sample files
user@linux:~$ ls -l | cut -d ' ' -f 10-
ch 10 - file.txt
ch 2 - file.txt
ch 3 - file.txt
ch 4a - file.txt
ch 5 - file.txt
user@linux:~$
I would like to add 0
to any single digit (including 4a
) so the final output would be like this.
user@linux:~$ ls -l | cut -d ' ' -f 10-
ch 10 - file.txt
ch 02 - file.txt
ch 03 - file.txt
ch 04a - file.txt
ch 05 - file.txt
user@linux:~$
Is this possible using built-in tools in Linux?
-
2What do you consider to be "built-in tools in Linux"? does this help How to mass rename files with ill-formed numbering?steeldriver– steeldriver2019年12月15日 03:56:45 +00:00Commented Dec 15, 2019 at 3:56
4 Answers 4
The pattern
'ch '[1-9][!0-9]*'- file.txt'
would match all filenames that needs changing (out of the ones that you show), i.e. any filename that starts with ch
, followed by a digit between 1 and 9, followed by something that is not a digit. After that, we allow for any characters whatsoever, and the names must end with - file.txt
.
We can loop over these files with
for name in 'ch '[1-9][!0-9]*'- file.txt'; do
...
done
The objective is now to insert a 0
after the ch
bit. This can be done by stripping the ch
substring off, and replacing it with ch 0
:
for name in 'ch '[1-9][!0-9]*'- file.txt'; do
newname='ch 0'${name#ch }
done
The ${name#ch }
parameter substitution would expand to 2 - file.txt
if $name
contained ch 2 - file.txt
(it would remove the prefix ch
).
After this, you could just rename the file:
for name in 'ch '[1-9][!0-9]*'- file.txt'; do
newname='ch 0'${name#ch }
printf 'would rename "%s" into "%s"\n' "$name" "$newname"
# mv -i "$name" "$newname"
done
Remove the #
on the line with the commented out mv
command after running the loop once to see that it does the correct thing.
The loop above, given the filenames that you show, would output
would rename "ch 2 - file.txt" into "ch 02 - file.txt"
would rename "ch 3 - file.txt" into "ch 03 - file.txt"
would rename "ch 4a - file.txt" into "ch 04a - file.txt"
would rename "ch 5 - file.txt" into "ch 05 - file.txt"
With Lary Wall's rename
/prename
(rename
in Debian/Ubuntu, prename
in RedHat/Fedora):
rename -n 's/^ch (\d) /ch 01ドル /' *
-n
is for dry runs, remove (or replace with `-v') for actual execution.
With zsh
:
autoload zmv # best in .zshrc
zmv -n '*[0-9]*' '${f//(#m)<->/${(l:2::0:)MATCH}}'
(remove -n
(dry-run) if happy).
Would l
eft-pad all numbers to width 2.
Beware it also truncates numbers to width 2 (would change 1234-2.txt
into 34-02.txt
).
Not sure what you mean by builtin, especially in relation to Linux which is just a kernel. autoload
is a zsh built-in, but note that the zmv
autoloadable function invokes the mv
utility which is not a builtin by default (but can be made so with zmodload zsh/files
).
This is what you are looking for.
What is need to be done basically are as follows:
1, check if the digit is single or double-digit. If it is single digit and the value less than or equal to 9 then, rename the file. Which should be as simple as doing:
(( someDigit <= 9 )) && [[ ${#someDigit'slength} -lt 2 ]]
2, since there would be some files with letters so, we can store them as:
digit=$( echo $file | cut -d '-' -f1 | grep ch | awk '{print 2ドル}' );
3, then filter the digit so that we can compare it for first option. Add more filters which you may feel would be necessary in tr -d
option as follows:
digitOnly=$( echo $digit | tr -d [a-Z] );
4, then run a loop in your directory where you intend to rename the files. Implementing all of this would look as follows:
for file in *file.txt; do
digit=$( echo $file | cut -d '-' -f1 | grep ch | awk '{print 2ドル}' );
digitOnly=$( echo $digit | tr -d [a-Z] );
if (( digitOnly <= 9 )) && [[ ${#digitOnly} -lt 2 ]]; then
# mv "${file}" "ch 0${digit} - file.txt"
echo "'${file}' is renamed to 'ch 0${digit} - file.txt'";
fi
done
Remove the # from mv, after you are satisfied before real execution. With your example:
touch 'ch 10 - file.txt' 'ch 2 - file.txt' 'ch 3 - file.txt' 'ch 4a - file.txt' 'ch 5 - file.txt'
Then, executing the script or copying pasting the above script
in bash
will give you the following output:
'ch 2 - file.txt' is renamed to 'ch 02 - file.txt'
'ch 3 - file.txt' is renamed to 'ch 03 - file.txt'
'ch 4a - file.txt' is renamed to 'ch 04a - file.txt'
'ch 5 - file.txt' is renamed to 'ch 05 - file.txt'
Obviously, you can even make it more generic, by including it in a function then take input such as file pattern search and so on, but your question does not demand that. So, I believe, the current answer would handle the situation stated in the question quite well.