How do I extract a file name into 2 parts, making one into directory and the other one inside of it?

旧时模样 提交于 2020-01-15 19:12:54

问题


I'm trying to sort all mp3 files by artist and name. At the moment, they're in 1 giant file name. E.g Artist - Song name.mp3 I want to convert this to Artist/Song name.mp3

Here's what I've tried so far. In this case, I was using a test file named "hi\ -\ hey":

#!/bin/bash
# filters by all .mp3 extensions in the current working directory 
for f in *.mp3; do
# extract artist and song name and remove spaces
        artist=${f%  -*}
        song=${f#*-  }
#make directory with the extracted artist name and move + rename the file into the directory
        mkdir -p $artist
        mv $f $artist/$song;
done

For some reason, it's creating a directory with the song name instead of the artist in addition to a load of errors:

mv: cannot move 'hey.mp3' to a subdirectory of itself, 'hey.mp3/hey.mp3'
mv: cannot stat 'hi': No such file or directory
mv: cannot stat '-': No such file or directory
mv: 'hey.mp3/hi' and 'hey.mp3/hi' are the same file
mv: cannot stat '-': No such file or directory

回答1:


By far the simplest way of doing this is to use rename a.k.a. Perl rename.

Basically, you want to replace the sequence SPACE-DASH-SPACE with a forward slash directory separator, so the command is:

rename --dry-run -p 's| - |/|' *mp3

Sample Output

'Artist - Song name.mp3' would be renamed to 'Artist/Song name.mp3'
'Artist B - Song name 2.mp3' would be renamed to 'Artist B/Song name 2.mp3'

If that looks correct, just remove --dry-run and run it again for real. The benefits of using rename are:

  • it can do a dry-run to test before you run for real
  • it will create all necessary directories with the -p option
  • it will not clobber (overwrite) files without warning
  • you have the full power of Perl available to you and can make your renaming as sophisticated as you wish.

Note that you can install on macOS with homebrew:

brew install rename



回答2:


Assuming there are many files, it's probably much faster to do this using pipes instead of a for loop. This has the additional advantage of avoiding complicated bash-specific syntax and using core unix/linux command line programs instead.

find *-*.mp3 |
  sed 's,\([^-]\+\)\s*-\s*\(.*\),mkdir -p "\1"; mv "&" "\1"/"\2",' |
  bash

Explanation:

This find to find all the files matching -.mp3 in the current directory.

This sed command changes each line to a command string, e.g.:

aaa - bbb.mp3
->
mkdir -p "aaa"; mv "aaa - bbb.mp3" "aaa"/"bbb.mp3"

The bash command runs each of those command strings.




回答3:


you can try this.

#!/usr/local/bin/bash
for f in *.mp3
do
artist=`echo $f | awk '{print $1}' FS=-`
song=`echo $f | awk '{print $2}' FS=-`
mkdir -p $artist
mv $artist-$song $song
mv $song ./$artist
done

here I am using two variable artist and song. as your test file name is "hi\ -\ hey" so I change the awk delimiter to "-" to store variable according to it.

we don't need to use awk..by using bash parameter expansion.... it is working.

#!/usr/local/bin/bash
for f in *.mp3
do
artist=`echo ${f%-*}`
song=`echo ${f#*-}`
mkdir -p $artist
mv $artist-$song $song
mv $song ./$artist
done


来源:https://stackoverflow.com/questions/58372435/how-do-i-extract-a-file-name-into-2-parts-making-one-into-directory-and-the-oth

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!