I propose here a solution to easily tag files, search them and edit tags.
After searching the web, as of August 2024 there doesn't seem to be actively maintained software that allows to have a tagging files ystem on any linux distribution (a promising one was TMSU, but like other pieces of software, the last commit on the repo is several years old).
So extended attributes (xattrs) might indeed be a future-robust enough way of achieving this.
So here is my solution. In short, it amounts to simply use the setfattr and getfattr commands, via command aliases to ease the tagging, searching, removing, ensuring consistency of tags: addtag, findtag, removetag, cleantag (some of them may only work with Zsh, but Bash compatibility is attempted whenever possible).
Note: I suggest using the user.xdg.tags namespace of xattrs to tag files or directories, since it seems to be the standard in many applications for user tags (see ArchWiki: User extended attributes).
Tagging files
To tag myfile with mytag1 and mytag2, we simply append the value of user.xdg.tags to mytag1,mytag2, by comma-separating the new tags. The below command is used like so:
addtag mytag1 mytag2 myfile
function addtag()
{ ## Add tag(s) to file.
## The specified tags are appended to the comma-separated string value of the user.xdg.tags namespace of extended attributes.
## Usage: addtag TAG [...] FILE
tags_existing=$(getfattr -n user.xdg.tags --only-value "$argv[-1]")
setfattr -n user.xdg.tags -v "${tags_existing},${(%j:,:)argv[1,-2]}" "$argv[-1]"
}
Searching files by tags
Below is a useful shell alias to find files or directories that have at least one of the specified tags (Version 1) or all the specified tags (Version 2). This assumes the tags are values of user.xdg.tags.
Version 1: Files matching any of the specified tags
For example, to find all files/directories from the current path that have a tag containing the string str1 or-inclusive a tag containing the string str2:
findtag str1 str2 ./
function findtag()
{ # Search for tags in the user.xdg.tags namespace of extended attributes.
# Usage: findtag TAG [...] PATH
local IFS="|"
find "$*[-1]" -exec getfattr -n user.xdg.tags '{}' \+ 2>/dev/null | grep -Ei "${*[1,-2]}"
}
function findtag()
{ # Search for tags in the user.xdg.tags namespace of extended attributes.
# Usage: findtag TAG [...] PATH
find "$argv[-1]" -exec getfattr -n user.xdg.tags '{}' \+ 2>/dev/null | grep -B 1 -Ei "${(%j:|:)argv[1,-2]}"
}
The only difference between the Bash and the Zsh version is the code to process the user-provided tags.
Version 2: Files matching all the specified tags
For example, to find all files/directories from the current path that have a tag containing the string str1 and a tag containing the string str2:
findtag str1 str2 ./
Warning: Zsh assumed.
function findtag()
{ # Search for tags in the user.xdg.tags namespace of extended attributes.
# Usage: findtag TAG [...] PATH
find "$argv[-1]" -exec getfattr -n user.xdg.tags '{}' \+ 2>/dev/null | grep -B 1 -Pi "(?<=user.xdg.tags=\")(?=.*${(%j:)(?=.*:)argv[1,-2]})"
}
Cleaning tags
This shell alias allows to avoid duplicate tags, or unwanted commas between tags in the tag string. This assumes the tags are values of user.xdg.tags.
Warning: Zsh assumed.
function cleantag()
{ ## Clean the tag string of file(s).
## Tags are assumed to be substrings of the value of the user.xdg.tags extended attributes namespace.
## Usage: cleantag FILEPATH
local tags_existing file
for file in "$@"; do
if getfattr -n user.xdg.tags --only-value $file &>/dev/null; then
tags_existing=$(getfattr -n user.xdg.tags --only-value $file)
tags_existing=${tags_existing//,,##/,} # Removing duplicate commas.
tags_existing=$(echo "$tags_existing" | tr ',' '\n' | sort -u | tr '\n' ',') # Removing duplicate tags.
tags_existing=${tags_existing/#,/} # Removing leading comma.
tags_existing=${tags_existing/%,/} # Removing trailing comma.
setfattr -n user.xdg.tags -v "${tags_existing}" $file
fi
done
}
Removing tags
This shell alias allows to remove individual tags. This assumes the tags are values of user.xdg.tags.
Warning: Zsh assumed.
function removetag()
{ ## Remove tag(s) from file.
## The specified tags are removed from the comma-separated string value of the user.xdg.tags extended attributes namespace.
## Usage: removetag TAG [...] FILEPATH
local tags_remaining tag
setopt localoptions extendedglob
if getfattr -n user.xdg.tags --only-value "$argv[-1]" &>/dev/null; then
tags_remaining=$(getfattr -n user.xdg.tags --only-value "$argv[-1]")
for tag in "$@[1,-2]"; do
tags_remaining=${tags_remaining//$tag/}
echo "Tag \"${tag}\" and its duplicates were removed."
done
tags_remaining=${tags_remaining//,,##/,} # Removing duplicate commas.
tags_remaining=${tags_remaining/#,/} # Removing leading comma.
tags_remaining=${tags_remaining/%,/} # Removing trailing comma.
setfattr -n user.xdg.tags -v "${tags_remaining}" "$argv[-1]"
fi
}
.tagsfile along with the original.