1

I have a folder which contains folders which contains files of the form

resultstatsDF_iris_2017-05-26--21-33-35-437096_methodnr-2_percentage-0.05_seed-0.wcr

I want to remove the date and time for each of these, so in this case get

resultstatsDF_iris_methodnr-2_percentage-0.05_seed-0.wcr

How do I do this in bash?

Jeff Schaller
  • 67,283
  • 35
  • 116
  • 255
Make42
  • 657

5 Answers5

1

Use find + prename (Perl rename) commands :

find yourfolder -type f -name "*.wcr" -exec prename 's/^(.+)_[0-9]{4}-[0-9]{2}-[0-9]{2}[^_]+(_.*)$/$1$2/' {} +

To view prename result without action add -n option (print names of files to be renamed, but don't rename):

man rename

  • [0-9]{4}-[0-9]{2}-[0-9]{2} - pattern pointing to date-like substring (e.g. 2017-05-26)
1
for i in ./*/*; do
  j=`echo "$i" | cut -d'_' -f1-2,4-`
  mv "$i" "$j"
done

That's not the most elegant solution, but it works just fine, assuming that you want to rename all the files in subsubdirectories and they all match this pattern.

ADDB
  • 486
1

If you really want to do it "with bash", then

find . -name '*.wcr' -execdir bash -c '
  shopt -s extglob; for f; do echo mv -- "$f" "${f/_+([0-9-])_/_}"; done
' bash {} +

This relies on extended glob +([0-9-]) that matches one or more occurrences of characters in the set [0-9-]

You could make the replacement more specific e.g. ${f/_2017+([0-9-])_/_} if simply matching digits and dashes is too generic.

Note: remove the echo once you're certain that it's doing what you want.

steeldriver
  • 81,074
1

Using the perl rename command (either variant, but not the util-linux rename):

find . -type f -name \*.wcr -exec rename 's/_\d{4}-(\d{2}-){2}-(\d{2}-){3}\d+_/_/' {} +
0

If the files are always one directory level deep, then you can iterate over them with something like for x in */*_20*_*. Which pattern to use depends on what other files might be present that you don't want to rename. The pattern I just gave assumes that the date starts with 20 and that all files whose name contains _20 and another undescore after that should be renamed.

You can do the renaming with a shell loop, using parameter expansion constructs to build the new file name.

for old_name in ./*/*_20_*_*; do
  base=${old_name##*/}        # remove the directory part
  prefix=${base%%_20*}        # remove everything from _20
  suffix=${base#*_20}         # remove everything up to _20
  suffix=${suffix#*_}         # ... then everything before the first remaining _
  mv "$old_name" "${old_name%/*}/${prefix}_${suffix}"
done

If the files are at varying depths, in bash ≥4.3, you can run shopt -s globstar then for x in **/*_20*_*; …. The pattern ** matches any directory depth if globstar is turned on. This also works in bash 4.0–4.2 with the caveat that it also traverses symbolic links to directories. This also works in zsh and ksh, without the caveat, out of the box in zsh and with set -o globstar in ksh.