Using find - Deleting all files/directories (in Linux ) except any one
find
can be a very good friend:
$ ls
a/ b/ c/
$ find * -maxdepth 0 -name 'b' -prune -o -exec rm -rf '{}' ';'
$ ls
b/
$
Explanation:
find * -maxdepth 0
: select everything selected by*
without descending into any directories-name 'b' -prune
: do not bother (-prune
) with anything that matches the condition-name 'b'
-o -exec rm -rf '{}' ';'
: callrm -rf
for everything else
By the way, another, possibly simpler, way would be to move or rename your favourite directory so that it is not in the way:
$ ls
a/ b/ c/
$ mv b .b
$ ls
a/ c/
$ rm -rf *
$ mv .b b
$ ls
b/
How to delete all files except directories?
find . -maxdepth 1 -type f -print0 | xargs -0 rm
The find
command recursively searches a directory for files and folders that match the specified expressions.
-maxdepth 1
will only search the current level (when used with.
or the top level when a directory is used instead), effectively turning of the recursive search feature-type f
specifies only files, and all files
@chepner recommended an improvement on the above to simply use
find . -maxdepth 1 -type f -delete
Not sure why I didn't think of it in the first place but anyway.
How to delete all files in a dir except ones with a certain pattern in their name?
This can be done as a one-liner, though I've preserved your variables:
#!/bin/bash
version="$(uname -r)"
dir="$HOME/Installed-kernels"
find "$dir" -maxdepth 1 -type f -not -name "*$version*" -print0 |xargs -0 rm
To set a variable to the output of a command, you need either $(…)
or `…`
, ideally wrapped in double-quotes to preserve spacing. A tilde isn't always interpreted correctly when passed through variables, so I expanded that out to $HOME
.
The find
command is much safer to parse than the output of ls
, plus it lets you better filter things. In this case, -maxdepth 1
will look at just that directory (no recursion), -type f
seeks only files, and -not -name "*$version*"
removes paths or filenames that match the kernel version (which is a glob, not a regex—you'd otherwise have to escape the dots). Also note those quotes; we want find
to see the asterisks, and without the quotes, the shell will expand the glob prematurely. The -print0
and corresponding -0
ensure that you preserve spacing by delimiting entries with null characters.
You can remove the prompts regarding read-only files with rm -f
.
If you also want to delete directories, remove the -type f
part and add -r
to the end of that final line.
Deleting all files except ones mentioned in config file
It is considered bad practice to pipe the exit of find
to another command. You can use -exec
, -execdir
followed by the command and '{}'
as a placeholder for the file, and ';'
to indicate the end of your command. You can also use '+'
to pipe commands together IIRC.
In your case, you want to list all the contend of a directory, and remove files one by one.
#!/usr/bin/env bash
set -o nounset
set -o errexit
shopt -s nullglob # allows glob to expand to nothing if no match
shopt -s globstar # process recursively current directory
my:rm_all() {
local ignore_file=".rmignore"
local ignore_array=()
while read -r glob; # Generate files list
do
ignore_array+=(${glob});
done < "${ignore_file}"
echo "${ignore_array[@]}"
for file in **; # iterate over all the content of the current directory
do
if [ -f "${file}" ]; # file exist and is file
then
local do_rmfile=true;
# Remove only if matches regex
for ignore in "${ignore_array[@]}"; # Iterate over files to keep
do
[[ "${file}" == "${ignore}" ]] && do_rmfile=false; #rm ${file};
done
${do_rmfile} && echo "Removing ${file}"
fi
done
}
my:rm_all;
delete all directories except one
With bash
you can do this with the extglob
option of shopt
.
shopt -s extglob
cd parent
rm -rf !(four)
With a posix shell I think you get to use a loop to do this
for dir in ./parent/*; do
[ "$dir" = "four" ] && continue
rm -rf "$dir"
done
or use an array to run rm
only once (but it requires arrays or using "$@"
)
arr=()
for dir in ./parent/*; do
[ "$dir" = "four" ] && continue
arr+=("$dir")
done
rm -rf "${arr[@]}"
or
for dir in ./parent/*; do
[ "$dir" = "four" ] && continue
set -- "$@" "$dir"
done
rm -rf "$@"
or you get to use find
find ./parent -mindepth 1 -name four -prune -o -exec rm -rf {} \;
or (with find
that has -exec +
to save on some rm
executions)
find ./parent -mindepth 1 -name four -prune -o -exec rm -rf {} +
Oh, or assuming the list of directories isn't too large and unwieldy I suppose you could always use
rm -rf parent/*<ctrl-x>*
then delete the parent/four
entry from the command line and hit enter where <ctrl-x>*
is readline's default binding for glob-expand-word
.
delete all folders and files within a linux directory except one folder and all contents inside that folder
Here's one way to do it:
(cd /usr/testing/member; find . -maxdepth 1 \( ! -name . -a ! -name public \) -exec echo rm -fr {} +)
That is: cd
into /usr/testing/member
, find all files and directories there, without going further below, and exclude the current directory (".") and any file or directory named "public", and execute a command for the found files.
This will print what would be deleted.
Verify it looks good, and then drop the echo
.
Related Topics
Android Studio 2.3 Using Emulator from Console, "/Dev/Kvm Device: Permission Denied" for Root User
Language-Agnostic Properly-Tabbing Code Editors for Linux
Doesn't Sh Support Process Substitution <(...)
Hiding User Input on Terminal in Linux Script
How to Continue One Thread at a Time When Debugging a Multithreaded Program in Gdb
Two File Descriptors to Same File
Using Sed to Remove a Block of Text
Whiptail: How to Redirect Output to Environment Variable
How to Grep Download Speed from Wget Output
Define Alias That References Other Aliases
Xdotool Commands Bound to Key Shortcuts Doesnot Work
Linux Kernel System Call Returns -1 Instead of {-1, -256}
Linux Shell Script for Each File in a Directory Grab the Filename and Execute a Program
What Is a Sysroot Exactly and How to Create One
How to Udp Broadcast with C in Linux