Find the answer to your Linux question:
Results 1 to 4 of 4
Hello! Suppose I have the files called: file - 1 file - 2 file - 55 and I want to rename them, using a single command (imagine there are many ...
Enjoy an ad free experience by logging in. Not a member yet? Register.
  1. #1
    Just Joined!
    Join Date
    Oct 2013
    Posts
    4

    Simple batch file renaming puzzle


    Hello!

    Suppose I have the files called:

    file - 1
    file - 2
    file - 55


    and I want to rename them, using a single command (imagine there are many more files, so manual editing is tedious) to:

    file - 01
    file - 02
    file - 55


    So, only the single-digit filenames should get an extra 0.

    How do I do this using either regex or 'globbing' from the command line? I'm finding it hard to figure out, not familiar with either. Tried regex in gprename too -- it recognizes

    - \d


    but trying to replace that with

    - %1


    does not seem to work: it takes the %1 characters literally...?

    Thanks!

  2. #2
    Linux Enthusiast
    Join Date
    Jan 2005
    Location
    Saint Paul, MN
    Posts
    675
    I could do it in a second. if the files did not have spaces in the names.

    However, you might try:
    Code:
    ls -1 file\ -\ [0-9] | while read f; do mv "${f}" "${f%[0-9]}0${f#file - }"; done

  3. #3
    Just Joined!
    Join Date
    Oct 2013
    Posts
    4
    Great, thank you, alf55! (Funny, my example happened to have the number 55, too...)

    Is there a quick explanation for how this works? I don't understand:
    • How exactly do the | and while read f work? I suppose the file name is stored in f? But then what if you wanted to refer to the file itself, not to its name?
    • How come the digit bit comes first, and the string bit last, in "${f%[0-9]}0${f#file - }", while the output is the other way around?

    No doubt the answers are somewhere in docs, but a little guidance would be very welcome. Thanks :)

  4. $spacer_open
    $spacer_close
  5. #4
    Linux Enthusiast
    Join Date
    Jan 2005
    Location
    Saint Paul, MN
    Posts
    675
    Let's look at the commands one at a time:

    I want to know the the files that match a pattern and decided to use the "ls" command. The option "-1" (dash one) says to report the file names one per line. Since the file names have spaces (a very bad idea same goes with $, !, (, ), [, ], &, *, and `) [stand_on_soapbox] Just because there are only two characters that can not be in a filename (and a directory name is a included here) because they cause the need of quoting. Do not use them! [/stand_on_soapbox]
    Since you have spaces in the name, I chose to use the "backqoute" method (the "backslash" is used a quote for a single character thus removing any special meaning for that character. The "space" (actually includes characters known as '[:blank:]') is a parameter separator.
    Code:
    ls -1 file\ -\ [0-9]
    The '[0-9]' is a character set of characters that can be matched for a single character. It could have been written as "[0123456789]" but the "-" can be used as a range. So this is is looking for file names that are like:
    Code:
    file - 0
    file - 1
    ...
    file - 9
    The "|" is the pipe operator and it takes the "stdout" of the left command and sends it into the right command via "stdin".
    Code:
    while read f; do mv "${f}" "${f%[0-9]}0${f#file - }"; done
    Is a loop construct using the "while condition; do code_block ; done"
    looks better to under stand in the form:
    Code:
    while read f; do
       mv "${f}" "${f%[0-9]}0${f#file - }"
    done
    The condition in this case is the exit status of the command "read" (it returns an exit status of 0 (true) if it was able to read and non-0 when it gets an end-of-file or runs into an error. If you desire to see if the value of a variable is "ok" then the condition would be:
    Code:
    while [ "${variable}" = "ok" ] ; do
       block of code
    done
    The semicolon ";" is used to separate statements when putting them on the same line.

    The block of code is the command:
    Code:
    mv "${f}" "${f%[0-9]}0${f#file - }"
    Uses the move "mv" command to rename the file and since there are (or could be spaces in the name), I make sure to quote the two arguments (this time with the double quote character as I am using the value of the variable "f" (while the "read" command in the while condition set to the line being read). The "${f}" is just the value of "f" and the "${f%[0-9]}" portion is the value of "f' with the smallest decimal digit (if any) removed from the value ("%%" would be the longest trailing digits, "#" would be the smallest match from the start of the string, and "##" would be longest match from the begining of the string). Hint on remembering which is left or right end of the string on a US keyboard is simply look at their relation on the keyboard to each other.
    bash syntax is very picky and care needs to be watched on spaces and semicolons. A little practice and it will be easy to do.

    Code:
    if condition ; then
       block_of_code
    fi
    Is the same as:
    Code:
    if condition
    then
       block_of__code
    fi
    Since I write in about 20 languages, I like the upper form rather than the lower form. In either case, you need to be able to understand both as many people write scripts and use the form that they like.

    This single line command is really a script that never was saved to a file.

    Does this help?


    As in programming writing fancy commands makes you look at what you have and in Unix/Linux have lots of commands that can be used in the creation of your script.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •