`ls -l` for all parent directories - linux

I want to get a list of all directory permissions from current folder to /. For example, for the directory: /var/lib/program/subfolder, I want an output such as:
$ pwd
/var/lib/program/subfolder
$ magic_ls_-l_command somefile
drwxr-xr-x 10 root root 4096 May 15 20:20 var
drwxr-xr-x 10 root root 4096 May 15 20:20 lib
drwxrwxr-x 10 root user 4096 May 16 20:21 program
drwxrwxr-x 10 root user 4096 May 16 20:21 subfolder
-rwxrwxr-- 1 root user 4096 May 16 20:22 somefile
I don't care about the order (from /var to /subfolder or the other way around), the number of hard links or even the date. I just wrote them down to emulate the ls -l output. Also, I don't care how each filename in printed (/var and /lib, var and lib, or /var and /var/lib). I'm just interested in the ownership of each file/directory in the path from the choosen file or pwd to /.
In case I should install some program, I'm under Ubuntu 20.04.

This question has already been answered in superuser.com (I don't know if I can mark a question from one site as duplicate from another). The solution is as simple as writing (assuming I am in the same directory as the target filename):
$ namei -l $(pwd)/somefile ## or `namei -l $(realpath -s somefile)`
Because of -l, it lists basic permissions in long format for each parent directory.
I have to use pwd/realpath because namei doesn't resolve relative paths. If I'm not in the target directory, just write the full path.

I made this small script that does this. I use cd "$1"; pwd to get the current directory so that paths are not canonicalized (say, if you try magic-ls . and your current directory is /var/lib/postgres, but that is a symlink to /mnt/postgres, you will get /var, /var/lib and /var/lib/postgres, while using realpath you would get /mnt and /mnt/postgres)
magic-ls() {
local current=$(cd "$1"; pwd)
while [[ $current != '/' ]]; do
ls -ld "$current"
current=$(dirname "$current")
done
}
Here's an example output:
[leodag#desk ~]$ magic-ls
drwx------ 1 leodag leodag 2722 jun 21 13:49 /home/leodag
drwxr-xr-x 1 root root 18 mai 2 2019 /home
By the way it will also work with no argument since cd "" does not change your directory.
Edit: removed realpath from the while check, since that could lead to unexpected results if there was a link to / in the path, and was unneeded.

I wrote a bash script for you. It'll have some bugs, if you have space in names. If it bothers you, I'm happy for changes recommendations in the comments.
#!/bin/bash
if [ ! -z "$1" ] && [ -e "$1" ]
then
path=`realpath -s "$1"` # read argument as absolute path
else
path="$PWD" # No valid argument, so we take pwd
fi
paths=""
while [ "$path" != / ];do
paths+=" $path"
path=`dirname "$path"`
done
paths+=" $path" # Adding / to pathlist too
ls -ld $paths
With realpath -s you can catch the absolute path, but you wont follow the link. If no argument is given, we will use pwd as the file/directory to list.
We append each path to a list. This gives us the advantage of a better layout in the end, so that we get a nice table because we run ls only once.
Output:
bobafit:~$ magic_ls_-l_command /usr/bin/python3
drwxr-xr-x 21 root root 4096 Jun 20 10:07 /
drwxr-xr-x 14 root root 4096 Sep 5 2019 /usr
drwxr-xr-x 2 root root 110592 Jun 20 10:07 /usr/bin
lrwxrwxrwx 1 root root 9 Apr 7 12:43 /usr/bin/python3 -> python3.8

Just using parameter expansion:
#!/usr/bin/env bash
path="$1"
while test -n "$path"; do
ls -lLd "$path"
path="${path%/*}"
done
calling method :
bash test.sh /var/lib/program/subfolder/somefile
giving
-rw-r--r-- 1 root root 0 Jun 21 18:49 /var/lib/program/subfolder/somefile
drwxr-xr-x 1 root root 4096 Jun 21 18:49 /var/lib/program/subfolder
drwxr-xr-x 1 root root 4096 Jun 21 18:49 /var/lib/program
drwxr-xr-x 1 root root 4096 Jun 21 18:49 /var/lib
drwxr-xr-x 1 root root 4096 Jun 13 19:24 /var

#! /bin/bash
cur=""
IFS="/"
path=`pwd`
for dir in ${path:1}
do
cur=$cur/$dir
ls -lhd "$cur"
done
cur=$cur/$1
ls -lhd "$cur"
Terminal Session:
$ pwd
/tmp/dir_underscore/dir space/dir special #!)
$ ls
bash.sh test.txt
$ ./bash.sh test.txt
drwxrwxrwt 28 root root 36K Jun 21 22:45 /tmp
drwxr-xr-x 3 root root 4.0K Jun 21 22:27 /tmp/dir_underscore
drwxr-xr-x 3 root root 4.0K Jun 21 22:28 '/tmp/dir_underscore/dir space'
drwxr-xr-x 2 root root 4.0K Jun 21 22:54 '/tmp/dir_underscore/dir space/dir special #!)'
-rw-r--r-- 1 root root 0 Jun 21 22:29 '/tmp/dir_underscore/dir space/dir special #!)/test.txt'

This should possibly work:
pwd ; ls -lh ; while true ; do cd .. ; pwd ; ls -lh ; [[ "$PWD" == "/" ]] && break ; done
EDIT: I misunderstood the question at first. Try this:
(pwd ; ls -ldh ; while true ; do cd .. ; pwd ; ls -ldh ; [[ "$PWD" == "/" ]] &&
break ; done ; cd "$START")
EDIT2: fillipe's answer is probably the best, but here's my third and last attempt, which works on both files and directories:
magic_ls() {
fname="$1"
while true ; do
ls -lhd "$fname"
[[ "$fname" == "/" ]] && break ;
fname=$(dirname $(readlink -f "$fname"))
done
}

Just my 2 cents. My mac doesn't have the namei command (perhaps homebrew has a copy), but wanted to whip up a quick version that aligned the output in top-down order
#!/usr/bin/env bash
path="${1%/}"
DIRS=()
while test -n "$path"; do
DIRS=( "$path" "${DIRS[#]}" )
path="${path%/*}"
done
ls -ld "${DIRS[#]}"
Example output:
$ lspath $TMPDIR
lrwxr-xr-x# 1 root wheel 11 Oct 5 2018 /var -> private/var
drwxr-xr-x 7 root wheel 224 Jul 16 2020 /var/folders
drwxr-xr-x# 3 root wheel 96 Apr 5 2018 /var/folders/0c
drwxr-xr-x# 5 me staff 160 Apr 5 2018 /var/folders/0c/2_s_qxd11m3d1smzqdrs3qg40000gp
drwx------# 255 me staff 8160 Oct 7 09:18 /var/folders/0c/2_s_qxd11m3d1smzqdrs3qg40000gp/T

Related

Path ambiguity through symbolic links

I have noticed a strange behavior in UNIX systems:
I'm standing in /noob/
I have a symbolic link to a folder (A# -> /B/C/D/A)
I enter the folder via my symlink (cd A)
pwd says /noob/A/
In /B/C/D/A/ i have a file abc which I can see now.
I want to copy it to /noob/
I type cp abc ..
I type cd ..
I end up in /noob/ which is empty - but the file ended up in /B/C/D/ ???
How come this ambiguity as to where cp and cd points when given .. as argument? I find it confusing. Can anyone explain it in terms I'll understand? (=simple)
All the best, and please forgive a UNIX-noob a stupid question. Lasse
First let's have a look at how cd command does behave by looking at the help menu. What we are looking for is option -L (the default behavior) and option -P
$ help cd cd: cd [-L|[-P [-e]] [-#]] [dir]
Change the shell working directory.
...
...
Options:
-L force symbolic links to be followed: resolve symbolic links in
DIR after processing instances of `..'
-P use the physical directory structure without following symbolic
links: resolve symbolic links in DIR before processing instances
of `..'
...
...
Important section
The default is to follow symbolic links, as if `-L' were specified.
`..' is processed by removing the immediately previous pathname component
back to a slash or the beginning of DIR.
Exit Status:
...
As you can see the default behavior of cd is not what you think it is since he will manipulate the $PWD variable accessed by pwd command in his own way, at each step you can run pwd command or do an echo $PWD to see how it reacts with the different cd commands hereunder.
Let's play with cd command:
We start from the following folder, with a sym link:
[/home/arobert/test/noob] >
ls -ltra
total 8
drwxrwxr-x 5 arobert arobert 4096 5月 11 09:48 ..
lrwxrwxrwx 1 arobert arobert 26 5月 11 09:48 A -> /home/arobert/link/B/C/D/A
drwxrwxr-x 2 arobert arobert 4096 5月 11 10:03 .
USAGE EXAMPLES:
[/home/arobert/test/noob] >
cd A
[/home/arobert/test/noob/A] >
cd ..
[/home/arobert/test/noob] >
cd -L A
[/home/arobert/test/noob/A] >
cd ..
[/home/arobert/test/noob] >
cd -P A
[/home/arobert/link/B/C/D/A] >
cd -P ..
[/home/arobert/link/B/C/D] >
cd /home/arobert/test/noob/
[/home/arobert/test/noob] >
cd A
[/home/arobert/test/noob/A] >
cd -P ..
[/home/arobert/link/B/C/D] >
Now let's play with readlink and cp command:
Let's say we have entered the symlink that points to A -> /home/arobert/link/B/C/D/A in which we have a file a
[/home/arobert/test/noob/A] >
ls -ltra
total 8
drwxrwxr-x 3 arobert arobert 4096 5月 11 09:55 ..
-rw-rw-r-- 1 arobert arobert 0 5月 11 10:10 a
drwxrwxr-x 2 arobert arobert 4096 5月 11 10:10 .
from this folder let's look at where does point . and .. by using readlink -f command:
[/home/arobert/test/noob/A] >
readlink -f .
/home/arobert/link/B/C/D/A
[/home/arobert/test/noob/A] >
readlink -f ..
/home/arobert/link/B/C/D
By consequence, when you run from the location /home/arobert/test/noob/A equivalent to /home/arobert/link/B/C/D/A the command cp a .. the file will be moved to /home/arobert/link/B/C/D as .. points to it.
What you can do now:
Use absolute path with your cp command to avoid bad surprise.
Call the command from /home/arobert/test/noob/ directory using
For example:
[/home/arobert/test/noob] >
cp A/a .
as readlink -f . points to the correct folder
[/home/arobert/test/noob] >
readlink -f .
/home/arobert/test/noob
Result:
[/home/arobert/test/noob] >
ls -ltra
total 8
drwxrwxr-x 5 arobert arobert 4096 5月 11 09:48 ..
lrwxrwxrwx 1 arobert arobert 26 5月 11 09:48 A -> /home/arobert/link/B/C/D/A
-rw-rw-r-- 1 arobert arobert 0 5月 11 10:13 a
drwxrwxr-x 2 arobert arobert 4096 5月 11 10:13 .

Move all folders except one [duplicate]

This question already has answers here:
How to move files and directories excluding one specific directory to this directory
(3 answers)
Closed 5 years ago.
I have two directories dir1 and dir2. I need to move the content of folder dir1 to dir2 except one folder dir1/src.
I tried this
mv !(src) dir1/* dir2/
But it dosn't work, it still displays this error
bash: !: event not found
Maybe you are looking for something like this?
The answer to my question there states that what you are trying to to is achievable by using the extglob bash shell option. You can turn it on by executing shopt -s extglob or by adding that command to your ~/.bashrc and relogin. Afterwards you can use the function.
To use your example of moving everything from dir1 except dir1/src to dir2, this should work:
mv -vt dir2/ dir1/!(src)
Example output:
$ mkdir -pv dir1/{a,b,c,src} dir2
mkdir: created directory 'dir1'
mkdir: created directory 'dir1/a'
mkdir: created directory 'dir1/b'
mkdir: created directory 'dir1/c'
mkdir: created directory 'dir1/src'
mkdir: created directory 'dir2'
$ ls -l dir1/
total 16
drwxrwxr-x 2 dw dw 4096 Apr 7 13:30 a
drwxrwxr-x 2 dw dw 4096 Apr 7 13:30 b
drwxrwxr-x 2 dw dw 4096 Apr 7 13:30 c
drwxrwxr-x 2 dw dw 4096 Apr 7 13:30 src
$ ls -l dir2/
total 0
$ shopt -s extglob
$ mv -vt dir2/ dir1/!(src)
'dir1/a' -> 'dir2/a'
'dir1/b' -> 'dir2/b'
'dir1/c' -> 'dir2/c'
$ ls -l dir1/
total 4
drwxrwxr-x 2 dw dw 4096 Apr 7 13:30 src
$ ls -l dir2/
total 12
drwxrwxr-x 2 dw dw 4096 Apr 7 13:30 a
drwxrwxr-x 2 dw dw 4096 Apr 7 13:30 b
drwxrwxr-x 2 dw dw 4096 Apr 7 13:30 c
More information about extglob can be found here.

How to get the name of the executables files in bash with ls

I try to get the name of the executable files using ls -l.
Then I tried to get the lines of ls -l which have a x using grep -w x but the result is not right : some executable files are missing (the .sh).
I just need the name of the executable files not the path but I don't know how ...
user#user-K53TA:~/Bureau$ ls -l
total 52
-rwxrwxrwx 1 user user 64 oct. 6 21:07 a.sh
-rw-rw-r-- 1 user user 11 sept. 29 21:51 e.txt
-rwxrwxrwx 1 user user 140 sept. 29 23:42 hi.sh
drwxrwxr-x 8 user user 4096 juil. 30 20:47 nerdtree-master
-rw-rw-r-- 1 user user 492 oct. 6 21:07 okk.txt
-rw-rw-r-- 1 user user 1543 oct. 6 21:07 ok.txt
-rw-rw-r-- 1 user user 119 sept. 29 23:27 oo.txt
-rwxrwxr-x 1 user user 8672 sept. 29 21:20 prog
-rw-rw-rw- 1 user user 405 sept. 29 21:23 prog.c
-rw-rw-r-- 1 user user 0 sept. 29 21:58 rev
drwxrwxr-x 3 user user 4096 sept. 29 20:51 sublime
user#user-K53TA:~/Bureau$ ls -l | grep -w x
drwxrwxr-x 8 user user 4096 juil. 30 20:47 nerdtree-master
-rwxrwxr-x 1 user user 8672 sept. 29 21:20 prog
drwxrwxr-x 3 user user 4096 sept. 29 20:51 sublime
Don't parse ls. This can be done with find.
find . -type f -perm /a+x
This finds files with any of the executable bits set: user, group, or other.
Use find instead:
find -executable
find -maxdepth 1 -type f -executable
find -maxdepth 1 -type f -executable -ls
One can use a for loop with glob expansion for discovering and manipulating file names. Observe:
#!/bin/sh
for i in *
do # Only print discoveries that are executable files
[ -f "$i" -a -x "$i" ] && printf "%s\n" "$i"
done
Since the accepted answer uses no ls at all:
ls -l | grep -e '^...x'

Unable to set SGID bit on linux directory

I am trying to set SGID bit on all directories within this directory, but uanble to do so.
Can anybody please help ?
$ ls -ld Music
drwxrwxr-x 16 pankaj usrgrp 1024 Apr 14 14:54 Music
$ chmod -R g+s Music
$ echo $?
0
$ ls -ld Music
drwxrwxr-x 16 pankaj usrgrp 1024 Apr 14 14:54 Music
$

How to remove and re-create an existing symlink in one single command?

I have a symlink for my live server called current and I have releases in the releases directory, i.e current -> releases/2012-05-08_15-13
If I want to update the symlink of my current directory, I have to unlink/rm it and re ln -s it.
My question is: How can I remove the symlink and update it to the latest release in one step.
The form of ln is
ln -sf sourcefile targetlink
Try
ln -sf releases/2012-05-08_15-13 current
to remove the current and create the new link.
If you want to do it in a single command, do as #hughw suggests and run ln -sf.
If you want to replace the symlink atomically (ie. so that there's no point in time where the symlink doesn't exist) create a new symlink, then mv it over the old one.
As suggested by ToddR, here is the only answer that actually works on maybe most flavours of Linux - definately Ubuntu - which uses ln from coreutils package). Let me prove it to you.
matthewh#xen:~$ mkdir -p releases/dirA
matthewh#xen:~$ mkdir -p releases/dirB
matthewh#xen:~$ ln -s releases/dirA
matthewh#xen:~$ ls -l dirA
lrwxrwxrwx 1 matthewh matthewh 13 Apr 7 09:58 dirA -> releases/dirA
matthewh#xen:~$ ln -sf releases/dirB
matthewh#xen:~$ rm dirA
matthewh#xen:~$ ln -s releases/dirA current
matthewh#xen:~$ ln -sf releases/dirB current
matthewh#xen:~$ ls -l current
lrwxrwxrwx 1 matthewh matthewh 13 Apr 7 09:59 current -> releases/dirA <--- DOESN'T WORK!
matthewh#xen:~$ ln -sfn releases/dirB current <--- WORKS!
matthewh#xen:~$ ls -l current
lrwxrwxrwx 1 matthewh matthewh 13 Apr 7 09:59 current -> releases/dirB
So the correct method on Linux is:
ln -sfn source target
-n, --no-dereference
treat LINK_NAME as a normal file if it is a symbolic link to a directory
This is essential, if you do not use -n switch you will end up with a symlink inside source directory named "target".
In my examples,
matthewh#xen:~$ ls -l releases/dirA/
total 0
lrwxrwxrwx 1 matthewh matthewh 13 Apr 7 10:03 dirB -> releases/dirB
correct answer:
ln -s new current_tmp && mv -Tf current_tmp current
Move is atomic operation.
Don't use 'ln -snf'.
strace 'ln -snf' shows two system calls unlink + symlink.
This example clears the use of -sfn switch:
drwxr-xr-x. 10 root root 4096 Aug 25 18:24 .
dr-xr-xr-x. 25 root root 4096 Aug 19 10:32 ..
lrwxrwxrwx. 1 wildfly wildfly 25 Aug 25 18:15 wildfly -> /opt/wildfly-8.2.0.Final/
drwxr-xr-x. 10 wildfly wildfly 4096 Aug 25 18:28 wildfly-8.2.0.Final
link to link
| |
[gecloud#ip-10-227-224-45 opt]$ sudo ln -sfn wildfly-8.2.0.Final /opt/wildfly
[gecloud#ip-10-227-224-45 opt]$ ls -la
total 115540
drwxr-xr-x. 10 root root 4096 Aug 25 18:34 .
dr-xr-xr-x. 25 root root 4096 Aug 19 10:32 ..
lrwxrwxrwx. 1 root root 19 Aug 25 18:34 wildfly -> wildfly-8.2.0.Final
drwxr-xr-x. 10 wildfly wildfly 4096 Aug 25 18:28 wildfly-8.2.0.Final

Resources