2

Just as the title suggests, I want to scan a directory and all the subdirectories and remove all newline characters from the filenames.

The current solution proposed here is not working: Remove newlines in file names

This is what I got when I used it:

$ find -name $'*\n*' -exec rename $'s|\n| |g' '{}' \; 
rename: not enough arguments 
Try 'rename --help' for more information.

if I add a *

$ find -name $'*\n*' -exec rename $'s|\n| |g' '{}' * \;

this does not produce any error messages (just accepts the command) but it does not rename the file in the subdirectory

5
  • 1
    In which way your cited answer isn't working? Can you add some console output of it?
    – 41754
    Sep 18, 2019 at 12:01
  • 1
    Are you sure it's newline \n you want to remove? Apparently, in the question you linked to, the relevant characted was "carriage return" \r instead. Maybe that's what you need too?
    – NickD
    Sep 18, 2019 at 12:11
  • Please edit your question and put additional details there.
    – Murphy
    Sep 18, 2019 at 12:28
  • Based on the error message, it looks like rename on your system is the util-linux implementation (whereas the linked answer assumes one of the Perl-based implementations). What type or flavor of Unix / Linux are you using? Sep 18, 2019 at 13:17
  • Thanks @steeldriver, I am using the latest Manjaro KDE. I was not aware there is a difference. When I run $ rename --version I get rename from util-linux 2.34 Sep 18, 2019 at 13:33

2 Answers 2

3

Make sure you use one of the perl-based rename variants, sometimes called prename, not the one from util-linux (sometimes called rename.ul).

The one from util-linux is much more limited (despite being much newer), uses a different syntax, can only replace fixed strings and can't replace all the occurrences of a string.

Here, I'd use perl rename variants, make sure to only convert newlines in the file names, not the directory components leading to them, and use -depth to process leaves before branches.

perl recognises \n as meaning a newline, so you don't need to use $'...' for its code argument (though it's fine if you do contrary to what I initially thought).

find . -depth -name $'*\n*' -exec rename '
  s{[^/]*\z}{$& =~ y/\n/ /r}e' '{}' +

Or use -execdir if available (though that will run at least one rename per directory with files with \n within):

find . -depth -name $'*\n*' -execdir rename 'y/\n/ /' {} +

Or you could use zsh's zmv:

autoload zmv
NL=$'\n'
zmv "(**/)(*$NL*)(#qD)" '$1${2//$NL/ }'

(which contrary to the find-based approaches will still work even if file names contain sequences of bytes that don't form valid characters. It will also bail out without doing anything if it detects a conflict).

2

The usage of the rename command provided by util-linux is different from that given in the answer you linked. In particular, it requires a separate from and to argument rather than a single sed-style replacement command - hence the error message not enough arguments.

SYNOPSIS
       rename [options] expression replacement file...

So in this case you would need

find . -name $'*\n*' -exec rename $'\n' ' ' {} \;

See also:

and for a simple usage comparison

2
  • But that (very limited) rename implementation only replaces once occurrence per filename. Sep 18, 2019 at 13:52
  • You'd also want -depth or it wouldn't properly if there were directories with newlines in their path. Sep 18, 2019 at 13:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.