I have a two part question.

I have a series of files with the names "file.pdb.#", where # is an integer.

1.) How can I script to rename all the "file.pdb.#" to "file_#.pdb", where the integer is preserved? All files are in the same directory.

I presumably need to read in the constant string (file.pdb.) with the variable being the #.

2.) Even more important, how would I have searched for this answer, properly, among answered questions? I have no doubt this question has been answered many times before. However, I am not sure how I might have properly searched for the answer to this question (and obviously I should learn to search previously answered questions better to not bother people in the future). I cannot imagine what the right search string is (googling how to read in files and rename them in Linux is awfully generic and I waded through suggestions not germane to this sort of file format manipulation).

  • 3
    Your 2nd part needs to be a different question on meta; not on AU. Please remove it and re-ask. – Rinzwind Jul 2 '15 at 3:30
  • Answer for first question mmv file.pdb.* file_#1.pdb – αғsнιη Jul 2 '15 at 3:47

You can use capture groups and backreferences e.g.

rename -vn -- 's/\.(pdb)\.(\d+)/_$2.$1/' *.pdb.*

to match a literal period \. followed by (literal) string pdb, followed by a second period \. and then a sequence of one or more digits \d+, copying the string and digit sequence into numbered capture groups $1 and $2 respectively and then re-substituting them in reverse order with an underscore in place of the first period.

Options -v and -n are for verbose output (listing any changes made) and no-operation, allowing you to test the correctness before committing to making any actual substitutions: you will need to remove the n option to actually apply the changes.

The -- marks the end of command options - this just makes the command more robust in the rare case that you have filenames that start with a hyphen.

Testing it in a directory containing files file.pdb.1 file.pdb.2 file.pdb.3 file.pdb.4 file.pdb.5 (among others):

$ rename -vn -- 's/\.(pdb)\.(\d+)/_$2.$1/' *.pdb.*
file.pdb.1 renamed as file_1.pdb
file.pdb.2 renamed as file_2.pdb
file.pdb.3 renamed as file_3.pdb
file.pdb.4 renamed as file_4.pdb
file.pdb.5 renamed as file_5.pdb
  • Can you explain the logic of the syntax a bit? The string did not work, when blindly copy/pasting it, such that there is a flaw in how I explained things that this did not work. I would like to understand what this says better. – Robert Molt Jul 2 '15 at 23:59
  • @RobertMolt please see updated answer – steeldriver Jul 3 '15 at 0:22
  • I appreciate your efforts very much. There still seems to be a flaw; when implemented, with or without the n flag, there is no output. If you were to make two files in a directory named file.pdb.1 and file.pdb.2, and implemented the above, do you see the desired results? You have explained everything beautifully, and I appreciate it. I assume .pdb. at the end simply clarifies on what set of files one should implement the aforementioned substitution. – Robert Molt Jul 4 '15 at 22:23
  • @RobertMolt sorry I don't understand why it is not working for you: I have edited my answer to include the result of testing on my own system. – steeldriver Jul 4 '15 at 22:35
  • I appreciate your sincere efforts; clearly it works for you. Is the command shell-dependent? I am using bash. I am desperately reaching here trying to understand why this is not recognizing any of my files with the same names are causing this. I am running this on Red Hat linux, but I cannot imagine this should matter (I personally own a Ubuntu OS, but am doing this on a Red Hat flavor). – Robert Molt Jul 4 '15 at 23:08

The strictest way possible, so that only filenames starting with file.pdb. followed by at least a digit will be renamed, using rename:

rename -n 's/(file)(\.pdb)\.([0-9]+)/$1_$3$2/' *

If the result is the expected one, remove the -n option:

rename 's/(file)(\.pdb)\.([0-9]+)/$1_$3$2/' *

Apart from excellent rename answers you have received already, you can use bash parameter expansion:

for i in *.pdb.*; do mv -i "$i" "${i%%.pdb.*}_${i##*.}.pdb"; done
  • The pattern ${i%%.pdb*} will get the file portion of name file.pdb.#

  • ${i##*.} will get the digits after .pdb.

  • Thank you, this suggestion worked (whereas the others did not for some reason). I was hoping to ask a bit more to understand it properly. -i seems like an implicit variable which is a string -"${i%%.pdb.*} how is %% specifying the file portion? can you direct me to where I can understand why this syntax is grabbing the file name preceding .pdb? -what is the underscore doing? -how does ## read in the correct digits? I tend to think of things in Fortran, my only real programming experience, and hence I am confused. – Robert Molt Jul 2 '15 at 23:30
  • @RobertMolt i have taken the variables as variable i, then renamed them by using parameter expansion..its a feature of bash that will allow you to manipulate variables to get your desired portion (and many more operations)..i am afraid to get a full grasp off of it you need to get some idea about it first....you can read this..check out the ${parameter##word} and ${parameter%%word} portions – heemayl Jul 2 '15 at 23:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.