rename file, mkdir, mv file

Forum rules
Before you post please read how to get help
Post Reply
chowse
Level 1
Level 1
Posts: 22
Joined: Sun May 14, 2017 10:02 am

rename file, mkdir, mv file

Post by chowse » Sun Mar 18, 2018 10:32 pm

Hi,
I have several directories with hundreds of files in each.
I must rename each file, create a directory of (almost) the same name, and move the file into that directory.

Here is what I have so far (not much):

#!/bin/bash

for file in /foo/*
do
rename the file so that the last 4 characters before the . are enclosed in braces: mv foobar 2017.ext to foobar (2017).ext
make a directory the same name as the file, minus the . and extension: mkdir foobar (2017)
move the file into that directory: mv foobar (2017).ext to foobar (2017)/foobar (2017).ext
done

I could use some help with the actual commands

Thanks,
Charles

User avatar
lsemmens
Level 5
Level 5
Posts: 973
Joined: Wed Sep 10, 2014 9:07 pm
Location: Rural South Australia

Re: rename file, mkdir, mv file

Post by lsemmens » Mon Mar 19, 2018 12:35 am

There's a Windoze utilitiy called Bulk File Rename. I'm sure that Linux has an equivalent. Google may well be your friend here.
Kernel: 4.15.0-36-generic x86_64 bits: 64
Desktop: Cinnamon 3.8.9
Distro: Linux Mint 19 Tara

Laptop T4500 Dualcore 4Gb RAM
Server AMD Phenom 9650 - GEForce 9400GT 6Gb RAM
+ three other Linux Mint machines
Out of my mind - please leave a message

rene
Level 8
Level 8
Posts: 2172
Joined: Sun Mar 27, 2016 6:58 pm

Re: rename file, mkdir, mv file

Post by rene » Mon Mar 19, 2018 1:20 am

But if you insist, something like

Code: Select all

for FILE in foo/*; do
	[ -f "$FILE" ] || continue
	
	NAME="${FILE%.*}"
	BASE="${NAME::-4}"
	BASE="$BASE (${NAME#$BASE})"
	
	echo mkdir "$BASE"
	echo mv "$FILE" "$BASE/${BASE##*/}${FILE#$NAME}"
done
Remove the "echo" statements once you're satisfied this does what you want. Note that these kinds of string substitutions are quite sensitive to input format; tweak as necessary with help of man bash, 749g if not fully right for some of your files. Does work for your example format:

Code: Select all

$ for FILE in foo/foobar2017.ext; do NAME=${FILE%.*}; BASE=${NAME::-4}; BASE="$BASE (${NAME#$BASE})"; echo mkdir "$BASE"; echo mv "$FILE" "$BASE/${BASE##*/}${FILE#$NAME}"; done
mkdir foo/foobar (2017)
mv foo/foobar2017.ext foo/foobar (2017)/foobar (2017).ext
[EDIT] looking a little closer you seem to have specified a space in the input filename; "foobar 2017.ext" rather than "foobar2017.ext". You'd simply remove the space before the opening brace in the above second BASE substitution to compensate -- but that's, then, an example of the mentioned sensitivity.

chowse
Level 1
Level 1
Posts: 22
Joined: Sun May 14, 2017 10:02 am

Re: rename file, mkdir, mv file

Post by chowse » Mon Mar 19, 2018 7:48 am

rene,
Works perfect! You don't know how much time and trouble you've saved me. Thank you kindly.

Charles

lmuserx4849

Re: rename file, mkdir, mv file

Post by lmuserx4849 » Fri Mar 23, 2018 12:24 am

Just for fun, in addition to using the cool bash parameter expansion above, I tried it with matching the filename to a regular expression and using BASH_REMATCH to get the parts.
The other method, uses the IFS to split the filename into pieces before putting it back together again to form the new name. I used $'\x20'" to represent the space, so if I look at the code in the future I know I clearly meant it to be a space :-)

I wasn't sure what "enclosed in braces" meant when I saw "foobar (2017).ext". I've always gone by:
( ) parentheses
[ ] brackets (hard "k" sound has hard corners :)))
{ } braces

Code: Select all

#!/bin/bash
# Use regular expression to parse file name into required pieces
#
declare -r pf='(' pe=')'
declare -- fname='' parts='' year='' ext='' newname=''
declare -r regexfname='^([^[:blank:]]+[[:blank:]])([[:digit:]]{4})(\.[[:alpha:]]{3})$'

while read -re line; do
  [[ "$line" =~ $regexfname ]]
  rc=$?
  case $rc in
    0) fname="${BASH_REMATCH[1]}"
       year="${BASH_REMATCH[2]}"
       ext="${BASH_REMATCH[3]}"
       echo "fname=$fname year=$year ext=$ext"
       newname="$fname$pf$year$pe"
       echo "newname=$newname"
       echo 'do other processing'
       ;;
    1) echo "filename does not match regex [$rc]"
       ;;
    *) echo "regex error [$rc]"
       ;;
  esac
done <<EOF
foobar 2017.ext
EOF

echo; echo

# Use IFS to split words.
# This eats the space and dot (.), so you have to put them back in.
#
#declare -r pf='(' pe=')'
declare -- fname='' parts='' year='' ext='' newname=''

while IFS=$'\x20' read -re fname parts; do
  IFS=. read year ext<<<$parts
  echo "fname=$fname year=$year ext=$ext"
  newname="$fname"$'\x20'"$pf$year$pe"
  echo "newname=$newname"
  echo 'do other processing'
  # mkdir "$newname"
  # mv "$fname $parts" "$newname/$newname.$ext"
done <<EOF
foobar 2017.ext
EOF

#######################
# Remove the last 3 lines

#done <<EOF
#foobar 2017.ext
#EOF

# And replace with

# done < <(find foo -maxdepth 1 -type f -printf '%f\n')

Post Reply

Return to “Scripts & Bash”