I have main DIR X and inside this I have many sub directories like A,B,C,D. I have main DIR Y and inside this i have many sub directories with same name as X main directory sub directories like A,B,C,D.
Now I need to MOVE only files from X main dir sub directories to Y main directory sub directory.
Eg:
Inside main directory X ,sub directory A has 100 files
and B has 1 file
and C has 5 files
and D has 50 files...
my cursor should be at main DIR X , from there I need to MOVE all the sub directory files to same named sub directories(A,B,C,D) which is there inside Y main directory.
how can I do this in SHELL SCRIPTING(KSH) or unix??
Note: Fundamentally revised to come up with a simpler solution. One-liners (more complex) at the bottom.
A POSIX-compliant solution:
#!/bin/sh
# Specify source and target directories (example values)
dirFrom='/tmp/from'
dirTo='/tmp/to'
# Use globbing to find all subdirectories - note the trailing '/'
# to ensure that only directories match.
for subdir in "$dirFrom"/*/; do
# Extract the mere name.
# Note that using ${subdir##*/} is NOT an option here, because $subdir ends in '/'.
name=$(basename -- "$subdir")
# Make sure a subdirectory of the same
# name exists in the target dir.
mkdir -p "$dirTo/$name"
# Move all items in the source subdir. to the analogous target subdir.
mv "$subdir"* "$dirTo/$name/"
done
The above will therefore work with ksh
as well, and also other POSIX-compatible shells such as bash
and zsh
.
There are two potential problems with this solution:
- Hidden items (those whose name starts with a
.
) are ignored.
- The script will break, if there are no subdirs. or if any of them is empty, because a globbing pattern such as
*
that does not match anything is left as-is, resulting in non-existent paths being passed to mv
.
More robust Bash version
Bash - unlike ksh
, unfortunately - has options that address both issues:
shopt -s dotglob
causes hidden items to be included when globbing is performed.
shopt -s nullglob
causes patterns that don't match anything to expand to the empty string.
#!/usr/bin/env bash
# Specify source and target directories (example values)
dirFrom='/tmp/from'
dirTo='/tmp/to'
shopt -s dotglob # include hidden items when matching `*`
shopt -s nullglob # expand patterns that don't match anything to the emtpy string
# Use globbing to find all subdirectories - note the trailing '/'
# to ensure that only directories match.
for subdir in "$dirFrom"/*/; do
# Extract the mere name.
# Note that using ${subdir##*/} is NOT an option here, because $subdir ends in '/'.
name=$(basename -- "$subdir")
# Make sure a subdirectory of the same
# name exists in the target dir.
mkdir -p "$dirTo/$name"
# Collect the paths of the items in the subdir. in
# an array, so we can test up front whether anything matched.
itms=( "$subdir"* )
# Move all items in the source subdir. to the analogous target subdir,
# but only if the subdir. contains at least 1 item.
[[ ${#itms[@]} -gt 0 ]] && mv "${itms[@]}" "$dirTo/$name/"
done
- Note how
shopt -s nullglob
by itself was not enough - we still had to collect globbing matches in an array first, so we could determine if anything matched.
- While we could instead just use
2>/dev/null
to let mv
fail silently if there are no matches, this is not advisable, because it could mask true error conditions.
If you're interested in one-liners, here are find
-based commands; they are, however, quite complex.
# Note: Both solutions below ignore symlinks to directories as subdirectories.
# [POSIX-compliant] Excluding hidden items.
# Note how `\! -name '.*'` is explicitly added to the `find` command to ensure that no hidden subdirs. are matched, so as to
# to match the shell globbing behavior of excluding hidden items.
find "$dirFrom" -mindepth 1 -maxdepth 1 -type d \! -name '.*' -exec sh -c \
'f=$1 t=$2/${1##*/}; set -- "$f"/*; [ -e "$1" ] && mkdir -p "$t" && mv "$@" "$t"' \
- {} "$dirTo" \;
# [Bash] Including hidden items.
find "$dirFrom" -mindepth 1 -maxdepth 1 -type d -exec bash -O dotglob -c \
'f=$1 t=$2/${1##*/}; set -- "$f"/*; [[ -e $1 ]] && mkdir -p "$t" && mv "$@" "$t"' \
- {} "$dirTo" \;
Here is a shell script which should work. I have not had time to check it though. Please check and confirm.
#!/bin/bash
##################################################################
#
# Please make sure this script is run from source
# directory. Also you need to have read, write and
# execute permission for this directory.
#
# If the subdirectories with same names are not present
# then those will be created directly.
##################################################################
# Replace name of source directory by "X"
sourcedir="X"
# Replace name of destination directory with complete path by "Y"
destdir="/home/user/Y"
# Copying names of all subdirectories in a temporary text file
ls -Rl | grep "[a-z]:" | sed 's/://' | cut -c 2- > temp.txt
# Moving files directory by directory to destination
while read line; do
mv $sourcedir$line/* $destdir$line/
echo "All files from directory $sourcedir$line moved to $destdir$line"
done < temp.txt
rm temp.txt
You can use script as below:
#!/bin/bash
dirX=X
dirY=Y
for subdir in `ls $dirX`; do
mv $dirX$subdir/* $dirY$subdir
done
Replace X, Y with your directory