1

I have some .mp3 files that have trailing $ signs in the extensions which I want to remove, they are named:

1.mp3$$
2.mp3$$
etc.

and they must be

1.mp3
etc.

I tried

for f in *; do a="(echo $f | sed s/mp3$$/mp3/)"; mv "$f" "$a"; done

but that gave the error message of

mv: rename 1.mp3$$ to (echo 1.mp3$$ | sed s/mp3582/mp3/): No such file or directory

for each file.

Then after reading the answer to this question, I tried

for i in *mp3$$; do mv "$i" "mp3"; done

but that not only didn't work, it resulted in all files but one being deleted, and only one files being left called "mp3". It also gave the error message of

mv: rename *mp3582 to mp3: No such file or directory

Luckily I've still got the original files and still want to rename them, but how to change "mp3$$" to "mp3" by command line?

Also, why did the 2nd command above result in all files but one being deleted?

And why do the error messages contain the sequence "3582", which is not in any of the files' names?

P.S. I've also already tried using \$ instead of $ in the command to escape the $ character, but that didn't work either.

LB7979
  • 113

3 Answers3

3

$$ expands to the process ID of the shell in all POSIX-compatible shells.

Changing *.mp3$$ files to *.mp3 could be done like this:

for i in *.mp3\$\$; do mv -i "$i" "${i%%\$\$}"; done 

Your second command line was apparently executed by a shell with a PID of 582, so it was expanded to:

for i in *mp3582; do mv "$i" "mp3"; done

Then, as nothing would probably match the wildcard expression, the loop would execute only once with the wildcard expression placed as-is in the $i variable:

mv "*mp3582" "mp3" 
mv: rename *mp3582 to mp3: No such file or directory

Your second attempt also would rename each file in turn to the fixed filename "mp3", overwriting any previous file by that name if "mp3" is not a directory that already exists. That might explain why all files but one was deleted, if you also attempted it with in the escaped form:

for i in *mp3\$\$; do mv "$i" "mp3"; done

This would do:

mv <filename1>.mp3$$ mp3
mv <filename2>.mp3$$ mp3   # overwrites the previous "mp3"
mv <filename3>.mp3$$ mp3   # overwrites the previous "mp3" again
...

Your original attempt is actually fixable:

for f in *; do a="(echo $f | sed s/mp3$$/mp3/)"; mv "$f" "$a"; done   # your version
for f in *; do a="$(echo \"$f\" | sed s/mp3\\$\\$/mp3/)"; mv "$f" "$a"; done    # fixed version

A command or pipeline within just parentheses won't be executed: you'll need the $( ... ) construct for that. Add double quotes around it to handle output that might contain whitespace, semicolons or other tricky characters.

Then, the $ characters within the $( ... ) get passed through the interpreter twice: once when the entire command line is parsed, then another time when the $( ... ) construct is parsed for execution. On each pass, backslash-escaped characters get the backslash removed. So you'll need double backslashes to preserve the "mp3$$" for sed.

Also, $f needs double quotes around it in case filenames contain whitespace or other tricky characters... and the double quotes need to be escaped as they're within another set of double quotes and we want to preserve them for the $( ... ) pass.

telcoM
  • 96,466
0

you can try this one

for file in *.mp3\$\$; do mv -v "$file" "${file/.mp3\$\$/.mp3}"; done
Ganesh_
  • 181
0

Using the perl rename utility (not the rename command from util-linux, that has completely different and incompatible command-line options):

rename -n -v 's/\$+//g' *.mp3*

That will remove all $ characters from anywhere in the filenames.

If you want it to remove $ characters only if they are at the end of the filenames:

rename -n -v 's/\$+$//' *.mp3*

BTW, the above commands use rename's -n option as a dry run to show you what rename would do without actually renaming anything. When you are sure it does exactly what you want without any unintended consequences, remove the -n from the command line. Optionally remove the -v (verbose) option too.


The perl rename utility is in the rename package on Debian and derived distros. This package contains both the File::Rename CPAN module and the rename utility.

Earlier versions of the perl package on Debian used to include an old version of rename, with some notable bugs. If you have that version installed, you should install the rename package ASAP.

This perl-based rename is also sometimes installed as either file-rename or prename. prename is usually the older buggy version.

cas
  • 78,579