Deleting a folder that contains symlinks - linux

If I rm -rf a folder that has soft links in it, will it try to follow those links and delete the corresponding folder, or will it simply unlink them?
I have a copy of my home directory with symbolic links in it, and I'm scared to rm -rf it in case it follows those links and blows up the corresponding folders...

Generally speaking, rm doesn't "delete". It "unlinks". This means that references to a file are removed by rm. When the number of references reaches zero, the file will no longer be accessible and in time, the area of disk where it resides will be used for something else.
When you rm a directory, the stuff inside the directory is unlinked. Symbolic links are (sort of like) files with the name of their targets inside them and so they're just removed. To actually figure out what they're pointing to and then unlink the target is special work and so will not be done by a generic tool.

No. rm -rf won't follow symbolic links - it will simply remove them.
% mkdir a
% touch a/foo
% mkdir b
% ln -s a b/a
% rm -rf b
% ls a
foo

Here is axample:
find a b
a
a/1
a/2
b
ll
drwxr-xr-x 2 ****** ****** 4.0K Feb 6 15:11 a
lrwxrwxrwx 1 ****** ****** 1 Feb 6 15:13 b -> a
.
rm -rf b
gives
find a b
a
a/1
a/2
.
rm -rf b/
gives error:
rm: cannot remove `b/': Not a directory
Conclusion:
rm does not follow symlinks

POSIX quote
Since rm -r is POSIX we can also look at what they have to say: https://pubs.opengroup.org/onlinepubs/009604599/utilities/rm.html
The rm utility shall not traverse directories by following symbolic links into other parts of the hierarchy, but shall remove the links themselves.
The rationale section mentions a bit more:
The rm utility removes symbolic links themselves, not the files they refer to, as a consequence of the dependence on the unlink() functionality, per the DESCRIPTION. When removing hierarchies with -r or -R, the prohibition on following symbolic links has to be made explicit.

Related

ZSH+GREP+REGEX. Why this snippet act as rm -r /

This is a little anecdote from earlier on why not running root is vital.
I was sorting my home directory and deleted a few compressed files I had, I wrote
ls . | grep -P 'zip|tar|7z' | xargs rm and thought, hey I could also write this as rm -r $(ls . | grep -P '...') I suppose.
The second part I didn't mean to use it since there was nothing to delete, it was morelike a mental exercise, I wrote it next to the last command with a 'divider' to visually compare them.
ls . | grep -P 'zip|tar|7z' | xargs rm **//** rm -r $(ls . | grep -P '...')
Being **//** the "divider" and ... the mental "substitute" for 'zip|tar..'
I thought this wouldn't run but to my surprise, it acted as rm -r /and tried to delete everything, luckily permissions saved me and nothing was deleted.
But I'm curious why it'd work that way,
my guess is that rm **//** somehow translated to rm / but I'm not sure.
In the zsh shell, **//** would expand to all names under / as well to all names below the current directory (recursively).
From an empty directory on my system:
$ echo **//**
/altroot /bin /boot /bsd /bsd.booted /bsd.rd /bsd.sp /dev /etc /extra /home /mnt /root /sbin /sys /tmp /tmp_mnt /usr /var /vol
Why? Well, **/ matches all directories recursively under the current directory. More importantly, it matches the current directory, but since the current directory's name is not available inside the current directory, there's no entry returned for that.
However, when you add a / to that to create **//, then you get a lone / back for the current directory. Again in an empty directory:
$ echo **//
/
Then, if you add a further ** to make **//**, you pick up all names from the root directory, together with all names from the current directory and below (directory names from the current directory and below would occur twice in the list).
Your xargs is calling
rm **//** rm -r $(ls . | grep -P '...')
If you're using GNU rm, it will helpfully rearrange the command line so that it is interpreted the same as
rm -r **//** rm $(ls . | grep -P '...')
What this does should now be clear.
If you want to delete all regular files in the current directory that have filename suffixes .zip, .tar or .7z, use
rm ./*.(zip|tar|7z)(.)
in the zsh shell. If want to do that recursively down into subdirectories, use
rm ./**/*.(zip|tar|7z)(.)
The glob qualifier (.) makes the globbing pattern only match regular files. You could even restrict it to files above a certain size, say 10MB, with ./**/*.(zip|tar|7z)(.Lm+10).
One difference is that the ls ... | xargs .... solution also works if there are really a lot of files involved, while your rm $( .... ) might produce a argument list too long error. But if this is not an issue in your case, an even simpler attempt would be (assuming here Zsh; I don't understand why you tagged this bash, since you explicitly refer to Zsh only in your question)
rm *(zip|tar|7z)*(N)
which would express your original statement; I believe however that you really meant
rm -- *.(zip|tar|7z)(N)
because the solution you posted would also remove a file tarpit.txt, for instance. The (N) flag is a frail attempt to treat the case, that you don't have any file matching the pattern. Without the (N), you would get an error message from Zsh, and rm would receive the unexpanded file pattern, and, since it is unlikely that a file of this name exists, would output a second error message. By using (N), Zsh would simply pass nothing in this case (without complaining), and in fact rm would be invoked without arguments. Of course you would then get a rm: missing operand on stderr, and if you don't like this, you can filter this message.
UPDATES:
As Kusalananda has pointed out in his/her comment, omitting the (N) would, by default, make zsh only print an error message, if no files match the pattern, but not cause rm to be invoked.
Also added the -- flag to rm to allow removal of, i.e., a file called -rf.tar.

Removing file from folder in linux

Linux os
Hello
i have downloaded the file using wget. the file downloaded with some messy name like
index.html?format=csv&timezone=Asia%2FKolkata&use_labels_for_header=true.
now i am trying to remove this file using rm, but its not getting removed.
Linux os
sudo rm -f Unconfirmed\sudo rm -f Unconfirmed\index.html?format=csv&timezone=Asia%2FKolkata&use_labels_for_header=true
how can i removed it from folder.
It works as below. Make sure you have permission for that file/folder to edit(delete or update). You have used sudo that is correct.
To remove the folder with all its contents(including all interior
folders):
rm -rf /path/to/directory
To remove all the contents of the folder(including all interior folders) but not the folder itself:
rm -rf /path/to/directory/* or
rm -rf /path/to/directory/{*,.*}
if you want to make sure that hidden files/directories are also removed.
To remove all the "files" from inside a folder(not removing interior folders):
rm -f /path/to/directory/{*,.*}
Where:
rm - stands for "remove"
-f - stands for "force" which is helpful when you don't want to be asked/prompted
if you want to remove an archive, for example.
-r - stands for "recursive" which means that you want to go
recursively down every folder and remove everything.
rm -rf "yourfilename" put double quotes over your file name.
Also check if you have permission to delete this file.
make notifies you of the deletion by printing an rm -f command that specifies the file it is deleting.
Eg:
Here MergeSort is the file that I need to remove.

Batch removing a sub folder in several parent folders

In linux, I have a group of folders, that all contain the same sub folder structure. E.g.
FolderA/x/y/z/file1.txt
FolderA/x/y/z/file2.txt
FolderB/x/y/z/file1.txt
FolderC/x/y/z/file1.txt
I want to run a batch process to remove one of the subfolders, but leave all files and folders beneath unchanged. E.g. if I were to remove folder "y":
FolderA/x/z/file1.txt
FolderA/x/z/file2.txt
FolderB/x/z/file1.txt
FolderC/x/z/file1.txt
I've tried putting together some combination of find and mv, but can't quite get it right
find . -name y -type d -exec sh -c '
for d; do echo mv "$d"/* "$d"/..; echo rmdir "$d"; done' _ {} +
Remove the echos if the results look like what you expect.
Considering You want to remove one of the subfolders, but leave all files and folders beneath unchanged, here is something you can do. It's NOT an exact Solution, kind of trick that might work for you. I have done it many times on my computer.
You can recursively copy the subfolder's contents to it's parent folder and then traverse to parent directory and then finally recursively remove the subfolder.
Traverse to subfolder
$ cd path/to/SubFolder
Copy all Contents Recursively to Parent Folder
$ cp -R * ..
Traverse to Parent Folder
$ cd ..
Remove recursively Subfolder
$ rm -rf SubFolder/
Say you have a folder y, then,
$ cd path/to/folder/y
$ cp -R * ..
$ cd ..
$ rm -rf y/
For running a Batch Process You can figure out the path to subfolder using find command.
Notice that this trick can consume a lot of time & resources, If you have a lot of folders in the folders. But that works !

Edit the contents of directory tables (Linux)

How can I edit the contents of a directory file? Since directories are just special files with a table of links to the files contained within, I should be able to view the table right?
When I open a directory in vim I can't go into insert mode to edit the links, which I assumed would be the basic available functionality, even if it didn't work.
Like Kevin said, you can't do it. What you CAN do is see symlinks and hardlinks with find, and I suppose use the ln command to do what you want from there.
Using the find command
You can see all normal files with this command...
find <DIR> -type f -links 1
You can see hardlinked files with this command...
find <DIR> -type f -links +1
To see all symlinks, you could use...
find <DIR> -type l
You can add -ls to any of these to get a more verbose output.
Finding all symlinks in current directory, non-recursively, with additional file info:
find . -type l -maxdepth 1 -ls
Using ln and unlink commands
Use ln to create links...
ln <current_file> <link_filename>
...and for symlinks...
ln -s <current_file> <shortcut_filename>
..and of course to delete a hardlink, use rm as usual...
rm <hard_link_filename>
...and for deleting a symlink....
unlink <symlink_filename>
Note: if the symlink links to a directory, be sure not to include a final "/"
This will not work.... unlink /var/www/deleteme/ if deleteme is a directory on the other end.
Hope this helps.

Unix command deleted every directory even though not specified

I am very new to the unix. I ran the following command.
ls -l | xargs rm -rf bark.*
and above command removed every directory in the folder.
Can any one explained me why ?
The -r argument means "delete recursively" (ie descend into subdirectories). The -f command means "force" (in other words, don't ask for confirmation). -rf means "descend recursively into subdirectories without asking for confirmation"
ls -l lists all files in the directory. xargs takes the input from ls -l and appends it to the command you pass to xargs
The final command that got executed looked like this:
rm -rf bark.* <output of ls -l>
This essentially removed bark.* and all files in the current directory. Moral of the story: be very careful with rm -rf. (You can use rm -ri to ask before deleting files instead)
rm(1) deleted every file and directory in the current working directory because you asked it to.
To see roughly what happened, run this:
cd /etc ; ls -l | xargs echo
Pay careful attention to the output.
I strongly recommend using echo in place of rm -rf when constructing command lines. Only if the output looks fine should you then re-run the command with rm -rf. When in doubt, maybe just use rm -r so that you do not accidentally blow away too much. rm -ir if you are very skeptical of your command line. (I have been using Linux since 1994 and I still use this echo trick when constructing slightly complicated command lines to selectively delete a pile of files.)
Incidentally, I would avoid parsing ls(1) output in any fashion -- filenames can contain any character except ASCII NUL and / chars -- including newlines, tabs, and output that looks like ls -l output. Trying to parse this with tools such as xargs(1) can be dangerous.
Instead, use find(1) for these sorts of things. To delete all files in all directories named bark.*, I'd run a command like this:
find . -type d -name 'bark.*' -print0 | xargs -0 rm -r
Again, I'd use echo in place of rm -r for the first execution -- and if it looked fine, then I'd re-run with rm -r.
The ls -l command gave a list of all the subdirectories in your current present-working-directory (PWD).
The rm command can delete multiple files/directories if you pass them to it as a list.
eg: rm test1.txt test2.txt myApp will delete all three of the files with names:
test1.txt
test2.txt
myApp
Also, the flags for the rm command you used are common in many a folly.
rm -f - Force deletion of files without asking or confirming
rm -r - Recurse into all subdirectories and delete all their contents and subdirectories
So, let's say you are in /home/user, and the directory structure looks like so:
/home/user
|->dir1
|->dir2
`->file1.txt
the ls -l command will provide the list containing "dir1 dir2 file1.txt", and the result of the command ls -l | xargs rm -rf will look like this:
rm -rf dir1 dir2 file1.txt
If we expand your original question with the example above, the final command that gets passed to the system becomes:
rm -rf di1 dir2 file1.txt bark.*
So, everything in the current directory gets wiped out, so the bark.* is redundant (you effectively told the machine to destroy everything in the current directory anyway).
I think what you meant to do was delete all files in the current directory and all subdirectories (recurse) that start with bark. To do that, you just have to do:
find -iname bark.* | xargs rm
The command above means "find all files in this directory and subdirectories, ignoring UPPERCASE/lowercase/mIxEdCaSe, that start with the characters "bark.", and delete them". This could still be a bad command if you have a typo, so to be sure, you should always test before you do a batch-deletion like this.
In the future, first do the following to get a list of all the files you will be deleting first to confirm they are the ones you want deleted.
find -iname bark.* | xargs echo
Then if you are sure, delete them via
find -iname bark.* | xargs rm
Hope this helps.
As a humorous note, one of the most famous instances of "rm -rf" can be found here:
https://github.com/MrMEEE/bumblebee-Old-and-abbandoned/commit/a047be85247755cdbe0acce6f1dafc8beb84f2ac
An automated script runs something like rm -rf /usr/local/........., but due to accidentally inserting a space, the command became rm -rf /usr /local/......, so this effectively means "delete all root folders that start with usr or local", effectively destroying the system of anyone who uses it. I feel bad for that developer.
You can avoid these kinds of bugs by quoting your strings, ie:
rm -rf "/usr/ local/...." would have provided an error message and avoided this bug, because the quotes mean that everything between them is the full path, NOT a list of separate paths/files (ie: you are telling rm that the file/folder has a SPACE character in its name).

Resources