5

I want to make a script to "unhide" all files and directories inside a certain directory in one go, e.g. ./unhide test.

test/
├── sub1
│   └── .hiddenfile1
└── sub2
    └── .hiddendir
        ├── .hiddendirsub
        ├── .hiddenfile2
        └── not.hidden

Desired outcome:

test/
├── sub1
│   └── hiddenfile1
└── sub2
    └── hiddendir
        ├── hiddendirsub
        ├── hiddenfile2
        └── not.hidden

How can I do that?

I'm still new to this and I've been trying to find a solution using find, but stuck at -exec, and rename (or mv) because I'm still struggling to understand how this combination works. :( So, I'll appreciate if anyone here can give a solution and detailed explanation as well. Thanks.

  • You are aware that changing the filename (removing the leading dot) might break functionality that expect the hidden filename? – Soren A Nov 2 '18 at 14:40
  • @SorenA Yes. Fortunately, I'm not going to change/mess with those kinds of files (files that "need" to be hidden). – Lukman Hakim Nov 2 '18 at 15:00
7

You can do that with find as follows:

find /path/to/test -depth -name ".*" -execdir rename -n 's|/\.|/|' {} +

This only prints the renaming actions, if it shows what you want remove the -n option.

Explanations

  • -depth – lets find process from bottom to top, renaming files before their parent directories
  • -name ".*" – lets find search for files (everything is a file) beginning with a dot – the dot is literal here, as this is not a regular expression
  • -execdir … + – execute in the matched file‘s directory
  • rename 's|/\.|/|' {} – replace the first occurence of “/.” from the matched file’s path (find’s placeholder for which is {}) with “/”, essentially removing the dot from the beginning of the filename
    This would be e.g. rename 's|/\.|/|' ./.hiddenfile1 in your case, this would be renamed to ./hiddenfile1.

Example run

$ tree -a
.
├── sub1
│   └── .hiddenfile1
└── sub2
    └── .hiddendir
        ├── .hiddendirsub
        ├── .hiddenfile2
        └── not.hidden

$ find ~/test -depth -name ".*" -execdir rename 's|/\.|/|' {} +
$ tree -a
.
├── sub1
│   └── hiddenfile1
└── sub2
    └── hiddendir
        ├── hiddendirsub
        ├── hiddenfile2
        └── not.hidden

Usage in a script

In a script you can simply use positional parameters instead of the path, it may be relative or absolute – just remember to quote correctly:

#!/bin/bash
find "$1" -depth -name ".*" -execdir rename -n 's|/\.|/|' {} +
3
$ tree -a test
test
├── .alsohidden
├── sub1
│   └── .hiddenfile1
└── sub2
    ├── .hiddendirsub
    ├── .hiddenfile2
    └── not.hidden

3 directories, 4 files

$ find test/ -depth -name ".*" -exec rename -n 's|(.*/)\.(.*)|$1$2|' {} +
rename(test/.alsohidden, test/alsohidden)
rename(test/sub2/.hiddendirsub, test/sub2/hiddendirsub)
rename(test/sub2/.hiddenfile2, test/sub2/hiddenfile2)
rename(test/sub1/.hiddenfile1, test/sub1/hiddenfile1)
  • find test/ begin paths with test/, search recursively from this path
  • -depth stolen from dessert's answer - seemed like a good idea
  • -name ".*" filename starts with .
  • -exec command {} + run command on the found files, constructing an argument list.
  • rename -n do not do anything, only show what will be done (remove -n after testing to actually rename)
  • s|old|new| replace old with new
  • (.*/)\.(.*) save all the chars up to and including the last directory separator/, skip a literal ., save all the subsequent characters
  • $1$2 print the saved patterns
  • 1
    Why does rename accept “|” as a delimiter, but not “_”? Should I ask a new question? ;) – dessert Nov 2 '18 at 13:53
  • 1
    @dessert omg I just assumed it would allow, I always use | if I need / in the regex (unless I need |). That would be a good question. Maybe it has to be a non-word char or there's a set of permitted chars – Zanna Nov 2 '18 at 14:00
  • 1
    @Zanna I think this needs -depth like the answer from @dessert. I tried it and it gave errors Can't rename test/sub2/.hiddendir/.hiddenfile2 test/sub2/.hiddendir/hiddenfile2: No such file or directory Can't rename test/sub2/.hiddendir/.hiddendirsub test/sub2/.hiddendir/hiddendirsub: No such file or directory – Lukman Hakim Nov 2 '18 at 15:05
  • @LukmanHakim thanks for your advice - I added it, although I couldn't get those errors I'm probably just lucky :) – Zanna Nov 2 '18 at 18:06
  • @Zanna try with a path where both a directory and a file inside it are to be renamed, e.g. test/.dir/.file – without -depth, test/.dir` is renamed first and renaming test/.dir/.file then obviously fails. – dessert Nov 4 '18 at 19:01

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.