-3

In my log directory I have 600+ log files that have a naming scheme as:

abc.log.DDMMMYYYY

For example:

abc.log.01Nov2017
abc.log.02Nov2017
abc.log.10Dec2017
abc.log.21Jan2018
abc.log.22Jan2018
abc.log.23Jan2018

I am looking a way to rename all these files as...

YYYY-MM-DD.abc.log

The month name in file name must convert to month number. (Jan = 01, Feb = 02 ...)

For example:

2017-11-01.abc.log
2017-11-02.abc.log
2017-12-10.abc.log
2018-01-21.abc.log
2018-01-22.abc.log
2018-01-23.abc.log

How can I rename all these files in bash?

shane
  • 1
  • 5
  • This would be *way easier* and *robust* with a scripting language that supports a proper calendar module. Perl, Python, Ruby -- take you pick. Otherwise awk. – dawg Dec 22 '18 at 04:01

3 Answers3

2
#!/bin/bash -e

# Create kludged associative array (for bash versions prior to 4 -- 4 has
# built-in associative arrays).
i=0
for Month in Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec; do
    let i=i+1
    # Pad with leading zero and then take last two characters.
    Padded=0$i
    eval Key$Month=${Padded: -2:2}
done

# Iterate on all files whose names match *.log.*.
for File in *.log.*; do
    # Match to pattern with expected date format.
    if [[ ! $File =~ ^(.*)\.log\.([0-3][0-9])([A-Z][a-z][a-z])([0-9]{4})$ ]]; then
        echo "$File does not match pattern."
    else
        # Extract matched name and date.
        Name=${BASH_REMATCH[1]}
        Day=${BASH_REMATCH[2]}
        InMonth=${BASH_REMATCH[3]}
        Year=${BASH_REMATCH[4]}

        # Convert month name abbreviation to month number.
        # number %m, day number %d).
        eval OutMonth=\$Key$InMonth
        NewName="$Year-$OutMonth-$Day.$Name.log"

        # Inform user.
        echo "Will rename $File to $NewName."

        # Rename.
        mv "$File" "$NewName"
    fi
done

This is locale sensitive, of course. And it expects four-digit dates, so it will break in the year 10,000. And you could add various error checks.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • thanks for your prompt reply.and script. when execute the script I get error " date: invalid option -- 'j'" I have removed the -j option and now i am getting error " $ date: extra operand ‘+%Y-%m-%d’" – shane Dec 22 '18 at 06:23
  • @shane: Sorry, the standard Unix `date` does not include all the nice features the macOS version does. (It is pretty handy for date manipulations, like moving forward or backward from dates by given amounts of days or other units or finding the most recent Sunday, the first Tuesday of the month, et cetera.) However, since we only need to replace a month name abbreviation with a number, that is fairly simple to implement. I have updated the script. – Eric Postpischil Dec 22 '18 at 12:12
  • Work like a charm. Thank you so much Eric, really appreciate it. – shane Dec 22 '18 at 15:54
1

As you have requested batch-file solution, here is a possible solution:

@echo off
setlocal EnableDelayedExpansion

rem Set Month Numbers:
set "Jan=01"
set "Feb=02"
set "Mar=03"
set "Apr=04"
set "May=05"
set "Jun=06"
set "Jul=07"
set "Aug=08"
set "Sep=09"
set "Oct=10"
set "Nov=11"
set "Dec=12"

rem Main Loop to rename files:
for %%A IN (*.log.*) do (
     for /f "delims=." %%B IN ("%%~xA") do (
          set "extension=%%B"
          call ren "%%A" "!extension:~5!-%%!extension:~2,-4!%%-!extension:~0,-7!.abc.log"
     )
)

Let me explain break it down:

  • First we set MMM variables to the requested format: MM.
  • Now, we come to the main loop.
    • We loop through all files in the current folder (%cd%) which contain .log.. We do this using * wildcard.
      • Then, we loop in the extension of each file found.
        • We set the extension (without the dot (.)) in the variable extension.
        • After that, we rename file found in first loop (%%A) with the strings found and analyzed.

To better understand how these commands work, I suggest you to open a cmd and type the following commands:

  • set /?
  • rem /?
  • for /?
  • ren /?

Some interesting references for further reading:

double-beep
  • 5,031
  • 17
  • 33
  • 41
  • Very good batch-file solution, Nicely explained. Thanks for sharing all these links, very informative. – shane Dec 22 '18 at 16:11
0

If gawk is available, please try:

#!/bin/bash

for f in abc.log.*; do echo "$f"; done | awk 'BEGIN {
    str="JanFebMarAprMayJunJulAugSepOctNovDec"
    for (i=1; i<=12; i++) s2n[substr(str, i*3-2, 3)] = sprintf("%02d", i)
    }
    {if (match($0, "(.+)\\.([0-9]{2})([A-Z][a-z]{2})([0-9]{4})", a))
    printf("%s%c%04d-%02d-%02d.%s%c", $0, 0, a[4], s2n[a[3]], a[2], a[1], 0)
    }
' | xargs -0 -n 2 mv
  • It maps the month name to month number via an array s2n.
  • The awk script outputs pairs of filenames like: abc.log.01Nov2017, 2017-11-01.abc.log... The filenames are separated by a null character.
  • The xargs reads the passed filenames two by two and performs mv command on them.
tshiono
  • 21,248
  • 2
  • 14
  • 22