This article will guide you through four methods to hide files on Finder in macOS. These methods include using a dot-file, chflags, SetFile, and xattr.

Hands-on

1. Dot-file

The first method to hide a file in Finder involves renaming it with a dot prefix.

/bin/mv <FILE> .<FILE>

2. chflags

The second method uses the chflags command, which modifies the UF_HIDDEN flag of a file.

/usr/bin/chflags hidden <FILE>
UF_HIDDEN: The file or directory is not intended to be displayed to the user.

https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/chflags.2.html

Example

You can observe the effect of this flag by using the stat command or Python.

❯ /usr/bin/chflags hidden b
❯ stat .a b
... 4096 0 0 .a
... 4096 0 0x8000 b

This 0x8000 indicates that file b has UF_HIDDEN flag set.

You can also use write a custom Python script to check the flag.

❯ python3 fflag.py b
b:  UF_HIDDEN

This is the Python script to check the flag.

'''
Check file flags of a given file.
'''
import os
import stat
import argparse

def get_file_flags(filepath):
    try:
        st = os.stat(filepath)
    except FileNotFoundError:
        return f"File not found: {filepath}"

    flags = {
        "UF_NODUMP": stat.UF_NODUMP,
        "UF_IMMUTABLE": stat.UF_IMMUTABLE,
        "UF_APPEND": stat.UF_APPEND,
        "UF_HIDDEN": stat.UF_HIDDEN,
        "SF_ARCHIVED": stat.SF_ARCHIVED,
        "SF_IMMUTABLE": stat.SF_IMMUTABLE,
        "SF_APPEND": stat.SF_APPEND,
    }

    set_flags = [flag for flag, value in flags.items() if st.st_flags & value]
    return set_flags

def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("filepath", type=str)
    args = parser.parse_args()
    filepath = args.filepath

    file_flags = get_file_flags(filepath)
    print(f"{filepath}:\t", ", ".join(file_flags))

if __name__ == "__main__":
    main()

https://gist.github.com/rand-tech/0385e35a5d4bf342ea7e5ad9c0edefc0

3. SetFile

The third method involves using SetFile. This command modifies the UF_HIDDEN flag and also sets the file’s com.apple.FinderInfo with a custom value.

/usr/bin/SetFile -a V <FILE>

Example

❯ /usr/bin/SetFile -a V c
❯ xattr -lx c
com.apple.FinderInfo:
00000000  00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00  |........@.......|
00000010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  |................|
00000020
❯ python3 fflag.py c
c:  UF_HIDDEN

4. xattr

The fourth method uses xattr. This technique stores the actual file content in the file’s extended attribute.

Ref: Storing a binary data in xattr - Deep Dive into xattr on macOS

value="$(xxd -c 0 -p <FILE_TO_SMUGGLE>)"

KEY="rand0m.key.$RANDOM"
xattr -x -w "$KEY" "$value" <FILE_TO_HIDE>

Example

❯ /bin/ls -alt@ $FILE
-rw-r--r--@ 1 rand0m  staff  0 Dec 10 22:13 d
 rand0m.key.29226 187120
❯ xattr -x -p $KEY $FILE|xxd -r -p|file -
/dev/stdin: Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e]
(null) (for architecture x86_64):