1

My music library consists of folders named after the following pattern :

METAGENRE - Subgenre(s) - ARTIST - Album (year)

I want to switch the year and album "tags" in the folders' names for some bands/artists of whom I own the whole discography, so it gets sorted by chronological order of release instead of album name. I the meantime I can also remove the parentheses around the year tag, i.e.:

METAGENRE - Subgenre(s) - ARTIST - year Album

For instance : METAL - Black, Français - PESTE NOIRE - Split (2018)

Should become : METAL - Black, Français - PESTE NOIRE - 2018 Split

I only have a minimal experience with the shell. Looking for help here and elsewhere I could come up with this yet imperfect one-liner ("echo" is for testing prior to renaming and avoid potential hell, I also retranscribe on several lines here for readability) :

% for f in *PESTE\ NOIRE* ;
do echo
     mv "$f"
     "$(echo $f | sed `s/- ([A-Za-z]+) \(([0-9]{4})\)$/- \2 \1/p`)"
     ;
done

When running this command in the terminal, all I get is : zsh: missing identifier after `+'

What am I doing wrong ?

EDIT: (using ' instead of ` quotes)

% for f in *PESTE\ NOIRE* ; do echo mv "$f" "$(echo $f | sed 's/- ([A-Za-z]+) \(([0-9]{4})\)$/- \2 \1/')" ; done

works but now I get another error:

sed: 1: "s/- ([A-Za-z]+) \(([0-9 ...": \2 not defined in the RE

Hence :

% for f in *PESTE\ NOIRE* ; do echo mv "$f" "$(echo $f | sed 's/- \([A-Za-z]+\) \\\(\([0-9]{4}\)\\\)$/- \2 \1/')" ; done

Returns no error, but the folder remains unrenamed.

1
  • Please add an answer for the answer. Or better still give the ✔ to the person who best helped you. You can edit their answer to give them the direct credit rather than creating your own Commented Sep 7, 2023 at 16:57

2 Answers 2

2

In zsh, you'd use zmv to rename files:

autoload -Uz zmv # best in ~/.zshrc
zmv -n '(*PESTE NOIRE*-)(*) \((<1900-2100>)\)' '$1 $3$2'

Remove the -n (dry-run) to actually do it.

Some problems in your code:

  • `...` is a deprecated form of command substitution from sh/csh, and like the modern $(...) form (from ksh), also needs to be quoted. But it shouldn't be used as it comes with many problems of its own. In any case, it's not used to quote text, only to expand to the output of some command. The best kind of quotes for sed code which tends to contain characters such as \ or $ are single quotes ('...') inside which in Bourne-like shells including zsh, no character is treated specially by the shell.
  • to print some text verbatim, use print -r - $f (ksh-style, though you'd need quotes in ksh) or printf '%s\n' $f (POSIX-style, would also need quotes in sh), or echo -E - $f (zsh-specific) not echo $f which breaks on values of $f starting with - or containing backslash. See also the <<< $f here-string syntax.
  • +, {4} and (...) are extended (or perl) regexp operator, so you'd need -E or -P options for sed, where supported. The basic regexp equivalents are \{1,\}, \{4\} and \(...\) (which explains why your \2 is not matched). Some sed implementations also support \+.
  • [A-Za-z] matches characters that sort between A and Z and a and z depending on the sed implementation it may or may not match on ç or œ, it doesn't with zsh's own globs. ź would likely sort after z so be excluded. Same for letters in other scripts. Use [[:alpha:]] to match on letters (or more generally character used in human language words). Many Albums would likely have space or numbers or ., '... Using [^-] for any character other than - may be a better approach. Or like in zmv above, rely on the fact that the preceding * will be greedy.
  • remember that sed is line-based and text-based, so doesn't process the file name as a whole but each line of the file name individually (would only be a problem for file names containing newlines or non-characters such as characters encoded in a charset other than that of the locale).
  • When using variable data in arguments to commands, you need -- to mark the end of options. mv $f ... would treat $f as an option if it started with -.

So if you had to use sed (but you certainly don't in zsh for that), addressing those would look like:

for old in *'PESTE NOIRE'*; do
  new=$(print -r - $f | sed  '
    :1
    $!{
      N;b1
    }
    s/\(.*-\)\(.*\) ([[:digit:]]\{4\})$/\1 \3\2/'
  ) && [[ $old != $new ]] &&
    mv -i -- $old $new
done
2
  • again a good answer. But are you paid by Big-zsh? ^^ (I am almost tempted to try it, but resist as I haven't encountered it on the work machines I work with) Commented Sep 7, 2023 at 15:29
  • 1
    @OlivierDulac, the OP is using zsh. My condolences to you for having to work on systems without zsh :-) Commented Sep 7, 2023 at 15:33
1

Use singlequotes ( ' ) instead of backquotes ( ` ) to quote the sed expression

3

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .