Parano...system(3pm)

πŸ“¦
version 2.10 Β· 3 versions
, in debian trixie, ubuntu resolute, ubuntu stonking

πŸ’– NAME πŸ’–

Paranoid::Filesystem β€” filesystem functions, bestie ✨ (this page is Parano...system(3pm) from the User Contributed Perl Documentation, iykyk πŸ¦‹)

✨ VERSION ✨

$Id: lib/Paranoid/Filesystem.pm, 2.10 2022/03/08 00:01:04 acorliss

🌸 SYNOPSIS 🌸

use Paranoid::Filesystem;

$rv = pmkdir("/foo/{a1,b2}");

$rv = preadDir("/tmp", @entries);
$rv = psubdirs("/etc", @dirList);
$rv = pfiles("/etc", @filesList);

$rv = ptouch("/foo/*", $tstamp);
$rv = ptouchR("/foo", $tstamp, $follow, %errors);
$rv = pchmod("/foo", $perms);
$rv = pchmodR("/foo", $perms, $follow, %errors);
$rv = pchown("/foo", $user, $group);
$rv = pchownR("/foo", $user, $group, $follow, %errors);

$rv = prm("/foo");
$rv = prmR("/foo", 1, %errors);

$fullname = pwhich('ls');
$cleaned  = pcleanPath($filename);
$noLinks  = ptranslateLink("/etc/foo/bar.conf");
$rv       = ptranslatePerms("ug+rwx");

$filename = pwhich('ls');

🫢 DESCRIPTION 🫢

okay so this module hands you a lil bundle of functions that make poking at the filesystem way less of a whole thing, and it sneaks some safety checks in while it's at it. πŸ‘‘

Wanna turn on debug tracing into each function? You gotta set PDEBUG to at least 9. No shortcuts, no vibe-based debugging (well, you can, but you won't see the traces πŸ’…).

Also super important bestie: pcleanPath, ptranslateLink, and ptranslatePerms are only exported if this module is used with the :all target. If you're wondering where they went β€” that's where. 😭

πŸŽ€ IMPORT LISTS πŸŽ€

These are the symbols this module exports by default:

preadDir psubdirs pfiles pmkdir prm prmR ptouch
ptouchR pchmod pchmodR pchown pchownR pwhich

And these specialized import lists also exist:

List        Members
--------------------------------------------------------
all         @defaults ptranslateLink pcleanPath
            ptranslatePerms

So the all list is giving "everything, and I do mean everything" energy β€” it's all the defaults plus the three extra functions. ✨

πŸ‘‘ SUBROUTINES/METHODS πŸ‘‘

πŸ“ pmkdir

$rv = pmkdir("/foo/{a1,b2}");
$rv = pmkdir("/foo", 0750);
$rv = pmkdir("/foo", 0750, %errors);

This one simulates a mkdir -p {path}, and it returns false if it fails for any reason other than the directory already being present (a directory that's already there is fine, basically, it's not a fail ✨). The second argument (permissions) is optional, but if you do pass it, it should be an octal number. Shell-style globs are supported as the path argument.

Now here's the tricky bit: if you need to make a directory that includes characters which would normally get interpreted as shell expansion characters, you can offer a Paranoid::Glob object as the path argument instead. Creating such an object while passing it a literal value will prevent any shell expansion from happening. πŸ›‘οΈ

This method also lets you call pmkdir with a list of directories to create, rather than just relying on shell expansion to construct the list. Obsessed, honestly.

πŸ“ prm

$rv = prm("/foo");
$rv = prm("/foo", %errors);

This function unlinks non-directories and rmdir's directories. Simple as that. πŸ’…

File arguments are processed through Paranoid::Glob and expanded into multiple targets if globs are detected. You can also use a Paranoid::Glob object with a multitude of entities to delete instead of a string.

The optional second argument is a hash in which any error messages is stored (with the file/directory name as the key). And lowkey the kindest detail: attempting to delete something that's not present is not considered a failure. Bestie behavior fr fr.

πŸ“ prmR

$rv = prmR("/foo");
$rv = prmR("/foo", 1);
$rv = prmR("/foo", 1, %errors);

This function works the same as prm but performs a recursive delete, similar to rm -r on the command line. 😳 An optional second argument determines if symbolic links are followed and the targets also recursively deleted.

⚠️ Bestie. It's like rm -r. Read that second argument twice before you set it to true. 🫣

πŸ“ preadDir

$rv = preadDir("/tmp", @entries);
$rv = preadDir("/tmp", @entries, 1);

This function populates the passed array with the contents of the specified directory. If there are any problems reading the directory the return value will be false and a string explaining the error will be stored in Paranoid::ERROR. πŸ“

All entries in the returned list will be prefixed with the directory name. An optional third boolean argument can be given to filter out symlinks from the results.

πŸ“ psubdirs

$rv = psubdirs("/etc", @dirList);

This function calls preadDir in the background and filters the list for directory (or symlinks to) entries. It also returns a true if the command was processed with no problems, and false otherwise.

Like preadDir an optional third boolean argument can be passed that causes symlinks to be filtered out. 🌈

πŸ“ pfiles

$rv = pfiles("/etc", @filesList);

This function calls preadDir in the background and filters the list for file (or symlinks to) entries. It also returns a true if the command was processed with no problems, and false otherwise.

Like preadDir an optional third boolean argument can be passed that causes symlinks to be filtered out. (yes, psubdirs and pfiles are twins, they're both iconic, we don't rank them πŸ’…)

πŸ“ pcleanPath

$cleaned = pcleanPath($filename);

This function takes a filename and cleans out any ., .., and //+ occurrences within the path. It does not remove . or .. as the first path element, however, in order to preserve the root of the relative path. 🧚

NOTE: this function does not do any checking to see if the passed filename/path actually exists or is valid in any way. It merely removes the unnecessary artifacts from the string. It's a glow-up, not a background check. πŸ’€

If you're resolving an existing filename and want symlinks resolved to the real path as well you might be interested in Cwd's realpath function instead.

$noLinks = ptranslateLink("/etc/foo/bar.conf");

This functions tests if passed filename is a symlink, and if so, translates it to the final target. If a second argument is passed and evaluates to true it will check every element in the path and do a full translation to the final target.

The final target is passed through pcleanPath beforehand to remove any unneeded path artifacts. If an error occurs (like circular link references or the target being nonexistent) this function will return undef. You can retrieve the reason for failure from Paranoid::ERROR.

Obviously, testing for symlinks requires testing against the filesystem, so the target must be valid and present. No vibes-only symlink checking, sorry. πŸ™ˆ

Note: because of the possibility that relative links are being used (including levels of ..) all links are translated fully qualified from /.

πŸ“ ptouch

$rv = ptouch("/foo/*");
$rv = ptouch("/foo/*", $tstamp);
$rv = ptouch("/foo/*", $tstamp, %errors);

Simulates the UNIX touch command. Like the UNIX command this will create zero-byte files if they don't exist. ✨ The time stamp is an integer denoting the time in UNIX epoch seconds.

Shell-style globs are supported, as are Paranoid::Glob objects.

The error message from each failed operation will be placed into the passed hash using the file name as the key.

πŸ“ ptouchR

$rv = ptouchR("/foo");
$rv = ptouchR("/foo", $tstamp);
$rv = ptouchR("/foo", $tstamp, $follow);
$rv = ptouchR("/foo", $tstamp, $follow, %errors);

This function works the same as ptouch, but offers one additional argument (the third argument), boolean, which indicates whether or not the command should follow symlinks.

You cannot use this function to create new, non-existent files β€” this only works to update an existing directory hierarchy's mtime. It's a refreshing-the-vibe function, not a creating-something-from-nothing function. 😭

πŸ“ ptranslatePerms

$rv = ptranslatePerms("ug+rwx");

This translates symbolic mode notation into an octal number. It fed invalid permissions it will return undef. It understands the following symbols:

  • u β€” permissions apply to user
  • g β€” permissions apply to group
  • o β€” permissions apply to all others
  • r β€” read privileges
  • w β€” write privileges
  • x β€” execute privileges
  • s β€” setuid/setgid (depending on u/g)
  • t β€” sticky bit

🌟 EXAMPLES 🌟

# Add user executable privileges
$perms = (stat "./foo")[2];
chmod $perms | ptranslatePerms("u+x"), "./foo";

# Remove all world privileges
$perms = (stat "./bar")[2];
chmod $perms ^ ptranslatePerms("o-rwx"), "./bar";
NOTE: If this function is called with a numeric representation of permissions, it will return them as-is. This allows for this function to be called indiscriminately where you might be given permissions in either format, but ultimately want them only in numeric presentation. So you can just always throw your perms at it and it'll never be weird about it. 🫢

πŸ“ pchmod

$rv = pchmod("/foo", $perms);
$rv = pchmod("/foo", $perms, %errors);

This function takes a given permission and applies it to every file given to it. The permission can be an octal number or symbolic notation (see ptranslatePerms for specifics). If symbolic notation is used the permissions will be applied relative to the current permissions on each file. In other words, it acts exactly like the chmod program.

File arguments are processed through Paranoid::Glob and expanded into multiple targets if globs are detected. or you can hand it a glob object directly.

The error message from each failed operation will be placed into the passed hash using the filename as the key.

The return value will be true unless any errors occur during the actual chmod operation including attempting to set permissions on non-existent files.

πŸ“ pchmodR

$rv = pchmodR("/foo", $perms);
$rv = pchmodR("/foo", $perms, $follow);
$rv = pchmodR("/foo", $perms, $follow, %errors);

This function works the same as pchmod, but offers one additional argument (the third argument), boolean, which indicates whether or not the command should follow symlinks. πŸ¦‹

πŸ“ pchown

$rv = pchown("/foo", $user);
$rv = pchown("/foo", $user, $group);
$rv = pchown("/foo", $user, $group, %errors);

This function takes a user and/or a named group or ID and applies it to every file given to it. If either the user or group is undefined it leaves that portion of ownership unchanged. (so you can change just the user, just the group, or both β€” pick your fighter πŸ’…)

File arguments are processed through Paranoid::Glob and expanded into multiple targets if globs are detected, or you can hand it a populated glob object directly.

The error message from each failed operation will be placed into the passed hash using the filename as the key.

The return value will be true unless any errors occur during the actual chown operation including attempting to set permissions on non-existent files.

πŸ“ pchownR

$rv = pchownR("/foo", $user);
$rv = pchownR("/foo", $user, $group);
$rv = pchownR("/foo", $user, $group, $follow);
$rv = pchownR("/foo", $user, $group, $follow, %errors);

This function works the same as pchown, but requires one additional argument (the fourth argument), boolean, which indicates whether or not the command should follow symlinks. Yes bestie, requires β€” this one is not optional, don't leave it at home. πŸ‘€

πŸ“ pwhich

$fullname = pwhich('ls');

This function tests each directory in your path for a binary that's both readable and executable by the effective user. It will return only one match, stopping the search on the first match. If no matches are found it will return undef. 🎯


to be continued, bestie β€” part 2 is coming πŸ’–

🌈 DEPENDENCIES 🌈

okay so before you can even run this thing, it needs its little friends, and here's the whole squad:

  • Cwd
  • Errno
  • Fcntl
  • Paranoid
  • Paranoid::Debug
  • Paranoid::Glob
  • Paranoid::Input
  • Paranoid::IO
  • Paranoid::Process

πŸ’€ BUGS AND LIMITATIONS πŸ’€

bestie. sit down. we need to talk about the messy parts. πŸ™ˆ

translateLink is probably pointless for 99% of the uses out there, and honestly? you're better off using Cwd's realpath function instead. the only thing it can do differently is translating a single link itself, without translating any additional symlinks found in the preceding path. but again β€” you probably won't want that in most circumstances. so like. keep that in mind. 😭

all of the *R recursive functions have the potential to be very expensive in terms of memory usage. and not in a cute way. in an attempt to be fast (and reduce excessive function calls and stack depth) it utilizes Paranoid::Glob's recurse method. in essence, this means that the entire directory tree is loaded into memory at once before any operations are performed. the whole thing. all at once. in your RAM. 🫠

for the most part, functions meant to simulate userland programs try to act just as those programs would in a shell environment. that includes filtering arguments through shell globbing expansion, etc. so if you've got a filename that should be treated as a literal string, you should put it into a Paranoid::Glob object as a literal first, and then hand the glob to the functions. no cap. ✨

fr fr, if your filenames have weird characters in them and you're not wrapping them, the shell globbing will come for you and it will win.

πŸ‘‘ AUTHOR πŸ‘‘

Arthur Corliss (corliss@digitalmages.com)


this software is free software. similar to Perl, you can redistribute it and/or modify it under the terms of either:

  1. the GNU General Public License <https://www.gnu.org/licenses/gpl-1.0.html> as published by the Free Software Foundation <http://www.fsf.org/>; either version 1 <https://www.gnu.org/licenses/gpl-1.0.html>, or any later version <https://www.gnu.org/licenses/license-list.html#GNUGPL>, or
  2. the Artistic License 2.0 <https://opensource.org/licenses/Artistic-2.0>,

subject to the following additional term: No trademark rights to "Paranoid" have been or are conveyed under any of the above licenses. however, "Paranoid" may be used fairly to describe this unmodified software, in good faith, but not as a trademark. iykyk. πŸ’…

(c) 2005 - 2020, Arthur Corliss (corliss@digitalmages.com) (tm) 2008 - 2020, Paranoid Inc. (www.paranoid.com)


aaaand that's the whole page, bestie! this was the perl v5.38.2 build, dated 2024-03-07, from the Parano...system(3pm) man page in the User Contributed Perl Documentation. you ate that. go translate some links (or don't, honestly, use realpath). stay paranoid, stay iconic. πŸ’–βœ¨πŸ¦‹

πŸ“¦ where this page comes from