Violapterin
Violapterin

Reputation: 347

Attempt to automatically rename date in filenames, using Bash regex

I have some 400 plain text files which are my writing pieces, all named as such,

A prose (June 30, 2013)
A sad story (Dec. 1, 2016)

The filename parts are mostly in Chinese or contain Chinese, but I suppose this is irrelevant. I want to rename them to be

130630_A prose
161201_A sad story

by using Bash script.

This is probably the first time I write a Bash script. I am reasonably familiar with c++, but I find Bash hard to learn. While I have basic familiarity with Vim's regex, I gather that Bash's regex is similar. I mainly relied on this reference on wildcards.

My attempt is as shown:

#! /usr/bin/env bash

EXT=.txt


for name_old in "*${EXT} */*${EXT} */*/*${EXT}"; do
   str_title=$(expr "${name_old}" : '\(.*\)(.*).*')
   str_date=$(expr "${name_old}" : '.*(\(.*\)).*')
   str_y=$(expr "${str_date}" : '.*\([0-9]*\).*')
   str_m=$(expr "${str_date}" : '.*\([a-zA-Z]*\).*')
   str_d=$(expr "${str_date}" : '.*\([0-9]*,\).*')

   if [ ${#str_y} -eq 0 ] || [ ${#str_m} -eq 0 ] || [ ${#str_d} -eq 0 ] ; then
      continue
   fi

   name_new="${name_new}${str_y:2:3}"

   convert_month "${str_m}" hold
   name_new="${name_new}hold"

   if [ ${#str_d} -eq 1 ]; then
      name_new="${name_new}0${str_d:0:0}"
   elif [ ${#str_d} -eq 2 ]; then
      name_new="${name_new}${str_d:0:1}"
   fi

   name_new="${name_new}_${str_title}"
   mv name_old name_new
done

function convert_month
{
   if [ "$(expr "$(1)" : '.*\(Jan\).*')" -ne "" ]; then
      $(2)=01
   # ... omitted for brevity
   elif [ "$(expr "$(1)" : '.*\(Dec\).*')" -ne "" ]; then
      $(2)=12
   fi
}

It seems nothing happens. Some syntax is just my guess, so I think many steps many be wrong. But I cannot find on Stack Overflow an example as complicated as this, and I have no clear idea how to debug.

Upvotes: 1

Views: 149

Answers (2)

Violapterin
Violapterin

Reputation: 347

There, there.

In the regex part, I have considered the possibility that in the parenthesis, there may be some other characters in between. For example, Something (new--Dec. 1, 2013).txt The reader may modify to suit you need. There is a good reference for Bash regex.

#! /usr/bin/env bash

# To renames, say, `Something (Dec. 13, 2016).txt` to `161213_Something.txt`
# First parse the `()`-enclosed part in filename, called `str_date`
# if `2016` is a substr of DATE, set YY
# if `Dec` is a substr of DATE, set MM
# if `13,` is a substr of DATE, set DD

# first define functions

function convert_year
{
   local tmp=$1
   if [ "${#tmp}" -eq 4 ]; then
      echo "${tmp:2:2}"
   fi
}

function convert_month
{
   if [ "${1}" == "Jan" ]; then
      echo "01"
   elif [ "${1}" == "Feb" ]; then
      echo "02"
   elif [ "${1}" == "Mar" ]; then
      echo "03"
   elif [ "${1}" == "Apr" ]; then
      echo "04"
   elif [ "${1}" == "May" ]; then
      echo "05"
   elif [ "${1}" == "une" ]; then
      echo "06"
   elif [ "${1}" == "uly" ]; then
      echo "07"
   elif [ "${1}" == "Aug" ]; then
      echo "08"
   elif [ "${1}" == "Sep" ]; then
      echo "09"
   elif [ "${1}" == "Oct" ]; then
      echo "10"
   elif [ "${1}" == "Nov" ]; then
      echo "11"
   elif [ "${1}" == "Dec" ]; then
      echo "12"
   fi
}

function convert_day
{
   local tmp=$1
   if [ ${#tmp} -eq 2 ]; then
      echo "0${tmp:0:1}"
   elif [ ${#tmp} -eq 3 ]; then
      echo "${tmp:0:2}"
   fi
}

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 

EXT=.txt

find . -maxdepth 3 -type f -name '*.txt' | while read -r name_file; do
   if [[ $name_file =~ (.*)/([^/_]*)_*\(.*([A-Za-z]{3}).*_([0-9]*,).*([0-9]{4})\).*${EXT} ]]
   then
       yy=$(convert_year "${BASH_REMATCH[5]}")
       mm=$(convert_month "${BASH_REMATCH[3]}")
       dd=$(convert_day "${BASH_REMATCH[4]}")

       name_new="${BASH_REMATCH[1]}/${yy}${mm}${dd}_${BASH_REMATCH[2]}${EXT}"
       mv ${name_file} ${name_new}
   fi
done

Upvotes: 0

SLePort
SLePort

Reputation: 15461

You can use the date command to convert the date part:

#!/usr/bin/env bash
for file in *.txt; do
  str_date="$(grep -oP '\(\K[^\)]+'  <<< "$file")";
  date_str=$(date -d"$str_date" +%y%m%d);
  alphachars="${file/ (*/}";
  ext="${file##*.}";
  mv "$file" "${date_str}_${alphachars}.${ext}";
done

With a recursive find:

#!/usr/bin/env bash
find . -maxdepth 3 -type f -name '*.txt' | while read -r file; do
  filename=${file##*/};
  dir=${file%/*};
  str_date="$(grep -oP '\(\K[^\)]+'  <<< "$filename")";
  date_str=$(date -d"$str_date" +%y%m%d);
  alphachars="${filename/ (*/}";
  ext="${filename##*.}";
  mv "${file}" "${dir}/${date_str}_${alphachars}.${ext}";
done

Upvotes: 1

Related Questions