Search code examples
bashseddirectorymoveabsolute-path

bash change absolute path in file line by line for script creation


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


Solution

  • 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)