Nautilus: Visited directory via symlink, now I can't go one level up in the target directory hierarchy
Why this is a challenging question
The question has a few challenges:
nautilus
does not communicate directly from the command line, to get the currently active directory for example, nor can you "send" the currently opened folder (-window) to another directory from the command line.- In the current path, as requested via
"NAUTILUS_SCRIPT_CURRENT_URI"
, Nautilus does not return the real path to the current folder, but "sees" the link as if it was an actual folder.
The solution therefore is as dirty as it gets; we need to find workarounds. Below four options to solve the issue.
1. right- click to go one level up
To get the real path to the current directory, we have to retrieve information from the link. We can do that either by using ls -l
on the link, which will output e.g.:
lrwxrwxrwx 1 jacob jacob 35 jan 15 08:23 /home/jacob/Bureaublad/Flyer_st/Verwijzing naar Site_current -> /home/jacob/Bureaublad/Site_current
where the section after ->
is the real path inside the symlink, or, using python
:
real = os.path.realpath("/path")
Using this in a nautilus
script, we can indirectly get the real path to the current file or folder.
Then if we have the path, how do we make nautilus move one level up?
Again, we cannot solve this and keep our hands clean. To move one level up, we first edit the found path a bit, from:
/path/to/a/folder
into
/path/to/a
then, using xdotool
to simulate Ctrl+L (the GUI shortcut to insert a path into a nautilus window, since there is no cli option to move to another directory using the current window), and subsequently make xclip
paste the edited path + Enter, we have a working solution!
In practice
We are in a folder, opened from a link ("Link to Telegram") on my Desktop. The real folder is a sub folder of my
Downloads
folder:Then if we right- click on any of the files inside the folder to run the script:
Automatically, the path to the superior directory is inserted:
And, also automatically, Return is pressed, and we move one directory level up:
The script
#!/usr/bin/env python3
import subprocess
import os
import time
def run(cmd):
subprocess.call(["/bin/bash", "-c", cmd])
current = os.getenv("NAUTILUS_SCRIPT_CURRENT_URI").replace("file://", "").replace("%20", " ")
real = os.path.realpath(current)
up = os.path.abspath(os.path.join(real, os.pardir))
run("xdotool key Control_L+l")
run("printf '"+up+"' | xclip -selection clipboard")
run("xdotool key Control_L+v")
# confirm by virtually press Return
time.sleep(0.1)
run("xdotool key Return")
How to set up
The script needs both
xdotool
andxclip
:sudo apt-get install xdotool xclip
create, if it doesn't exist already, the directory
~/.local/share/nautilus/scripts
Copy the script above into an empty file, save it as
level_up
(no extension) in~/.local/share/nautilus/scripts
, and make it executable- You might have to log out and back in.
Now you should be able to run the script by right- click on a file (any) > scripts > level_up:
[EDIT] I changed the script above to paste the path into the nautilus
window, instead of making xdotool
type it. It needs
xclip
to be installed, but it is a major improvement, especially on very long paths.
2. Alternatively, open a new nautilus window in the superior directory
You could avoid using xdotool
, by making the script open a new nautilus window, in the parent's directory. The script would then be even shorter:
#!/usr/bin/env python3
import subprocess
import os
def run(cmd):
subprocess.call(cmd)
current = os.getenv("NAUTILUS_SCRIPT_CURRENT_URI").replace("file://", "").replace("%20", " ")
real = os.path.realpath(current)
up = real[:real.rfind("/")]
subprocess.Popen(["nautilus", up])
In this case, you wouldn't need to install xdotool
. We could even extend the script by closing the original window and placing the new window exactly in the same position (& size).
The downside is that the history of the original window is lost this way.
3. An additional solution: alternative way to (automatically) create links
Not relevant to existing links, but when used from the GUI, a nautilus script to automatically create executable .desktop
files on right-click might be helpful:
right-click on the directory to create a shortcut (behaving as a link)
Unlike symlinks, these links will take you to the actual folder, without behaving as a folder itself:
The script
#!/usr/bin/env python3
import subprocess
import os
current = os.getenv(
"NAUTILUS_SCRIPT_SELECTED_FILE_PATHS"
).replace("file://", "").replace("%20", " ").strip()
if os.path.isdir(current):
parent = os.path.abspath(os.path.join(current, os.pardir))
fldr_path = '"'+current+'"'
folder = current[current.rfind("/")+1:]
linkfilename = parent+"/"+folder+"link.desktop"
linkdata = [
"[Desktop Entry]",
"Type=Application",
"Name=Link to -> "+folder,
"Icon=folder",
"Exec=nautilus "+fldr_path,
]
with open(linkfilename, "wt") as wt:
for l in linkdata:
wt.write(l+"\n")
command = "chmod +x "+"'"+linkfilename+"'"
subprocess.Popen(["/bin/bash", "-c", command])
How to use
- Copy the script into an empty file, save it as
make_link
(no extension) in~/.local/share/nautilus/scripts
, and make it executable - Use the script by choosing it as a right- click option as in the first image. An executable
.desktop
file will be created in the same directory, move it elsewhere if you need to; the linked path is absolute.
Give the link an distinguishing icon
You could give the alternative link a distinguishing icon. If you search inside the directory /usr/share/icons
for "folder", numerous valid options pop up.
If in the script the line "Icon=folder",
is replaced by Icon=stock_folder-copy,
(use the icon name without extension), the result on my system is:
Of course you can use your own custom icon as well, but if you use full paths (don't use ~
), you should include the icon's extension.
4. Move to the superior directory with a short cut key
Probably the most convenient option; with the nautilus window in front, press a short cut key to move one directory up.
The script
#!/usr/bin/env python3
import subprocess
import time
import os
def get(cmd):
return subprocess.check_output(cmd).decode("utf-8").strip()
def run(cmd):
subprocess.call(["/bin/bash", "-c", cmd])
# get information on the active window
front = get(["xprop", "-id", get(["xdotool", "getactivewindow"])])
# (only) if it is a "normal" nautilus window, take action
if 'WM_CLASS(STRING) = "nautilus", "Nautilus"' in front:
# "virtually" press Ctrl + l
run("xdotool key Control_L+l"); time.sleep(0.05)
# copy the current path, calculate the "real" parent directory
real = os.path.realpath(get(["xclip", "-o"]))
up = os.path.abspath(os.path.join(real, os.pardir))
time.sleep(0.1)
# enter the superior directory
run("printf '"+up+"' | xclip -selection clipboard")
run("xdotool key Control_L+v")
# confirm by virtually press Return
time.sleep(0.1)
run("xdotool key Return")
To use
For this solution, both
xclip
andxdotool
Need to be on your system.sudo apt-get install xdodool xclip
Copy the script into an empty file, save it as
level_up.py
(anywhere).Add it to a shortcut key: choose: System Settings > "Keyboard" > "Shortcuts" > "Custom Shortcuts". Click the "+" and add the command:
python3 /path/to/level_up.py
NB The shortcut options are a bit limited in this case, since the script itself will simulate Ctrl+L, and Ctrl+Alt+L will make you log out... Ctrl+\ worked fine on my system.
Explanation
This script also simulates Ctrl+L, but instead of using nautilus' "NAUTILUS_SCRIPT_CURRENT_URI"
, it uses xclip
to copy the automatically selected path in the nautilus window. Like option 1, the script then calculates the real path and derives the superior directory.
This option might be useful if you prefer the keyboard to using right- click.
Or for Ubuntu 14.04, nautilus 3.10-1, with the xdotool
package added, just use the following in your .local/share/nautilus/scripts/updirtree
file:
# In nautilus, the pwd is the actual, not the link path
xdotool key ctrl-l
xdotool type "$(dirname $(pwd))" "
"
The final quote should just contain a newline or return (0x0a
).
The pwd
from within nautilus produces a different result than when run from a bash/terminal -- it returns the actual path, not the path using the link.
I agree it makes no sense, it's undocumented, and I can't even figure out what sort of execution environment is running the code (I can't find any shell which produces that result), but it works. It's a hack, which is why I included the version of nautilus. Who knows how long it will work? Might break at the next nautilus upgrade (or the unknown interpreter), but for me, it works on links to mounted locations, links to places in the directory tree, or just plain locations in the directory tree.