1

I have directory structure like the following:

.
├── ParentDirectory
│   ├── ChildDirectory1
│   │   ├── 01-File.txt
│   │   ├── 02-File.txt
│   │   ├──      03-File.adoc
│   │   ├──    04-File.md
│   │   └──     05-File.txt
│   ├──     ChildDirectory2
│   │   ├── 01-File.txt
│   │   ├── 02-File.txt
│   │   ├──    03-File.adoc
│   │   ├── 04-File.txt
│   │   ├── 05-File.txt
│   │   └──     06-File.md
│   ├── ChildDirectory3
│   │   ├──     01-File.txt
│   │   ├── 02-File.txt
│   │   ├── 03-File.adoc
│   │   ├── 04-File.md
│   │   ├──    05-File.md
│   │   └── 06-File.txt

You may have noticed that some file and directory has leading whitespace. What you may not have noticed is some file and directory has trailing whitespace as well.

For trailing whitespace I tried:

% find . -exec rename 's/ *$//' {} +

I have to run this command multiple times. On first run it rename parent directory. On second run it rename the child. My directory structure actually goes much deeper. So, running a command multiple times is not a good solution.

For leading whitespace find . -exec rename 's/^ *//' {} + does not work at all.

How can I remove all leading and trailing whitespace from both file and directory names recursively?

7
  • 2
    Did you try depth-first search? You can do that by supplying the -depth flag to find.
    – shilch
    Dec 12, 2020 at 7:04
  • Yes now find . -depth -exec rename 's/^ *//' {} + works. Still find . -depth -exec rename 's/^ *//' {} + not working. Dec 12, 2020 at 7:47
  • @blueray You just said that a command was and was not working, at the same time.
    – Kusalananda
    Dec 12, 2020 at 9:07
  • Also, at no point do you actually say what "not working" means. Does it produce errors? What are those errors? Does it rename files in the wrong manner? Not at all? Does your filenames contain tab characters?
    – Kusalananda
    Dec 12, 2020 at 9:08
  • @Kusalananda the command runs without any error. But it does not remove the leading whitespace. Dec 12, 2020 at 9:55

1 Answer 1

2

With zsh:

autoload -Uz zmv # best in ~/.zshrc
zmv -n '(**/)(*)' '$1${${2##[[:space:]]#}%%[[:space:]]#}'

(remove -n (dry-run) if happy).

Add a (#qD) at the end of the pattern, if you also want to process hidden files and files in hidden directories.

With rename, that would be something like:

find . -depth ! -name . -exec rename -n '
  my ($dir, $base) = m{(.*)/(.*)}s;
  $base =~ s/^\s*//;
  $base =~ s/\s*$//;
  $_ = "$dir/$base";' {} +

Though note that \s (contrary to zsh's [[:space:]]) only matches on ASCII whitespace, not the other whitespace characters in your locale. rename also doesn't have any of zmv's safeguards to check for conflicts before starting the renaming.

In any case, note that on several systems, including GNU ones, the non-breaking-space character (U+00A0) is not considered whitespace on the ground that it's not meant to delimit words, so it won't be removed, even with the zmv approach. If you have such characters, you could strip them with:

zmv -n '(**/)(*)' $'$1${${2##[[:space:]\ua0]#}%%[[:space:]\ua0]#}'
3
  • By the way, what does my ($dir, $base) = m{(.*)/(.*)}s; do? Dec 12, 2020 at 7:57
  • 1
    @blueray, it splits the path into directory ($dir) and filename ($base) parts to get the equivalent of $1 and $2 in zmv. Dec 12, 2020 at 8:00
  • Thank you very much. Dec 12, 2020 at 8:01

You must log in to answer this question.

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