4

Following this post as a reference, I'm able to run a find and sed command without it throwing an error, but the filenames remain unchanged.

Trying to strip pronunciation_de_ from all mp3s in the current directory:

pronunciation_de_wählen.mp3
pronunciation_de_wange.mp3
pronunciation_de_weil.mp3
pronunciation_de_werden.mp3
pronunciation_de_zentrum.mp3

Before troubleshooting the command, here's a quick sanity check: find . -name "*.mp3"
It returns all the mp3s in the current directory. Continuing on now that we know this part works...

sed --version returns sed (GNU sed) 4.4

I run find . -name "*.mp3" -exec sed -i 's/pronunciation_de_//g' {} \;

To make sure I'm fully understanding what's happening:

find . runs the find command in the current directory.
-name "*.mp3" returns any .mp3 filetypes.
-exec executes the next command you type.
sed -i The -i switch means work on the actual files, not a (temporary) copy.

For 's/old_word/new_word/g':

  • The s sets sed to substitute mode.
  • /old_word is the word you want to replace.
  • /new_word is the word you want to replace with. In my example it'll be blank.
  • /g apply the replacement to all matches (not just the first).

{} this string will be replaced by the filename during every iteration.
\; the semicolon terminates the find command. The backslash escapes the semicolon character in case the shell tries to interpret it literally.

Most of this information I'm getting from random blogs and Stack Exchange posts:

Understanding the -exec option of find
What is meaning of {} + in find's -exec command?

I really wanted to take my time and experiment and research before posting this almost-certainly-duplicate question but I'm completely stuck!

6
  • 5
    sed does not rename files, it replaces text inside them; it is possible, but unlikely, that your MP3 files are now corrupted. Jul 19, 2019 at 5:41
  • It's ok, I backed them up before experimenting. Jul 19, 2019 at 5:42
  • 2
    Read man rename for what you are actually looking for
    – Philippos
    Jul 19, 2019 at 5:52
  • @Philippos dear god, I spent a few hours reading blogs and experimenting only to discover that what I was attempting was futile. Thanks for the tip. I read the man page and got it done in less than 20 seconds. Curious, in the first link I made in my post, some who commented reported it works for them. Jul 19, 2019 at 6:30
  • 3
    Surely it did help them, because they are doing something completely different: They don't want to rename files, but change words inside of files. And for this purpose find with exec and sed is the perfect combination. (-: Glad I could help, anyhow.
    – Philippos
    Jul 19, 2019 at 6:41

4 Answers 4

1

You can use find -exec ... to do the replacement in a one-line shell script.

find . -name "*.mp3" -type f -exec bash -c 'mv "$1" "${1/pronunciation_de_}"' bash {} \;

The filename {} is passed as parameter $1 to the executed bash process and ${1/pronunciation_de_} makes use of bash's parameter expansion capabilities and replaces the first occurrence of pronunciation_de_ in $1 with an empty string.

Additional option -type f makes sure to match only regular files.

3
  • You don't have to launch so many bash processes with: -exec bash 'for file; do mv "$file" "${file%pronunciation_de_}"; done' bash {} + Jul 19, 2019 at 12:26
  • Note that if there is a subdirectory called, e.g., pronunciation_de_files, then this will remove the wrong substring from the pathnames.
    – Kusalananda
    Apr 6, 2020 at 7:10
  • @glennjackman The % in the parameter expansion in your comment removes a suffix string, not a prefix string.
    – Kusalananda
    Apr 6, 2020 at 7:11
1

If you want to remove the pronunciation_de_ prefix from all files with names that end in .mp3, then you should search for files with names matching pronunciation_de_*.mp3 to be sure that you don't modify other filenames by mistake (however unlikely this may be).

Don't use line-oriented text-editing tools like sed on filenames. The shell knows how to delete prefix and suffix strings from filenames efficiently.

By using find, you could solve your issue with

find . -type f -name 'pronunciation_de_*.mp3' -exec sh -c '
    for pathname do
        newname=${pathname##*/}              # removes directory path, leaves filename
        newname=${newname#pronunciation_de_} # deletes the prefix string

        mv -i "$pathname" "${pathname%/*}/$newname"
    done' sh {} +

The find command generates pathnames of files that pass the -type and -name tests and gives these in batches to a short inline sh -c script.

This script iterates over the given pathnames, and for each pathname it creates a new name by

  1. removing the initial directory path, turning a pathname like ./some/path/pronunciation_de_werden.mp3 into pronunciation_de_werden.mp3, and then
  2. removing the prefix string pronunciation_de_, turning pronunciation_de_werden.mp3 into werden.mp3.

The mv command renames the original file by moving it to the new name in the original directory (${pathname%/*} will expand to the original pathname's directory path).

1

Your:

find . -name "*.mp3" -exec sed -i 's/pronunciation_de_//g' {} \;

finds files (of any type including regular, directory, symlink, fifo...) whose name ends in .mp3 in the current working and below and for each one runs sed in in-place mode¹ with s/pronunciation_de_//g as the program and the found file as the file to operate on.

However sed is stream editor. Its job is to edit a stream of text. When given a file as argument the stream is the contents of the file, not its name, sed does not rename files. The standard command to rename files is mv and there are several other commands that can including some specialised in batch renaming (which will also have the ability to find files and edit their name builtin) such as mmv, rename, zmv.

While sed could be used to edit the name of the file if we managed to feed that name as a stream, it would be quite cumbersome and hard to do reliably.

LC_ALL=C find . -depth -name 'pronunciation_de_*.mp3' -exec sh -c '
  ret=0
  for file do
    new_file=$(
      printf '%s\n' "$file" |
        sed "
          :1
          \$!{
            N
            b1
          }
          s|/pronunciation_de_\([^/]*\)\$|/\1|
        "
    )
    mv -i -- "$file" "$newfile" || ret=$?
  done
  exit "$ret"' sh {} +

(untested)

Note how we need:

  • LC_ALL=C to avoid problem with file names not encoded in the locale's character encoding.
  • -depth to process leaves before the branches they're on.
  • to run a shell in order to be able to feed the file path to sed via a pipe (|) and collect its output into a shell variable via another pipe ($(...)).
  • as sed works on one line at a time and a file paths can be made of any number of lines, we need to tell sed to accumulate all the lines in its pattern space before doing the substitution.
  • take care that the substitution be done on the basename of the file only, not earlier path components.
  • give the opportunity to the user to avoid data loss with the -i option. Though note that it will still not help if there's a file called ./pronunciation_de_weil.mp3 and a directory called ./weil.mp3 in which case the file will silently be renamed to ./weil.mp3/pronunciation_de_weil.mp3. The GNU implementation of mv has a -T option that can alleviate that.

Compare with zmv (an autoloadable function of the zsh shell):

autoload zmv
zmv '(**/)pronunciation_de_(*.mp3)' '$1$2'

Which also has the benefit of:

  • checking for potential conflicts prior to doing any renaming
  • skipping hidden files (if you also want to rename hidden files, you can change it to zmv '(**/)pronunciation_de_(*.mp3)(#qD)' '$1$2').

¹ assuming a sed implementation that supports that non-standard extension and for which the following argument is not taken as the backup suffix.

-1

For batch operation, I prefer GNU parallel.

ls -1 *.mp3 |
  sed 's/pronunciation_de_//' |
  parallel mv pronunciation_de_{} {}

sed removed the pronunciation_de_ in filenames. Therefore {} will be like wählen.mp3, wange.mp3, ...

1
  • 1
    Or: parallel mv {} {=s/pronunciation_de_//=} ::: *mp3
    – Ole Tange
    Jul 27, 2022 at 21:57

You must log in to answer this question.

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