delete folder contents except a specified folder and its contents - linux

Per my other question, I have a script to update drupal core quickly and easily.
Presently, it moves the sites folder out of the directory, deletes the site folder's contents, then moves the sites folder back in.
mv ./sites ../sites
rm -rf *
cp -R /sources/drupal-7/* ./
mv ../sites ./sites
Given the fact that a sites folder can get fairly large, I would like to avoid moving it if at all possible, and delete only the other folders, leaving only the sites folder behind.
I have tried some other suggestions around the internet, a few from here, one of which used find, but that deleted the files and folders WITHIN the sites folder too.
I would also like to keep the source folder intact, i.e.: keep a sites folder within it for new sites, and move copy only other files/folders to the site to update like:
rm -rf * !sites/*
cp -R /sources/drupal-7/* ./ !sites/*
I have tried numerous methods, most of which simply don't work or give a syntax error (or delete sites or its contents)
EDIT: here is the script in its entirity, for clarity:
#/bin/bash
CWD=$(pwd)
cd $CWD
echo $CWD
if [[ $CWD = "/var/www/vhosts/"* ]]; then
echo "Updating drupal core files"
read -r -p "do you need to keep the .htaccess file? [y/N]" response
if [ $response = y ]
then
/usr/local/bin/drush vset maintenance_mode 1
mv ./.htaccess ../.htaccess
mv ./sites ../sites
rm -rf *
cp -R /sources/drupal-7/* ./
cp -R /sources/drupal-7/.* ./
mv ../sites ./sites
mv ../.htaccess ./.htaccess
chown -R httpd:httpd *
/usr/local/bin/drush up && /usr/local/bin/drush updb
/usr/local/bin/drush cc all && /usr/local/bin/drush cron
/usr/local/bin/drush vset maintenance_mode 0
elif [ $response = n ]
then
/usr/local/bin/drush vset maintenance_mode 1
mv ./sites ../sites
rm -rf *
cp -R /sources/drupal-7/* ./
cp -R /sources/drupal-7/.* ./
mv ../sites ./sites
chown -R httpd:httpd *
/usr/local/bin/drush up && /usr/local/bin/drush updb
/usr/local/bin/drush cc all && /usr/local/bin/drush cron
/usr/local/bin/drush vset maintenance_mode 0
else
echo "Response must be either y or n"
fi
else
echo "not in a web directory, exiting"
fi

Updated as per suggestions in comments:
find /path/to/dir -maxdepth 1 -mindepth 1 ! -name "sites" -type d -print0 | xargs -0 rm -rf

In bash you can enable extended globs (shopt -s extglob) and then use !(sites):
bash-3.2$ shopt -s extglob
bash-3.2$ rm -r !(sites)
bash-3.2$ cp -R /sources/drupal-7/!(sites) .

Related

Shell: Move file and folder listed in a list

I'm using Alpine Linux and would like to move specified files and folders from a list. My final goal being to reset a folder by making a backup of some files and folders inside it.
Here is my current script:
cd /mnt/server
mkdir /tmp/backup
[ -d folderOne ] && mv folderOne /tmp/backup
[ -f fileOne.txt ] && mv fileOne.txt /tmp/backup
rm -fr *
rm -fr .*
[ -d /tmp/backup/folderOne ] && mv /tmp/backup/folderOne /mnt/server
[ -f /tmp/backup/fileOne.txt ] && mv /tmp/backup/fileOne.txt /mnt/server
rm -fr /tmp/backup
I would like to use a list to avoid script redundancy and use as few external software as possible. So the idea would be to have a list containing the path to the folders and files to backup. I would use a for loop to go through the list and, if the file/folder exists, then move it by recreating the hierarchy if necessary.
folder/folder2/file.txt would recreate the missing folders and place the file there. And if it is a folder (folder/folderTwo) then it recreates the missing hierarchy and places the folder (and its contents) in it.
#! /bin/bash
set -eu
workdir=/mnt/server
cd "$workdir"
mkdir /tmp/backup
movables=(folderOne fileOne.txt folder/folder2/file.txt)
for move in "${movables[#]}" ; do
if [[ -d $move || -f $move ]] ; then
if [[ "$move" = */* ]] ; then
mkdir -p /tmp/backup/"${move%/*}"
fi
mv "$move" /tmp/backup/"${move%/*}"
fi
done
shopt -s extglob
rm -fr !(.|..)
find /tmp/backup
for move in "${movables[#]}" ; do
if [[ -d /tmp/backup/$move || -f /tmp/backup/$move ]] ; then
if [[ "$move" = */* ]] ; then
mkdir -p "$workdir/${move%/*}"
fi
mv /tmp/backup/"$move" "$workdir"
fi
done
rm -fr /tmp/backup

How can I find the temp file?

I was going to get it to work by creating a temporary file, print the name to the screen, using the brace expansion to make it quickly by creating a backup file with the .BAK suffix in the same directory, using the ls for both files, and delete both files:
#!/bin/bash
fileone=$(mktemp)
echo cp "$fileone"{,.bak}
ls "$fileone"*
rm -rf "$fileone"*
I got an error that tells me that it did not find "/tmp/tmp.UQxlOPQXri.BAK" in my output.
Here's what I got when I run it
++ mktemp
+ fileone=/tmp/tmp.CYQY3BP693
+ echo cp /tmp/tmp.CYQY3BP693 /tmp/tmp.CYQY3BP693.bak
cp /tmp/tmp.CYQY3BP693 /tmp/tmp.CYQY3BP693.bak
+ ls /tmp/tmp.CYQY3BP693
/tmp/tmp.CYQY3BP693
+ rm -rf /tmp/tmp.CYQY3BP693
#!/bin/bash
fileone=$(mktemp)
echo cp "$fileone"{,.bak}
ls "$fileone"*
rm -rf "$fileone"*
You generate a temp file, echo what a cp would do, but you never do it.
That's all.

Bash recursively execute a command on each directory

I have a directory with many subdirectories inside, i want to execute a command on each of those subdirectories.
What i want to do is run 'svn up'
this is what i have tried so far
find . -type d -maxdepth 1 -exec svn "up '{}'" \;
and
for dir in * do cd $dir; svn up; cd ..;
None of them works so far (I have tried many things without luck)
You just need a trailing slash on the glob:
for d in */; do # only match directories
( cd "$d" && svn up ) # Use a subshell to avoid having to cd back to the root each time.
done
This works for me - the -d checks for a directory:
for f in *; do if [ -d "$f" ]; then cd "$f"; echo "$f"; cd ..; fi; done
echo "$f" can be substituted for whatever command you wish to run from inside each directory.
Note that this, and the trailing / solution, both match symbolic links, as well as files. If you want to avoid this behaviour (only enter real directories), you can do this:
for f in *; do if [ -d "$f" -a ! -L "$f" ]; then cd "$f"; echo "$f"; cd ..; fi done
This seems to work:
find . -type d -maxdepth 1 -exec svn up "{}" \;
But it tried to update the current directory, which should be ommited. (althought it works for me because current dir is not a svn directory)

copy a directory structure with file names without content

I have a huge directory structure of movie files. For analysis of that structure I want to copy the entire directory structure, i.e. folders and files however I don't want to copy all the movie files while I want to keep there file names. Ideally I get zero-byte files with the original movie file name.
I tried to and then rsync to my remote machine which didn't fetch the link files.
Any ideas how to do that w/o writing scripts?
You can use find:
find src/ -type d -exec mkdir -p dest/{} \; \
-o -type f -exec touch dest/{} \;
Find directory (-d) under (src/) and create (mkdir -p) them under dest/ or (-o) find files (-f) and touch them under dest/.
This will result in:
dest/src/<file-structre>
You can user mv creatively to resolve this issue.
Other (partial) solution can be achieved with rsync:
rsync -a --filter="-! */" sorce_dir/ target_dir/
The trick here is the --filter=RULE option that excludes (-) everything that is not (!) a directory (*/)
On ubuntu you can try:
cp -r --attributes-only <source_dir> <target_dir>
It doesn't copy file data.
From manpage of cp
--attributes-only
don't copy the file data, just the attributes
Note: I'm not sure this option available for other distributions, if anybody can confirm please update the answer.
I needed an alternative to this to sync only the file structure:
rsync --recursive --times --delete --omit-dir-times --itemize-changes "$src_path/" "$dst_path"
This is how I realized it:
# sync source to destination
while IFS= read -r -d '' src_file; do
dst_file="$dst_path${src_file/$src_path/}"
# new files
if [[ ! -e "$dst_file" ]]; then
if [[ -d "$src_file" ]]; then
mkdir -p "$dst_file"
elif [[ -f $src_file ]]; then
touch -r "$src_file" "$dst_file"
else
echo "Error: $src_file is not a dir or file"
fi
echo -n "+ "
ls -ld "$src_file"
# modification time changed (files only)
elif [[ -f $dst_file ]] && [[ $(date -r "$src_file") != $(date -r "$dst_file") ]]; then
touch -r "$src_file" "$dst_file"
echo -n "+ "
ls -ld "$src_file"
fi
done < <(find "$src_path" -print0)
# delete files in destination if they disappeared in source
while IFS= read -r -d '' dst_file; do
src_file="$src_path${dst_file/$dst_path/}"
# file disappeard on source
if [[ ! -e "$src_file" ]]; then
delinfo=$(ls -ld "$dst_file")
if [[ -d "$dst_file" ]] && rmdir "$dst_file" 2>/dev/null; then
echo -n "- $delinfo"
elif [[ -f $dst_file ]] && rm "$dst_file"; then
echo -n "- $delinfo"
fi
fi
done < <(find "$dst_path" -print0)
As you can see I use echo and ls to display changes.
ls > listOfMovie.txt; You will have the list of your films in a .txt file
.For multiple directories see the man page.

find list of files, make a directory and copy those files in linux

For example, I want to copy the "file-to-be-copied.txt" from different directories
/home/user1/file-to-be-copied.txt
/home/user2/file-to-be-copied.txt
/home/user3/file-to-be-copied.txt
Then create a new directory based on the user account
/home/user4/user1/
/home/user4/user2/
/home/user4/user3/
Then copy the "file-to-be-copied.txt" to the new created directories
/home/user4/user1/file-to-be-copied.txt
/home/user4/user2/file-to-be-copied.txt
/home/user4/user3/file-to-be-copied.txt
All I know is that it should be done using bash scripting but I don't know how. This is as far as I go
find /home . "file-to-be-copied.txt" | xargs -i mkdir ... cp {} ...
No find necessary, and more so: no xargs (which is almost always superfluous with find, since find has -exec).
cd /home
cp --parents user?/file-to-be-copied.txt user4
for f in $(/usr/bin/find '/home' -name 'file-to-be-copied.txt'); do
tmpname=${f%/*}
dirname=${tmpname##*/}
/bin/mkdir -p $dirname && /bin/cp -p $f $dirname
done
This is the code I used.
for f in $(find '/home' -name 'file-to-be-copied.txt')
do
tmpname=${f%/*}
dirname=${tmpname##*/}
mkdir -p /home/user4/$dirname && /bin/cp -p $f /home/user4/$dirname
echo $f copied to /home/user4/$dirname
done

Resources