I recently converted all of my FLAC files to a lower sampling rate of 44.1 kHz and bit depth of 24 bits (because iPhone/iPod don't support anything above that) using XLD on my Mac OS 10.7 (Lion).
Although I told XLD to overwrote all previous files, XLD appended a (1)
at the end of very file like from
some_song.m4a
to
some_song(1).m4a
So now I want to remove that (1)
from all the FLAC files I converted.
I know I could have probably used some program or even an AppleScript to rename the files, but I wanted to learn using the old school way of command line.
I know that find . -name *\(1\).m4a
will grab all the converted FLAC file.
Next I know I have to do something with -exec
and mv
to rename all the found files. But what I can't figure out is how to keep the original filename and only remove the (1)
.
Maybe I need to do some group regex capturing to store the part of the filename that I don't want to modify? Or maybe it's not possible to do everything in one line and I should create a shell script (which I'm not that comfortable doing, but I'm willing to give it a try).
Any tips or suggestions are welcomed! Thanks!
-
5Why the downvote? It seems like a valid question...jshin47– jshin472012年05月11日 19:44:08 +00:00Commented May 11, 2012 at 19:44
5 Answers 5
Don't try to parse find
output except as a last resort. It is important to realize that on Unix file systems, file names are not strings (a common misconception) but rather binary blobs which can contain any character except /
and the null character. Parsing file names safely and correctly is enough of a pain that 99% of the time you'll just want to avoid doing it altogether (just look at how hairy the sed
expression in @yarek's answer is and even that doesn't cover all cases). Thankfully, in this case there is a much simpler approach:
find . -name '*(1).m4a' -execdir sh -c \
'for arg; do mv "$arg" "${arg%(1).m4a}".m4a; done' _ {} \+
-
neat approach yet as hairy as the sed expression :) +1yrk– yrk2012年05月11日 21:18:38 +00:00Commented May 11, 2012 at 21:18
-
1something's missing here... where's
done
?yrk– yrk2012年05月11日 21:27:29 +00:00Commented May 11, 2012 at 21:27 -
@yarek Thanks for the catch. The biggest difference between this and the
sed
pipe approach is this is much safer. It's still easier to read than regex backslash soup, provided you understand some basic shell scripting constructs.jw013– jw0132012年05月11日 23:23:15 +00:00Commented May 11, 2012 at 23:23 -
You're right; this is much cleaner. And the
$arg
variable makes it much easier to read.hobbes3– hobbes32012年05月12日 02:17:50 +00:00Commented May 12, 2012 at 2:17 -
1@DQdlM The snippet I posted above is just
find
invokingsh
with fairly portable POSIX syntax. For more details, you can check out the section on Parameter Expansion, the section on compound commands that explains thefor
loop, and the POSIXfind
spec. In addition to those resources, I'd be happy to answer any specific questions you have.jw013– jw0132012年05月12日 21:46:36 +00:00Commented May 12, 2012 at 21:46
On Debian and Ubuntu, I can use rename 's/\(1\)//' *.m4a
to solve your problem.
-
Weird, I can do
man rename
, but I actually don't haverename
:-bash: rename: command not found
(which rename
doesn't show anything).hobbes3– hobbes32012年05月11日 19:10:23 +00:00Commented May 11, 2012 at 19:10 -
@hobbes3 on the mac here
man -a rename
finds rename (2) - the syscall, and rename (n) - the TCL command. There are neither rename (1) nor the utility itself there.yrk– yrk2012年05月11日 19:18:02 +00:00Commented May 11, 2012 at 19:18 -
1IIRC,
rename
is a perl script included with the examples included in some perl installs, and a couple distros include it in the path because it's a convenient command. (@Hobbes perhaps you can find it on the internet)Kevin– Kevin2012年05月11日 22:47:32 +00:00Commented May 11, 2012 at 22:47 -
@Kevin on my system
rename
is normal binary (not perl). However another caveat is that not all versions ofrename
support regexes. The version I have for example only lets you do direct string replacements, egrename '(1)' '' *.m4a
phemmer– phemmer2012年05月11日 23:13:14 +00:00Commented May 11, 2012 at 23:13 -
1The Perl
rename
script is only installed by Debian and derivatives. Other Linux systems have a differentrename
utility (shown by Patrick). OSX has neither.Gilles 'SO- stop being evil'– Gilles 'SO- stop being evil'2012年05月11日 23:30:02 +00:00Commented May 11, 2012 at 23:30
In zsh, using zmv:
autoload zmv # you can put this line in your .zshrc
zmv '(*)\(1\)(*)' '1ドル2ドル'
In the second argument (the new name), 1ドル
and 2ドル
refer to the parenthesized groups (PATTERN)
in the source pattern. Another way of writing this renaming is
zmv '(*)' '${1/\(1\)/}'
The following approach gives you ability to preview/prune the generated commands before executing them, and it is very portable: it should work not only on a mac, not only with bash, and not only with GNU sed; even on systems without find
(1) command it is possible to substitute it with du
(1) without a trouble.
find . -name '*(1).m4a' |
sed 's/\(.*\)(1).m4a$/mv & 1円.m4a/'
If happy with the printed commands, re-run with | sh -x
appended.
If concerned about spaces in file names, add another s to escape all spaces:
find . -name '*(1).m4a' |
sed -e 's/ /\\ /g' -e 's/\(.*\)(1).m4a$/mv & 1円.m4a/'
If other special chars are expected, it gets a litle bit more tricky:
find . -name '*(1).m4a' |
sed -e "s/'/'\\\\''/g" -e 's/\(.*\)(1).m4a$/mv '\''&'\'' '\''1円.m4a'\'/
First function converts all '
into a form such that these are taken literally when in the middle of '...'
-escaped string. Second function generates mv commands whose arguments are enclosed within '...'
.
-
1That command isn't escaping the filename (the spaces,
(
, and all other special characters) or adding quotes around the filename. I tried modifying your command, but I couldn't figure out how to add quotes around both filenames for themv
command. One of the resulting output from your command ismv ./The Beatles - Let It Be (MFSL LP 1-109) 24-96 Vinyl Rip/01 Two Of Us(1).m4a ./The Beatles - Let It Be (MFSL LP 1-109) 24-96 Vinyl Rip/01 Two Of Us.m4a
. And if I run that command I get-bash: syntax error near unexpected token '('
.hobbes3– hobbes32012年05月11日 19:23:51 +00:00Commented May 11, 2012 at 19:23 -
this is supposed to be a homework :) but ok, i'll update the answeryrk– yrk2012年05月11日 19:26:53 +00:00Commented May 11, 2012 at 19:26
-
By the way GNU sed can execute command itself by adding
e
command after substitution. For examplefind . -name '*(1).m4a' | sed 's/\(.*\)(1).m4a$/mv & 1円.m4a/e'
will exexcute command without piping tosh -x
.rush– rush2012年05月11日 20:39:04 +00:00Commented May 11, 2012 at 20:39 -
@Rush that's the only sed which does that; and it needs to start the shell anyway, but a new one for each command (reminds the make's problem, doesn't it?)yrk– yrk2012年05月11日 20:46:48 +00:00Commented May 11, 2012 at 20:46
-
2I don't think we should be downvoting. It is a valid solution after all.hobbes3– hobbes32012年05月12日 02:17:29 +00:00Commented May 12, 2012 at 2:17
Here's a small script that does it:
for var in `find . -type f -name "*(1).m4a"`; do
new=`echo $var | cut -d'(' -f1`;
mv $var $new.m4a;
done
-
this one will choke on files with spaces in their names; also it requires big enough temporary storage for the
`find ...`
yrk– yrk2012年05月22日 16:31:17 +00:00Commented May 22, 2012 at 16:31
You must log in to answer this question.
Explore related questions
See similar questions with these tags.