views:

198

answers:

5

I have a folder with about 1700 files. They are all named like 1.txt or 1497.txt etc. I would like to rename all the files so that all the filenames are 4 digits long.

IE 23.txt becomes 0023.txt.

What is a shell script that will do this? Or a related question: How do I use grep to only match lines that contain \d.txt (IE 1 digit, then a period, then the letters txt)?

Here's what I have so far:

for a in [command i need help with]
do
  mv $a 000$a
done

Basically, run that 3 times, with commands there to find 1 digit, 2 digit, and 3 digit filenames (with the number of initial zeros changed)

A: 

To only match single digit text files, you can do...

$ ls | grep '[0-9]\.txt'
LukeN
Is there a way to remove the `.txt` from $a? I've wondered this several times before.
David Oneill
Yes, `${a%.txt}` will do that (or `${a%.*}` to remove any suffix).
Chris Jester-Young
A quick way if you know the extension before is piping the output through sed, like this: `ls | grep '[0-9]\.txt' | sed 's/\.txt//g'`
LukeN
@Luke: That's _way_ too painful for the task at hand. :-)
Chris Jester-Young
Figured.. but I'd think it's more portable with all the different shells out there :)
LukeN
+3  A: 

Try this

for a in [0-9]*.txt; do
    mv $a `printf %04d.%s ${a%.*} ${a##*.}`
done

You can change your txt extension by whatever you want.


On the same topic :

Colin Hebert
This outputs a bunch of errors for each filename that is already 4 digits. Adding the foo at the start would get around this, but I didn't want that there. Thanks for those links.
David Oneill
Sorry for the "foo"... Bad copy/paste
Colin Hebert
+2  A: 
for a in *.txt; do
  b=$(printf %04d.txt ${a%.txt})
  if [ $a != $b ]; then
    mv $a $b
  fi
done
Chris Jester-Young
Thanks for the answer! Basically the same as the other one that came in at exactly the same time.
David Oneill
@David: My solution doesn't cause a bunch of errors. :-P
Chris Jester-Young
A: 

One-liner:

ls | awk '/^([0-9]+)\.txt$/ { printf("%s %04d.txt\n", $0, $1) }' | xargs -n2 mv

How do I use grep to only match lines that contain \d.txt (IE 1 digit, then a period, then the letters txt)?

grep -E '^[0-9]\.txt$'
rkhayrov
A: 

Using the rename (prename in some cases) script that is sometimes installed with Perl, you can use Perl expressions to do the renaming. The script skips renaming if there's a name collision.

The command below renames only files that have four or fewer digits followed by a ".txt" extension. It does not rename files that do not strictly conform to that pattern. It does not truncate names that consist of more than four digits.

rename 'unless (/0+[0-9]{4}.txt/) {s/^([0-9]{1,3}\.txt)$/000$1/g;s/0*([0-9]{4}\..*)/$1/}' *

A few examples:

Original    Becomes
1.txt       0001.txt
02.txt      0002.txt
123.txt     0123.txt
00000.txt   00000.txt
1.23.txt    1.23.txt

Other answers given so far will attempt to rename files that don't conform to the pattern, produce errors for filenames that contain non-digit characters, perform renames that produce name collisions, try and fail to rename files that have spaces in their names and possibly other problems.

Dennis Williamson
Yagni. Seriously. Who cares about filenames that contain spaces or non-numeric characters if that's not in the scope of the question? If the OP wanted to write a general-use script, he would have said so in the question, and the answers would have accommodated accordingly.
Chris Jester-Young
@Chris: My answers often do two things. One, they attempt to answer an OP's question. Two, they attempt to provide additional information that may be useful to future viewers of the question.
Dennis Williamson