When working with log files in Linux, we often encounter inconsistently named files that need standardization. The specific case we're addressing involves files with patterns like:
System-Log-01-01-2009-NODATA.txt
Something-Log-01-01-2009-NODATA.txt
Our goal is to transform these into a more standardized format:
system.20090101.log
something.20090101.log
The Linux rename
command (also known as prename
on some systems) is perfect for this task. It supports Perl-compatible regular expressions, allowing for complex pattern matching and substitution.
Here's the complete command that handles all requirements:
rename -n 's/([^-]+)-Log-([0-9]{2})-([0-9]{2})-([0-9]{4}).*\.txt/lc($1) . ".$4$3$2.log"/e' *.txt
Let's examine each component:
([^-]+) # Captures everything before first hyphen as group 1
-Log- # Matches literal "-Log-"
([0-9]{2}) # Captures month (group 2)
-([0-9]{2}) # Captures day (group 3)
-([0-9]{4}) # Captures year (group 4)
.*\.txt # Matches remaining text before .txt extension
Always use -n
(dry-run) first to verify changes:
rename -n 's/([^-]+)-Log-([0-9]{2})-([0-9]{2})-([0-9]{4}).*\.txt/lc($1) . ".$4$3$2.log"/e' *.txt
Sample output would show what changes would occur without actually renaming files.
Remove -n
when ready to perform the actual rename:
rename 's/([^-]+)-Log-([0-9]{2})-([0-9]{2})-([0-9]{4}).*\.txt/lc($1) . ".$4$3$2.log"/e' *.txt
For systems without rename
, you can use a bash loop:
for file in *.txt; do
if [[ $file =~ ([^-]+)-Log-([0-9]{2})-([0-9]{2})-([0-9]{4}) ]]; then
prefix=$(echo "${BASH_REMATCH[1]}" | tr '[:upper:]' '[:lower:]')
newname="${prefix}.${BASH_REMATCH[4]}${BASH_REMATCH[2]}${BASH_REMATCH[3]}.log"
mv -i "$file" "$newname"
fi
done
For files that might have different patterns, add additional checks:
rename 's/([^-]+)-Log-([0-9]{2})-([0-9]{2})-([0-9]{4}).*\.txt/lc($1) . ".$4$3$2.log"/e' *.txt || \
rename 's/([^-]+)_Log_([0-9]{2})_([0-9]{2})_([0-9]{4}).*\.txt/lc($1) . ".$4$3$2.log"/e' *.txt
When dealing with log files in Linux, you might encounter inconsistently named files like:
System-Log-01-01-2009-NODATA.txt
Something-Log-01-01-2009-NODATA.txt
What we want is a standardized format:
system.20090101.log
something.20090101.log
We can use a combination of Bash scripting and rename
(or prename
) with Perl regular expressions to achieve this transformation in one command.
The most efficient way is using the Perl-based rename utility (sometimes called prename):
rename 's/(.*)-Log-(\d{2})-(\d{2})-(\d{4})-NODATA\.txt\$/lc($1) . "." . $4 . $3 . $2 . ".log"/e' *.txt
Let's break down this command:
s/ # Start substitution
(.*) # Capture the prefix (System/Something)
-Log- # Match literal text
(\d{2})-(\d{2})-(\d{4}) # Capture day, month, year
-NODATA\.txt\$ # Match ending
/ # Replacement starts
lc($1) # Lowercase the first capture group
. "." . $4 . $3 . $2 # Reorder date to YYYYMMDD
. ".log" # New extension
/e # Evaluate replacement as Perl code
If you don't have rename/prename available, here's a pure Bash solution:
for file in *-NODATA.txt; do
prefix=${file%%-Log-*}
date_part=${file#*-Log-}
day=${date_part:0:2}
month=${date_part:3:2}
year=${date_part:6:4}
newname=$(echo "$prefix" | tr '[:upper:]' '[:lower:]')."$year$month$day".log
mv "$file" "$newname"
done
For more complex scenarios where filenames might vary, consider adding validation:
for file in *.txt; do
if [[ $file =~ ^(.*)-Log-([0-9]{2})-([0-9]{2})-([0-9]{4})-NODATA\.txt$ ]]; then
prefix=${BASH_REMATCH[1]}
day=${BASH_REMATCH[2]}
month=${BASH_REMATCH[3]}
year=${BASH_REMATCH[4]}
newname=$(echo "$prefix" | tr '[:upper:]' '[:lower:]')."$year$month$day".log
mv -- "$file" "$newname"
fi
done
Always test your rename operations first. With rename/prename:
rename -n 's/.../' *.txt
With Bash loop, add an echo before mv:
echo mv "$file" "$newname"