thingsiplay / fpath

Reformat and stylize file path like text output.
MIT License
2 stars 0 forks source link

targets of symlinks processed not symlinks themselves #3

Open mcmikemn opened 5 months ago

mcmikemn commented 5 months ago

I'm trying to display symbolic links in red. The following command displays them in blue if they point to a directory, or white (default color) if they point to a file. \ls -a1 | fpath -s blue -F'{red}{.mode}{/color}\t{owner} {green}/{/color} {group}\t{.mtime:%Y-%m-%d}\t{.size} \t{.isreg:{name}}{red}{.islink:{name} -> {path}}{/color}{blue}{.isdir:{name}}{/color}'

I've also tried with -r: \ls -a1 | fpath -r -s blue -F'{red}{.mode}{/color}\t{owner} {green}/{/color} {group}\t{.mtime:%Y-%m-%d}\t{.size} \t{.isreg:{name}}{red}{.islink:{name} -> {path}}{/color}{blue}{.isdir:{name}}{/color}'

I've also tried with find -P instead of ls: find -P ./.* -maxdepth 1 | fpath -r -s blue -F'{red}{.mode}{/color}\t{owner} {green}/{/color} {group}\t{.mtime:%Y-%m-%d}\t{.size} \t{.isreg:{name}}{red}{.islink:{name} -> {path}}{/color}{blue}{.isdir:{name}}{/color}'

..and with find -P but without -r: find -P ./.* -maxdepth 1 | fpath -a -s blue -F'{red}{.mode}{/color}\t{owner} {green}/{/color} {group}\t{.mtime:%Y-%m-%d}\t{.size} \t{.isreg:{name}}{red}{.islink:{name} -> {path}}{/color}{blue}{.isdir:{name}}{/color}'

Examples:

$ \ls -al
lrwxrwxrwx   1 mike mike       10 Jun  3 06:47  testlink -> ./smb.conf
lrwxrwxrwx   1 mike mike        9 Jun  3 06:48  testlink2 -> .simdock/

$ \ls -a1 | fpath -s blue -F'{red}{.mode}{/color}\t{owner} {green}/{/color} {group}\t{.mtime:%Y-%m-%d}\t{.size}   \t{.isreg:{name}}{red}{.islink:{name} -> {path}}{/color}{blue}{.isdir:{name}}{/color}'
-rwxr-xr-x      mike / mike     2017-09-21      9.3 KB          testlink
drwxrwxr-x      mike / mike     2020-08-18      4.0 KB          testlink2

(in the fpath example, "testlink" is white and "testlink2" is blue)

thingsiplay commented 5 months ago

Global option -r would resolved any symlinks, so that is not the option you want to use. You can display stats of symlinks themselves, adding an l in front. This only works with stat commands, meaning those starting with a dot like {.isdir . Look at the help with fpath -H fmt to see a list of them. All of the {. commands exist as same as {.l , which do not follow links and handle symlinks themselves.

So simply replace {.isdir: with {.lisdir: and {.islink: with {.lislink: . Do the same with {.isreg} . Here is the modified variant of the last command you gave:

find -P ./.* -maxdepth 1 | fpath -a -s blue -F'{red}{.mode}{/color}\t{owner} {green}/{/color} {group}\t{.mtime:%Y-%m-%d}\t{.size}   \t{.lisreg:{name}}{red}{.lislink:{name} -> {path}}{/color}{blue}{.lisdir:{name}}{/color}'

Does this work as expected?

Edit: Don't forget {.mode} to replace with {.lmode} too, if that is what you want.

mcmikemn commented 5 months ago

Thanks for your help. This is a really comprehensive utility!

But I'm still doing something wrong. \ls -a1 | fpath -r -s blue -F'{red}{.mode}{/color}\t{owner} {green}/{/color} {group}\t{.mtime:%Y-%m-%d}\t{.size} \t{.lisreg:{name}}{red}{.lislink:{name} -> {path}}{/color}{blue}{.lisdir:{name}}{/color} results in showing the name of the target of a symlink, and not in a color or with the path as I specify:

-rwxr-xr-x      mike / mike     2017-09-21      9.3 KB          smb.conf
drwxrwxr-x      mike / mike     2020-08-18      4.0 KB          .simdock
-rw-rw-r--      mike / mike     2021-10-16      16.5 KB         lutris.log

(smb.conf and lutris.log are displayed in white, and .simdock is displayed in blue. And note that they display the name of the target of the symlink and are listed alphabetically where the name of the symlink would be.)

For reference, here is the result of \ls -al:

-rw-rw-r--   1 mike mike     16860 Oct 16  2021  lutris.log
drwxrwxr-x   2 mike mike      4096 Aug 18  2020  .simdock
-rwxr-xr-x   1 mike mike      9542 Sep 21  2017  smb.conf
lrwxrwxrwx   1 mike mike        10 Jun  3 06:47  testlink -> ./smb.conf
lrwxrwxrwx   1 mike mike         9 Jun  3 06:48  testlink2 -> .simdock/
lrwxrwxrwx   1 mike mike        10 Jun  4 07:10  testlink3 -> lutris.log
thingsiplay commented 5 months ago

That's because you have still the global option fpath -r in use, which will resolve all symlinks before it hits the -F option. Remove the -r option.

mcmikemn commented 5 months ago

Doh! Of course you're right. Thanks again.

mcmikemn commented 5 months ago

Unrelated: my command (after I removed -r) has {.lislink:{name} -> {path}}, and the result is: symlink -> symlink instead of symlink -> /full/path/to/target

I assume this is because both {name} and {path} are directed to the symlink. But what I want is the name of the symlink but the path of the symlink's target. I didn't find anything about an alternative {path} command. Is there an option for this?

thingsiplay commented 5 months ago

Hmm, I didn't think too deep into this before. Have in mind the script operates on a fairly simple manner. So limitations are expected. Just a disclaimer. :-)

So your goal is to display the name of the symlink with the path it resolves to; just like what ls does. The {.lislink:{name} -> {path}} does exactly that, it displays the name of the link file and the full path to of this same link file. There is no evaluation of the symlink to its target at the moment. So right now we can't do that.

But I have looked into the Python documentation and found something that could be useful for this. Maybe I change all to evaluate the symbolic link by default like the {. based stat commands, and provide an l . But I have to test this before and see if it works.

mcmikemn commented 5 months ago

That would be cool.

thingsiplay commented 5 months ago

v0.12 Hi I finally found some time again and got things sorted out. Instead giving l variants to each path and filename based commands, there are now r based commands to resolve symlink. There was a debate which way I should have done this, but there are good reasons. Take your previous command here and try it out. Here it is:

\ls -A1 | fpath -s blue -F'{red}{.mode}{/color}\t{owner} {green}/{/color} {group}\t{.mtime:%Y-%m-%d}\t{.size}   \t{.lisreg:{name}}{red}{.lislink:{name} -> {rpath}}{/color}{blue}{.lisdir:{name}}{/color}'

Notice I use \ls -A1 instead -a1, because of the "." and ".." directories. Also this is still not perfect, because a symbolic link that points to a non existent file is displayed as an (almost) empty line.

Changelog: https://github.com/thingsiplay/fpath/blob/main/CHANGES.md

mcmikemn commented 5 months ago

Thanks for adding this feature!

However, I get this error:

$ ls
Traceback (most recent call last):
File "/usr/local/bin/fpath", line 1388, in <module>
exit(main())
File "/usr/local/bin/fpath", line 1379, in main
for path in app:
File "/usr/local/bin/fpath", line 430, in __iter__
for index, path in enumerate(self.apply_format(), start=1):
File "/usr/local/bin/fpath", line 690, in apply_format
if "{.l" in fmt and entry.exists:
File "/usr/local/bin/fpath", line 85, in exists
self.exists_cache = self.work_path.exists(follow_symlinks=False)
TypeError: Path.exists() got an unexpected keyword argument 'follow_symlinks'
thingsiplay commented 5 months ago

Edit: Spelled "Python" a few times less. It was repetitive.

Oh no, this is not good. Because I see this "follow_symlinks" feature in Python got added to its standard library in in version 3.12. You have a version that is older than this, which makes sense as the most distributions don't have the newest one. This is one of the shortcomings of Python and definitely unfortunate then.

I could provide a compiled binary in the releases page (its truly compiled to C code with Nuitka). This makes it independent from any Python version or library you have installed, but loses its open nature of the script. Otherwise you have to use the older version, because there is not much else I can do about (I think).

mcmikemn commented 5 months ago

Ah. Well I definitely recommend remaining open and not offering a compiled executable. :)

I can try to install a newer python, but from past experience, that's not necessarily easy. Thanks for adding the feature. I'll catch up someday.

thingsiplay commented 5 months ago

I would highly discourage from updating your system Python, as this can cause lot of issues and incompatibilities. There are AppImages of Python like https://github.com/niess/python-appimage/releases/tag/python3.12 (which are binaries obviously).

I'm sorry for this complicated mess. I will look into the code again to see if can come up with any solution to this.

mcmikemn commented 5 months ago

I used pyenv and it was super easy: pyenv install 3.12.0 and then pyenv global 3.12.0.

fpath now works. I have yet to find out if anything else I have that requires python is going tohave problems. :)

And the new {rpath} works great. :)

thingsiplay commented 5 months ago

That's nice. If you have multiple Python versions installed, then I would recommend to leave the original one at default. Then just adapt the line from the script to call the correct Python version, something like #!/usr/bin/env Python3.12 or like that. Then you can opt in to the specific version of Python for any script you like, while the system version is unaffected.

Just a suggestion. Good to see it works. However most people don't have the newest version available, so I need to find a solution (if there is one).