【问题标题】:Attempt to automatically rename date in filenames, using Bash regex尝试使用 Bash 正则表达式自动重命名文件名中的日期
【发布时间】:2017-05-30 07:59:53
【问题描述】:

我有大约 400 个纯文本文件,它们是我的写作作品,都以这样的方式命名,

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

文件名部分大多是中文或包含中文,但我想这无关紧要。 我想将它们重命名为

130630_A prose
161201_A sad story

通过使用 Bash 脚本。

这可能是我第一次编写 Bash 脚本。我对 c++ 相当熟悉,但我发现 Bash 很难学习。 虽然我对 Vim 的正则表达式有基本的了解,但我认为 Bash 的正则表达式是相似的。 我主要依靠this reference on wildcards

我的尝试如图:

#! /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
}

似乎什么都没有发生。一些语法只是我的猜测,所以我认为很多步骤都是错误的。 但是我在 Stack Overflow 上找不到这么复杂的例子,我也不清楚如何调试。

【问题讨论】:

  • 第二个look输入行看起来很可疑,A sad story (Dec. 1, 2016)Dec后面的.不应该在那里?
  • 是的,它就在那里。这是我的命名约定,或者是——我已经下定决心改变。是的,它很穷。故事是我很遗憾包含了这么多空格和句号,并为日期选择了如此冗长的命名方式。现在我想在 CLI 中编译它们,遇到了巨大的困难。

标签: regex bash shell parsing rename


【解决方案1】:

可以使用date命令转换日期部分:

#!/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

使用递归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

【讨论】:

  • 抱歉,这不起作用。当我运行它时,shell 会打印出usage: grep...usage: date...。当我尝试date_str=$(date -d"Dec. 1, 2013" +%y%m%d)echo ${date_str} 时,它是空的。
【解决方案2】:

那里,那里。

在正则表达式部分,我考虑了括号中可能存在其他字符的可能性。例如,Something (new--Dec. 1, 2013).txt 读者可以根据需要进行修改。 Bash 正则表达式有一个很好的reference

#! /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

【讨论】:

    猜你喜欢
    • 2016-08-22
    • 2018-11-12
    • 2020-01-19
    • 2011-09-14
    • 2018-04-24
    • 2012-08-31
    • 2017-08-10
    • 1970-01-01
    相关资源
    最近更新 更多