6

Suppose I have a file bar.txt inside directory foo and create a symlink baz.txt to foo/bar.txt. Like:

./foo
./foo/bar.txt
./baz.txt -> foo/bar.txt

If I open baz.txt my editor will think baz.txt is opened in directory .. Is there a way to create a link such that rather bar.txt is (literally) opened?

Context (or why I'm trying to do this): I have a directory with a large collection of files which I index and comment inside an .odt file which remains in the same directory. In this .odt file I create hyperlinks to the indexed files in the directory, so that I can easily access the individual files with (much) more context than just the filename. I set LibreOffice to save the hyperlinks as relative paths, so that these links will work in all of my computers, which not always have the same directory tree to my user files.

I'd like to create a symlink (or equivalent) to this .odt file, but (in the terms of the above example) if the link opens baz.txt then relative paths (from the point of view of LibreOffice) will be wrong. The formerly created hyperlinks will not work, and if I happen to create an hyperlink in baz.txt (figuratively, of course) it won't work in the original bar.txt.

  • 1
    how do you open these files? Do you run libreoffice file.txt, do you click on them from a file manager or do you open Libre Office and then File=>Open to select the files? – terdon Jul 30 '17 at 13:37
  • Usually I click on them from the File Manager. Would there be any difference if I opened them from command line or from within LibreOffice's open menu? – gusbrs Jul 30 '17 at 13:39
  • Well, I have an idea for a workaround but that would really only work if you opened the files from the command line. Would that be acceptable for you? – terdon Jul 30 '17 at 13:40
  • 1
    Well, PSlocik already posted (a slightly abbreviated) version of what I had in mind. If you open the link's target (libreoffice "$(readlink -f baz.txt)"), it will work the way you expect it to. – terdon Jul 30 '17 at 13:51
  • 3
    This is probably something like culture shock. Operating in document-centric environments like Apple MacOS or MS Windows, where file associations and double-clicks are the name of the game, tend to make people forget — or never even know — what is really happening when they “open” a file. – can-ned_food Jul 30 '17 at 14:32
12

No. But you can create a libreoffice wrapper that'll take each argument that is a symlink and turn it into $(readlink -f $the_symlink). You can then set your file manager to open libreoffice files through that wrapper.

lowrapper:

#!/bin/bash -e
args=()
for a; do
    case $a in 
        -*) args+=("$a");;  #skip flags (your file names don't start with -, right?)
        *)  if ! [ -L "$a" ]; then #not a link
                args+=("$a")
            else #link => target
                args+=( "$( readlink -f "$a")" )
            fi
            ;;
    esac
done
libreoffice "${args[@]}"

Now if you chmod +x lowrapper, put it in some directory of your PATH, and then change the handler program of your libreoffice files from libreoffice to lowrapper, then libreoffice will be opening the link targets instead of the links.

  • @PSkocik, I'm not sure I understand. You mean to change the launcher for libreoffice (in Caja's "Open With") so that it will be something like libreoffice $(readlink -f $the_symlink)? – gusbrs Jul 30 '17 at 13:51
  • @gusbrs I added some code and text that should explain it. – PSkocik Jul 30 '17 at 13:57
  • PSkocik and @terdon, both your solutions work for me and are very similar in content. I'm familiar with some others SX communities, but not exactly this one. What would be the local custom in this respect to which answer should be accepted? (I gladly and thankfully upvoted both of them). – gusbrs Jul 30 '17 at 14:34
  • 1
    @gusbrs As a general rule, accept whichever solution you ended up using, but in this case, just accept PSkocik's, I have more rep than I know what to do with. Also, this script is a bit better than mine since it discards option flags. Mine will break if you give it a file name starting with a -, this one will silently ignore it which seems better. – terdon Jul 30 '17 at 14:38
  • Ok, @terdon, I will do it this way then. For the register, both answers are equally useful to me. Thank you both very much! – gusbrs Jul 30 '17 at 14:48
3

You can't, but what you can do instead is create symlinks to your "foo" directory like so:

./foo
./foo/root -> .
./root -> foo/.

Then append "root" to the start of all your hyperlinks. Now if you open your document from the ".", "root" will be resolved into "foo/.", and your "root/baz.txt" will resolve into "foo/./baz.txt". If opened from the "foo" itself, the same "root/baz.txt" will be resolved into "./baz.txt" because "root" points to ".".

  • That seems a neat idea as well. I'll test it in my context, to see how LibreOffice deals with it (but I cannot append "root" to all the hyperlinks, for there is no way to search-replace in them in LibreOffice that I'm aware of). – gusbrs Jul 30 '17 at 14:11
  • 1
    Alexander, I could not make it work. I cannot manually edit the hyperlinks, it must work with the hyperlinks generated by LibreOffice, and if open it in . the program will not by itself create links relative to ./root, and the same for ./foo. Still, thank you for your time and thoughts. – gusbrs Jul 30 '17 at 14:27
  • Oh, I wasn't aware LibreOffice won't let you edit the links. – Alexander Batischev Jul 30 '17 at 15:55
  • I didn't mean it is not possible, but that it is not practical. LibreOffice will allow you to edit the link reference, through "Edit Hyperlink", but this is essentially a mouse and click(ss) operation, and is not doable for the hundreds of links I have. So I must stick to LibreOffice's logic for this. – gusbrs Jul 30 '17 at 16:03
  • 1
    @PeterCordes, thank you for your comment. Very true. AFAIK, is is possible to search and replace, provided I unpack the odt and do so on the base xml. Though I think this is far from ideal for, even if less error prone, there's a risk of doing something wrong and corrupting the file altogether. Luckily, I don't have to, given the above solutions work fine. :) – gusbrs Jul 31 '17 at 11:20
3

I don't think there's any way to do exactly what you're asking for. Symlinks are file system constructs. However, there might be a decent workaround if you create a little script that opens the link target instead of the link:

#!/bin/bash

i=0
declare -a targets
for file in "$@"; do
        targets[$i]="$(readlink -f "$file")"
        ((i++))
done
libreoffice "${targets[@]}"

Save that in your PATH, for example as ~/bin/openLink.sh and make it executable:

chmod a+x ~/bin/openLink.sh`

Now, open your file manager (you mentioned caja), right click on an .odt file and choose "Open With" => "Other Application":

open with dialog

Click on the "Use a custom command" and put your script there:

custom command dialog

Finally, close everything. Now, each time you click on an .odt file, it will be opened using that wrapper so it should open all links in their target directory. Note that readlink -f on a regular file just returns the name of the file, so this will also work for non-links.

  • 1
    targets+=( "$(readlink)" ) is an easier way to append to a bash array, and doesn't need a counter. AFAIK, it's just as efficient. – Peter Cordes Jul 31 '17 at 2:29
0

I've ended up following a somewhat different approach, of creating a new file type -- let's call it "sim(ulated)link" -- which contains the (relative) path of file to be open, and then created a script do deal with it.

Consider the following scenario:

./dir1/dir11/foo.odt
./dir2/foo.odl

"odl" for "OpenDocument Link". The contents of "foo.odl" being:

../dir1/dir11/foo.odt

You can then create the following script to open *.odl (adapted from terdon's answer):

#!/bin/bash

declare -a targets
for file in "$@"; do
    targets+=( "$(readlink -f "$( dirname "$file" )"/"$( <"$file" )")" )
done
libreoffice "${targets[@]}"

(In case you don't want to use readlink because it will follow the link in case the path thus specified is a symlink, you could simply go with:)

#!/bin/bash

for file in "$@"; do
    cd "$( dirname "$file" )"
    libreoffice "$( <"$file" )"
done

You can then associate this script with .odl files in your file manager (as explained in terdon's answer). Eventually, you will have to create a mime type for .odl files and associate it with the script (lacking this step in my case worked, but associated all plain text files with the script, which was clearly undesirable).

Why have I preferred this alternative to the ones provided by PSkocik and terdon, even though they work perfectly fine? For three reasons:

  • Not all of my .odt files (and links to them) have this structure that I have to open them "in the target file's directory". In this way, I affect the opening procedure only of the files of interest.

  • The system regular symlinks will work regardless of the settings in my current computer. This means that, if I happen to open one of these files in a computer in which I didn't change the settings to open .odt files with my script, it will open anyway and I may inadvertently mess up with the paths of my hyperlinks within it. This way, if the computer is not setup to open .odl files, they simply won't work, and I will have to go for the target files directly.

  • Lastly, in case I move or rename some of the directories involved, I can easily search and replace the .odl files to correct the paths. (This is likely also doable with the symlinks, but seems somewhat harder).

Caveats: I think this will only work for a well formed, non redundant, relative path given in the .odl file. There is probably a way to do things more robustly in the sense that it would work also with an absolute path or with a relative path with some redundancy in it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.