Skip to content

Instantly share code, notes, and snippets.

@FelisDiligens
Last active December 7, 2023 12:45
Show Gist options
  • Save FelisDiligens/9886ede399e3a321797b43ab53a371f9 to your computer and use it in GitHub Desktop.
Save FelisDiligens/9886ede399e3a321797b43ab53a371f9 to your computer and use it in GitHub Desktop.
Python script to hide dotfiles under Windows
#!/usr/bin/env python3
import os
import sys
import platform
import subprocess
import ctypes
import argparse
from pathlib import Path
# https://stackoverflow.com/a/25432403
# https://stackoverflow.com/a/43441940
FILE_ATTRIBUTE_HIDDEN = 0x02
def set_hidden_attrib(file_name):
"""
Adds the NTFS hidden attribute to a file or folder.
Calls the SetFileAttributesW WinApi function or attrib.exe, depending on what's available.
"""
# Check if running under Windows natively:
if platform.system() == "Windows":
# https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-setfileattributesw
ret = ctypes.windll.kernel32.SetFileAttributesW(
str(file_name), FILE_ATTRIBUTE_HIDDEN
)
if not ret: # There was an error.
raise ctypes.WinError()
# Check if running under Cygwin. Try ATTRIB.exe:
elif platform.system().startswith("CYGWIN_NT"):
# Get native file path:
native_file_name = (
subprocess.check_output(["cygpath", "-w", str(file_name)])
.decode("UTF-8")
.strip()
)
# Call ATTRIB.exe with native file path:
# print(f"> ATTRIB.exe +H /D /L \"{native_file_name}\"")
subprocess.check_call(["attrib", "+H", "/D", "/L", native_file_name])
def hide_files(folder, depth=1, verbose=False):
"""
Hides all files and folders that start with a ".".
Reads .hidden file, if it exists and hides all files and folders written in that file (newline separated).
If depth > 1, will look into subdirectories recursively.
"""
if depth < 1:
return
# Read .hidden:
hidden = []
try:
if (folder / ".hidden").is_file():
with open(folder / ".hidden", "r") as f:
hidden = list(filter(None, f.read().split("\n")))
except Exception as ex:
print(f"Couldn't read file \"{folder / '.hidden'}\": {ex}")
# Hide matching files and folders:
try:
for entry in os.listdir(folder):
entry_path = folder / entry
if entry.startswith(".") or entry.endswith("~") or entry in hidden:
try:
if verbose:
print(entry_path)
set_hidden_attrib(entry_path)
except Exception as ex:
print(f'Couldn\'t hide "{entry_path}": {ex}')
if depth > 1 and entry_path.is_dir():
hide_files(entry_path, depth - 1, verbose)
except Exception as ex:
print(f'Couldn\'t search directory "{folder}": {ex}')
def range_limited_int_type(arg):
"""Type function for argparse"""
try:
n = int(arg)
except ValueError:
raise argparse.ArgumentTypeError("argument must be a number")
if n < 1:
raise argparse.ArgumentTypeError("argument must be at least 1")
return n
if __name__ == "__main__":
if platform.system() != "Windows" and not platform.system().startswith("CYGWIN_NT"):
print("Run this script under Windows!")
sys.exit(-1)
parser = argparse.ArgumentParser(
prog="hide-files.py",
description='Adds the NTFS hidden attribute to all files and folders that start with a ".".\n'
+ "Reads .hidden file, if it exists and hides all files and folders written in that file (newline separated).",
epilog="uses ~ if no paths were given",
)
parser.add_argument("folders", nargs="*", default=[Path.home()])
parser.add_argument(
"-v",
"--verbose",
action="store_true",
help="print which files/folders get hidden",
)
parser.add_argument(
"-d",
"--depth",
type=range_limited_int_type,
default=1,
help="search subdirectories",
)
args = parser.parse_args()
folders = [Path(p) for p in args.folders]
for folder in folders:
hide_files(folder, args.depth, args.verbose)
@FelisDiligens
Copy link
Author

FelisDiligens commented Apr 29, 2023

In Linux, and other UNIX like systems, a file with a name that starts by a dot (.) is considered a hidden file. Some file managers also hide files that end with a tilde (~), which are considered backup files. Source Typically files ending with a ~ are created by editors like emacs, nano, or vi. Source

If you create the file .hidden in a directory in which you want to hide files or folders and enter the names of files or folders there without specifying the path, they will not be displayed in the file manager. Source

Since a lot of developers simply put dot files and folders into the %UserProfile% under Windows without adding the hidden attribute, I wrote this python script to have a tidy home folder once again.

  • usage: ./hide-files.py [paths]
    • example: ./hide-files.py "C:\Users\you" "D:" "E:\Workspace"
    • uses ~ if no paths were given
    • can search recursively by adding --depth parameter, e.g. --depth 2
  • works under Windows (native) or Cygwin
  • reads .hidden file and hides all matching files/folders (just like in Nautilus/Nemo/Dolphin/etc.)

Help output:

$ python ./hide-files.py -h
usage: hide-files.py [-h] [-v] [-d DEPTH] [folders ...]

Adds the NTFS hidden attribute to all files and folders that start with a ".". Reads .hidden file, if it exists and
hides all files and folders written in that file (newline separated).

positional arguments:
  folders

optional arguments:
  -h, --help            show this help message and exit
  -v, --verbose         print which files/folders get hidden
  -d DEPTH, --depth DEPTH
                        search subdirectories

uses ~ if no paths were given

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment