I have a directory reference in my Downloads directory that contains a symbolic link (created with ln -s) to another directory. I get conflicting error message when trying to remove the symlink:
rm returns "Is a directory"
rmdir returns "Not a directory"
This only occurs with cellranger/ (followed by a forward slash) and not with cellranger.
[tom#rlgsw68 cellranger]$ pwd
/home/tom/Downloads/reference
[tom#rlgsw68 cellranger]$ ls -lth
lrwxrwxrwx 1 tom genome 33 Apr 4 14:52 cellranger -> /analysisdata/genomes/cellranger/
[tom#rlgsw68 cellranger]$ rm cellranger/
rm: cannot remove directory `cellranger/': Is a directory
[tom#rlgsw68 cellranger]$ rmdir cellranger/
rmdir: cellranger/: Not a directory
[tom#rlgsw68 cellranger]$ rm cellranger
Why does neither of these commands to remove the symlink work and why do these conflicting errors occur? What is the recommended way to remove symbolic links without removing the content in the source directory. rm -rf cellranger/ also does not remove the symlink (but does not return an error).
Information: I'm running a linux server (Debian 9.0). These errors occur with both bash and zsh. Ambiguous names have been removed from the example. I encountered this when a directory included a link to the parent directory in addition to the contents:
/home/tom/Downloads/reference/cellranger/cellranger/ -> /analysisdata/genomes/cellranger/
By putting a trailing slash you are referring to the directory the symlink points to, no longer the symlink itself. Printing the inode number (the number that a path refers to in the file system) shows the difference between dereferencing the symlink and the directory:
$ cd "$(mktemp --directory)"
$ mkdir a
$ stat --format %i a/
9
$ ln --symbolic a b
$ stat --format %i b
10
$ stat --format %i b/
9
This may be related to the fact that a symlink is never a directory, it is always just a file containing a path.
Related
So, I was getting rid of the unwanted files and I deleted some folder. After some time I saw another folder which appears red in ls -la. I knew I deleted the actual folder and thus I was very sad and tried to run foremost to recover but bad luck. Can anyone give me some good aliases/function that deletes like rm -rf but if that file is linked to other files then it won't delete. Like if /usr/bin/a.py is linked to /root/a.py and I run rm -rf /usr/bin/a.py then /root/a.py will be deleted. How can I prevent that from an alias?
Joe W's answer is correct in saying rm typically does not delete the targets of symlinks, but to say that it "does not follow symlinks" is not quite accurate, at least on my system (GNU coreutils 8.25). And deleting files is a place where accuracy is pretty important! Let's take a look at how it behaves in a few situations.
If your symlink is to a file, rather than to a directory, there is no plausible way to accidentally delete the target using rm. You would have to do something very explicit like rm "$(readlink file)".
Symlinks to directories, however, get a bit dicey, as you saw when you accidentally deleted one. Here's a test case we can use:
$ mkdir test1
$ touch test1/foo.txt
$ ln -s test1 test2
$ ls -lR
.:
total 4
drwxrwxr-x 2 soren soren 4096 Jun 29 17:02 test1
lrwxrwxrwx 1 soren soren 5 Jun 29 17:02 test2 -> test1
./test1:
total 0
-rw-rw-r-- 1 soren soren 0 Jun 29 17:02 foo.txt
These are all safe:
rm test2 (deletes the symlink only)
rm -r test2 (deletes the symlink only)
rm -rf test2 (deletes the symlink only)
rm test2/ (rm: cannot remove 'test2/': Is a directory -- no action taken)
rm -rf *2 (or any other glob matching the symlink -- deletes the symlink only)
These are not safe:
rm -r test2/ (rm: cannot remove 'test2/': Not a directory -- but deletes the contents of the test1 directory)
rm -rf test2/ (deletes the contents of the directory, leaves the symlink, no error)
rm -rf test2/* (deletes the contents of the directory, leaves the symlink, no error)
The last unsafe case is probably obvious behavior, at least to someone well-acquainted with the shell, but the two before it are quite a bit more subtle and dangerous, especially since tab-completing the name of test2 will drop the trailing slash in for you!
It's interesting to note that test has similar behavior, considering a symlink to a directory with a trailing slash to be not a symlink but a directory, while a symlink without a trailing slash is both:
$ [ -L "test2" ] && echo "is link"
is link
$ [ -d "test2" ] && echo "is directory"
is directory
$ [ -L "test2/" ] && echo "is link"
$ [ -d "test2/" ] && echo "is directory"
is directory
Here's a previous treatment of "deleting a symlink to a directory without deleting the target," with a less thorough analysis of exactly what works and what doesn't but with a bunch of other useful information.
Unfortunately, I am not aware of any way to alias rm to prevent this mistake. I suppose you could write a function to parse the arguments to rm and warn you if any of them are symlinks that end with a trailing slash, something like this:
function rm {
for i in "$#"; do
if [[ $i =~ /$ ]] && [ -L "${i:0:-1}" ]; then
read -rp "Really delete symlink '$i' with trailing slash (y/n)? " result
[ "$result" != "y" ] && return
fi
done
command rm "$#"
}
Use at your own risk, though! It passes shellcheck and it worked when I tested it, but implementing a wrapper on top of something as fundamental and dangerous as rm gives me the heebie-jeebies.
Two other potentially useful switches you might include in your alias/function would be --one-file-system (at least skips files past a symlink or mount onto a different drive) and, if you don't already use it, -i or -I to prompt when doing something potentially dangerous.
I don't think that it is behaving the way you're indicating. rm doesn't follow symlinks
https://superuser.com/questions/520058/does-rm-r-follow-symbolic-links
https://superuser.com/questions/382314/does-rm-rf-follow-symbolic-links
The only way you could delete something is if you did something like rm -rf myFolder/* where myFolder is a linked directory.
I'm trying to exclude the current directory from the tarball without excluding its contents, because when I extract it out using the -k flag I get an exit status of 1 and a message
./: Already exists
tar: Error exit delayed from previous errors.
How do I do this? I've tried the --exclude flag but that excludes the contents also (rightly so). I'm trying to code this for both the OSX/BSD and GNU versions of tar.
Test case:
# Setup
mkdir /tmp/stackoverflow
cd /tmp/stackoverflow
mkdir dir
touch dir/file
# Create
tar cCf dir dir.tar .
# List contents
tar tf dir.tar
gives
./
./file
showing that the current directory ./ is in the tar. This would be fine, but when I do the following:
mkdir dir2
tar xkfC dir.tar dir2
due to the -k flag, I get an exit code of 1 and the message
./: Already exists
tar: Error exit delayed from previous errors.
To exclude the current directory you can create your archive on this way:
tar cf /path/to/dir.tar ./*
use ./*instead of ., this will not match current directory (.) and therefore not include in the archive
This does the trick:
GLOBIGNORE=".:.."
cd dir
tar cf ../dir.tar *
The extra cd is to replace the use of the -C flag, so that we can use a glob. The GLOBIGNORE ignores the current directory, but also sets shopt -s dotglob implicitly to include hidden files. Using * instead of ./* means that the tar file listing doesn't list ./file, but instead lists it as file. The entire listing of the tar is:
file
On macOS 10.12.5, bash 3.2.57 is generally case sensitive. This is not the case, however, with file names. For instance:
mbp:~ $ rmdir a
rmdir: a: No such file or directory
mbp:~ $ rmdir A
rmdir: A: No such file or directory
This was done to verify that neither a nor A exists.
mbp:~ $ mkdir a
mbp:~ $ mkdir A
mkdir: A: File exists
mbp:~ $ rmdir a A
rmdir: A: No such file or directory
What is the reason for such behavior?
The answer, as pointed out in the link given in the comments (thank you, Digital Chris), is that case insensitivity is not a feature of bash, but of the file system configuration (HFS+, not Case Sensitive).
I'm trying to make an alias of a directory in a set of directories
for D in $(find * -maxdepth 0 -type d) ; do
ln -s location/to/directory/ $D/Test2 ;
done
It looks like the link is made correctly (I can see it in my finder window), but when I double click it, I get the error The operation can't be completed because the original item for "Test2" can't be found.
Why doesn't this work? Is there a way from a bash script to make a "normal" mac alias? I have opened up the permissions, as suggested here, without any luck.
Use the absolute source path while creating the link. That worked for me having the same issue.
You want to create a symbolic link called Test2 in each directory in the current directory, and each created link should point to location/to/directory.
for dir in */; do
ln -s 'location/to/directory' "$dir/Test2"
done
The slash after * ensures that we will only match directories in the current directory, or links to directories in the current directory.
If you're only interested in real directories an not symbolically linked directories, you may use
find . -type d -mindepth 1 -maxdepth 1 \
-exec ln -s 'location/to/directory' {}/Test2 ';'
Note that the link destination is relative to the location of the link, so if a directory does not contain location/to/directory, the link will be "dead".
You may solve this be specifying an absolute path for the links.
What are you attempting to do?
Think of a link as a cp command. Maybe that will help:
# Copies the 'svnadmin' command from /opt/svn/bin to /usr/local/bin
$ cp /opt/svn/bin/svnadmin /usr/local/bin
# Links the 'svnadmin' command from /opt/svn/bin to /usr/local/bin
$ ln -s /opt/svn/bin/svnadmin /usr/local/bin
Note that the ln and cp command have the same order of files.
In your command, you're linking whatever location/to/directory/ to $D/test2 over and over again.
Also, -maxdepth 0 won't be in the first level of the directory.
I use ln when I install new software, and the binary commands are in some other directory. Instead of building on $PATH to include all of these extra directories, I symbolically link them to /usr/local/bin:
$ cd /usr/share/apache-ant/bin
$ for file in *
> do
> [[ -f $file ]] || continue
> ln -s $PWD/$file /usr/local/bin/$file
> done
Note that the link simply copies the entire reference for the first file to the link. I want to make sure that this link works everywhere, so I prefix that $PWD in front of it. This way, my links look like this:
$ ls -l ant
lrwxr-xr-x 1 root wheel 29 Sep 3 2014 ant -> /usr/share/apache-ant/bin/ant
I have a rm command which clears all the files in a particular directory.
#!/usr/bin/ksh
cd /asd/ded/ses/ddd/rty/leg/
rm *.sas7bdat
rm p_bt*
Unfortunately it clears all the files under this directory, but now I just want it to clear in "parent directory" i.e. "/asd/ded/ses/ddd/rty/leg/" and not in "/asd/ded/ses/ddd/rty/leg/21_11" which is the child directory inside it.
I know level rm is possible in bash. Does it change for KSH and if yes then how.
LonelySoul,
Chepner is correct. The default for 'rm' in ksh is to only remove the files in the current directory. You can remove files from the lower directories (recursively) by adding the '-r' option.
If you are observing different behavior, you may have an alias setup somewhere in your profile. Try entering 'whence rm' to see if there is an alias that is causing you unexpected behavior.
Examples.
>pwd
/tmp
>touch abc.txt
>mkdir ced
>touch ced/abc.txt
>rm abc.txt (will remove abc.txt in /tmp, but leave the file in directory ced.
>whence rm
rm -f