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 .
Related
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
The program I'm running inside the Docker image, first creates a directory and writes some file into the directory.
To transfer the directory onto the host machine, I've mounted a datadir/ and then moved the directory created inside the image into the mounted directory, e.g.:
mkdir datadir
DATADIR=datadir/
docker run -i \
-v $(pwd)/$DATADIR:/$DATADIR/ ubuntu \
bash -c "mkdir /x1 && echo 'abc' > x1/test.txt && mv x1 $DATADIR"
But when I tried to access datadir/x1, it has root as the owner and it comes with read-only permissions:
$ mv datadir/x1/ .
mv: cannot move 'datadir/x1/' to './x1': Permission denied
$ ls -lah datadir/x1/
total 12K
drwxr-xr-x 2 root root 4.0K Jun 28 16:38 .
drwxrwxr-x 3 alvas alvas 4.0K Jun 28 16:38 ..
-rw-r--r-- 1 root root 4 Jun 28 16:38 test.txt
Is mounting the additional volume and copying the created directory inside the image the right approach to move files between the Docker image and the host machine? If not, what's the "canonical" way to perform the same operation?
About the directory permissions, what should be the correct way to assign the host machine permission to any files inside the mounted volume?
I've tried to chmod -R 777 inside the Docker image but I don't think that's the safe approach, i.e.:
$ docker run -i -v $(pwd)/$DATADIR:/$DATADIR/ -i ubuntu bash -c "mkdir /x1 && echo 'abc' > x1/test.txt && mv x1 $DATADIR && chmod -R 777 $DATADIR"
$ mv datadir/x1/ .
$ ls -lah x1
total 12K
drwxrwxrwx 2 root root 4.0K Jun 28 16:47 .
drwxrwxr-x 12 alvas alvas 4.0K Jun 28 16:47 ..
-rwxrwxrwx 1 root root 4 Jun 28 16:47 test.txt
To avoid permission issues use docker cp
For example:
# This is the directory you want to save the outputs
mkdir datadir
# We create a directory and file inside it, inside the Docker image.
# And we are naming the Docker image "thisinstance"
docker run -i --name thisinstance ubuntu \
bash -c "mkdir /x1 && echo 'abc' > x1/test.txt"
# Copies the new directory inside the Docker image to the host.
docker cp thisinstance:/x1 datadir/
# Destroy the temporary container
docker rm thisinstance
# Check the ownership of the directory and file
ls -lah datadir/x1/
[out]:
drwxr-xr-x 3 alvas 679754705 102B Jun 29 10:36 ./
drwxr-xr-x 3 alvas 679754705 102B Jun 29 10:36 ../
-rw-r--r-- 1 alvas 679754705 4B Jun 29 10:36 test.t
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.
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
$
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