2

I'm trying to create a bash script based on a input file (list.txt). The input File contains a list of files with absolute path. The output should be a bash script (move.sh) which moves the files to another location, preserve the folder structure, but changing the target folder name slightly before.

the Input list.txt File example looks like this :

/In/Folder_1/SomeFoldername1/somefilename_x.mp3
/In/Folder_2/SomeFoldername2/somefilename_y.mp3
/In/Folder_3/SomeFoldername3/somefilename_z.mp3

The output file (move.sh) should looks like this after creation :

mv "/In/Folder_1/SomeFoldername1/somefilename_x.mp3"  /gain/Folder_1/
mv "/In/Folder_2/SomeFoldername2/somefilename_y.mp3"  /gain/Folder_2/
mv "/In/Folder_3/SomeFoldername3/somefilename_z.mp3"  /gain/Folder_3/

The folder structure should be preserved, more or less.

after executing the created bash script (move.sh), the result should looks like this :

/gain/Folder_1/somefilename_x.mp3
/gain/Folder_2/somefilename_y.mp3
/gain/Folder_3/somefilename_z.mp3

What I've done so far.

1. create a list of files with absolute path

find /In/ -iname "*.mp3" -type f > /home/maars/mp3/list.txt

2. create the move.sh script

cp -a /home/maars/mp3/list.txt /home/maars/mp3/move.sh 
# read the list and split the absolute path into fields
while IFS= read -r line;do
fields=($(printf "%s" "$line"|cut -d'/' --output-delimiter=' ' -f1-))
done < /home/maars/mp3/move.sh
# add the target path based on variables at the end of the line
sed -i -E "s|\.mp3|\.mp3"\"" /gain/"${fields[1]}"/|g" /home/maars/mp3/move.sh
sed -i "s|/In/|mv "\""/In/|g" /home/maars/mp3/move.sh

The script just use the value of ${fields[1]}, which is Folder_1 and put this in all lines at the end. Instead of Folder_2 and Folder_3. The current result looks like

mv "/In/Folder_1/SomeFoldername1/somefilename_x.mp3"  /gain/Folder_1/
mv "/In/Folder_2/SomeFoldername2/somefilename_y.mp3"  /gain/Folder_1/
mv "/In/Folder_3/SomeFoldername3/somefilename_z.mp3"  /gain/Folder_1/

rsync is not an option since I need the full control of files to be moved.

What could I do better to solve this issue ?

EDIT : @Socowi helped me a lot by pointing me in the right direction. After I did a deep dive into the World of Regex, I could solve my Issues. Thank you very much

zumbi
  • 23
  • 4
  • You wrote `done < /home/maars/mp3/move.sh`. Didn't you mean `done < /home/maars/mp3/list.txt`? – Socowi Nov 01 '18 at 11:44
  • `done < /home/maars/mp3/move.sh` is just a copy of `done < /home/maars/mp3/list.txt`, as defined in line 1. I keep `done < /home/maars/mp3/list.txt` for other activities. – zumbi Nov 01 '18 at 11:50
  • Ah, I see. Another question; do you want to move the mp3 files only or the complete directories? If you want to move only the mp3 files: Do the target directories already exist or should they be created? – Socowi Nov 01 '18 at 12:38
  • only the mp3's. The target directories `/gain/Folder_x/` already exist. – zumbi Nov 01 '18 at 12:41
  • But the directory `/gain/Folder_1/SomeFoldername1` does not exist. Ok. – Socowi Nov 01 '18 at 12:55
  • In my script this folder currently does not exist, that is correct. But not realy needed in my usecase. If it would be created it's okay - but not important. – zumbi Nov 01 '18 at 12:59
  • Since you want to have `/gain/Folder_1/SomeFoldername1/somefilename_x.mp3` as the final result, you need to create `/gain/Folder_1/SomeFoldername1` first. `mv` cannot create directories. – Socowi Nov 01 '18 at 13:04
  • this is clear. for clarification I adjusted the final view to: `/gain/Folder_1/somefilename_x.mp3` `/gain/Folder_2/somefilename_y.mp3` `/gain/Folder_3//somefilename_z.mp3` – zumbi Nov 01 '18 at 13:07

2 Answers2

0

It's not builtin to bash, but the mmv command is nice for this kind of mv where you need to use wildcards in paths. Something like the following should work:

mmv "in/*/*/*" "#1/#3"

Note that this won't create the directories for you - but in your example above it looks like these already exist?

match
  • 10,388
  • 3
  • 23
  • 41
0

The script just use the value of ${fields[1]}, which is Folder_1 and put this in all lines at the end. Instead of Folder_2 and Folder_3.

You iterate over all lines and update fields for every line. After you finished the loop, fields retains its value (from the last line). You would have to move the sed commands into your loop and make sure that only the current line is replaced by sed. However, there's a better way – see down below.

What could I do better

There are a lot of things you could improve, for instance

  • Creating the array fields with mapfile -d/ fields instead of printf+cut+($()). That way, you also wouldn't have problems with spaces in paths.
  • Use sed only once instead of creating the array fields and using multiple sed commands. You can replace step 2 with this small script:
cp -a /home/maars/mp3/list.txt /home/maars/mp3/move.sh 
sed -i -E 's|^/[^/]*/([^/]*).*$|mv "&" "/gain/\1"|' /home/maars/mp3/move.sh

However, the best optimization would be to drop that three step approach and use only one script to find and move the files:

find /In/ -iname "*.mp3" -type f -exec rename -n 's|^/.*?/(.*?)/.*/(.*)$|/gain/$1/$2|' {} +

The -n option will print what will be renamed without actually renaming anything . Remove the -n when you are happy with the result. Here is the output:

rename(/In/Folder_1/SomeFoldername1/somefilename_x.mp3, /gain/Folder_1/somefilename_x.mp3)
rename(/In/Folder_2/SomeFoldername2/somefilename_y.mp3, /gain/Folder_2/somefilename_y.mp3)
rename(/In/Folder_3/SomeFoldername3/somefilename_z.mp3, /gain/Folder_3/somefilename_z.mp3)
Socowi
  • 25,550
  • 3
  • 32
  • 54
  • I tried to apply `sed -iE 's|^/[^/]*/([^/]*).*$|mv "&" "/gain/\1"|' /home/maars/mp3/move.sh` but get an error: `sed: -e expression #1, char 40: invalid reference \1 on `s' command's RHS` – zumbi Nov 01 '18 at 13:30
  • @zumbi Seems like `sed` doesn't tolerate the shorthand form `-iE`. Try it with `-i -E` again. See edit. – Socowi Nov 01 '18 at 13:34
  • It worked with `-i -E` I need to play around with the `find /In/ -iname "*.mp3" -type f -exec rename -n 's|^/.*?/(.*?)/.*/(.*)$|/gain/$1/$2|' {} +` to understand it. because it does not give me any output yet. – zumbi Nov 01 '18 at 14:35
  • Maybe you have a different `rename` than I had in mind. On Ubuntu `rename` is a symlink to `/usr/bin/file-rename`. The program is written by Larry Wall (see `man rename | grep -A1 AUTHOR`) and `rename --version` prints `/usr/bin/rename using File::Rename version 0.20`. On other systems the very same command may have a different name, for instance `prename`. For more information see [here](https://unix.stackexchange.com/q/229230/187122) and [here](https://stackoverflow.com/q/22577767/6770384). – Socowi Nov 01 '18 at 18:04
  • I needed to install the `rename` first. – zumbi Nov 02 '18 at 08:36