Linux: How to move files with same name, diff ext. into their own folder? - linux

I have files like this
This list is a sample of my files note the actual files are not in sucessive order.
file1.a
file2.a
file1.b
file2.b
...
and some have a .c extension but not all
How would I move these files into their own named folder.
I have tried this
find . -type f -print0 | xargs -0 -l sh -c 'mkdir "${1%.*}" && mv "$1" "${1%.*}"' sh
but it doesn't work as intended i.e. Well it creates the folders but wont put the second file of same name different extension in the same folder.
mkdir: cannot create directory ‘./file1’: File exists
mkdir: cannot create directory ‘./file2’: File exists
mkdir: cannot create directory ‘./file3’: File exists

You should use mkdir -p, it won't complain (and break the &&) if directories exist (it will also create parent directories if those don't exist).

for FILE in $(ls file[0-9].[a-z])
do
DIRNAME=$(echo $FILE |cut -c1-5)
[ -d $DIRNAME ] || mkdir $DIRNAME
mv ${FILE}* $DIRNAME
done
This will give you:
$ ls file1 file2
file1:
file1.a file1.b file1.c
file2:
file2.a file2.b file2.c

Related

Copy files from multiple folders with including folder name in Linux

I have multiple sub folders e.g.:
ls ./
F1 F2 F5 F8 F12 ...
Each folder contain file "file.txt"
How to copy all file.txt files to main folder containing folder name?
cp ./F1/file.txt ./file_1.txt
cp ./F2/file.txt ./file_2.txt
...
Perl One Liner
first go to main folder than:
find . | perl -a -F/ -lne 'qx(cp -r "$F[1]" T/ )'
note
do not worry about log file on the screen if would be!
T/
is your target directory
main folder
Where all your file exist. If your all file is in the folder Music for example; so cd Music then that Perl One Liner
declare -a dirs
i=1
for d in */
do
dirs[i++]="${d%/}"
done
echo "There are ${#dirs[#]} dirs in the current path"
for((i=1;i<=${#dirs[#]};i++))
do
echo "Copying file.txt from ${dirs[i]} dir..."
cp ./${dirs[i]}/file.txt ./file_$i.txt
done
Save it as a script file, fileTxtCopy.sh, for instance. Then place it at the parent dir and give it executable permission sudo chmod +x fileTxtCopy.sh.
Run it as script and you should have all your file.txt file copied in parent dir.
Copies file.txt files from each folder inside a current directory to the current directory and appends numbers contained in a folder name to the name of the copied file.
for i in *; do a=$(<<< "$i" grep -o "[0-9]*" -); cp "$i/file.txt" "file_$a.txt"; done
Not the most robust approach though.

Command for moving subfolders with files, with keeping the original structure

I have a parent/ folder with a couple of subfolders in it. Structure:
/parent/
/subfolder_1/
- file_1.txt
- file_2.txt
/subfolder_2/
- file_3.txt
- file_4.txt
Now, I need to recursively move the contents of parent/ folder to the empty parent_tmp/ directory. Thing is, I need to keep the original folder structure in parent/.
Expected outcome after moving:
/parent/
/subfolder_1/
(empty)
/subfolder_2/
(empty)
/parent_tmp/
/subfolder_1/
- file_1.txt
- file_2.txt
/subfolder_2/
- file_3.txt
- file_4.txt
Normally, I would simply do
mv parent/* parent_tmp
but this will, of course, move the subfolders permanently.
Is there a way to adjust the mv command to keep the original structure of the source directory?
Note:
I realize that I can e.g. copy parent/ to parent_tmp, and then remove the files in parent/ subfolders. This is plan B to me.
You can use find from parent of parent and parent_tmp directoroies:
find parent -type f -exec bash -c 'mkdir -p "parent_tmp/${1%/*}" &&
mv "$1" "parent_tmp/${1%/*}"' - {} \;
You could copy the files
cp -r parent/* parent_tmp/
or create hard links (should be a lot faster for big files)
cp -l -r parent/* parent_tmp/
and then delete the original files
find parent -type f -delete
while keeping the directory structure.
Zip the content of the parent folder and Unzip it in the target folder.
Quick and Dirty:
I don't think you'll find a tool or option in the mv command to do what you want, but you should be able to achieve the desired goal by using find:
cd parent && while read file ; do dirname="$(dirname "$file")" ; mkdir -p ../parent_tmp/"$dirname"/; mv "$file" "../parent_tmp/"${file#}"" ; done < <( find . -type f ) && cd -
Function
If you use this a lot then you can add the above to your ~/.basrc like so (append to the end of the file):
alias mvkp=moveandkeep
moveandkeep() {
cd "$1"
while read file ;
do dirname="$(dirname "$file")" ;
mkdir -p "$2"/"${dirname#}";
mv "$file" ""$2"/"${file#}"";
done < <(find . -type f)
cd -
}
Now you could simply do the following: (Full path to directories required)
mvkp /home/user/parent /home/user/parent_tmp

copy entire directory excluding a file

As we know, cp -r source_dir intended_new_directory creates a copy of source directory with a new name. Now I want to do the same but want to exclude a particular file. I have found some related answers here, using tar and rsync, but in those solutions I need to create the destination directory first (using mkdir).
I honestly searched a lot, but didn't find exactly what I want.
So far the best I got is this:
tar -c --exclude=\*.dll --exclude=\*.exe sourceDir | tar -x -C destDir
(from http://www.linuxquestions.org/questions/programming-9/how-to-copy-an-entire-directory-structure-except-certain-files-385321/)
If you have binutils, you could use find to filter next cpio to copy (and create directories) :
find <sourceDir> \( ! -name *.dll \) -a \( ! -name *.exe \) | cpio -dumpv <destDir>
Try this by excluding the file using 'grep -v' ->
cp `ls | grep -v <exclude-file>` <dest-dir>
If the directory is not very large I used to write something like this:
src=path/to/source/directory
dst=path/to/destination/directory
find $src -type f | while read f ; do mkdir -p "$dst/`dirname $f`"; cp "$f" "$dst/$f" ; done
Here we list all regular files in $src, iterate over this list and for each file make a directory in $dst if it does not exist yet (-p option of mkdir), then copy the file to that directory.
The above command will copy all the files. Finally, just use
find $src -type f | grep -v whatever | while ...... # same as above
to filter out the files you don't need (e.g. \.bak$, \.orig$, or whatever files you don't want to copy).
Move all exclude file into home or other directory,copy the directory containing all remaining files to the destination folder then restore all exclude files.
#cd mydirectory
#mv exclude1 exclude2 /home/
#cp mydirectory destination_folder/
#cd /home/
#mv eclude1 exclude2 mydirectory/

Rename files in multiple directories to the name of the directory

I have something like this:
v_1/file.txt
v_2/file.txt
v_3/file.txt
...
and I want to rename those files to something like this:
v_1.txt
v_2.txt
v_3.txt
...
in the same directory.
I guess I can use rename but I can't figure out how to use it with folder and file renaming at the same time.
The result can be achieved with a bash for loop and mv:
for subdir in *; do mv $subdir/file.txt $subdir.txt; done;
Note that the solution above will not work if the directory name contains spaces. Related link.
Another solution based on comments (that works for directories having spaces in the name as well):
find . -type d -not -empty -exec echo mv \{\}/file.txt \{\}.txt \;
You can use rnm. The command would be:
rnm -fo -dp -1 -ns '/pd0/.txt' -ss '\.txt$' /path/to/the/directory
-fo implies file only mode.
-dp directory depth. -1 makes it recursive to all subdirectories.
-ns implies name string i.e the new name of the file.
/pd0/ is the immediate parent directory of the file which is subject to rename operation.
-ss is a search string (regex). '\.txt$' regex searches for file with .txt at the end of the filename.
/path/to/the/directory this is the path where the v_1, v_2 ... directories reside. You can pass the directories ( v_1, v_2 ...) too in place of the parent directory path. For example:
#from inside the parent directory
rnm -fo -dp -1 -ns '/pd0/.txt' -ss '\.txt$' v_*
Seem pretty straightforward to me:
$ mkdir /tmp/sandbox
$ cd /tmp/sandbox
$ mkdir v_{1,2,3}
$ touch v_{1,2,3}/file.txt
$ rename -v 's#/file##' v_{1,2,3}/file.txt
rename v_1/file.txt v_1.txt
rename v_2/file.txt v_2.txt
rename v_3/file.txt v_3.txt
$ ls -F
v_1/ v_1.txt v_2/ v_2.txt v_3/ v_3.txt

Shell Script for renaming and relocating the files

I am working on something and need to solve the following. I am giving a analogous version of mine problem.
Say we have a music directory, in which there are 200 directories corresponding to different movies. In each movie directory there are some music files.
Now, say a file music.mp3 is in folder movie.mp3 . I want to make a shell script such that it renames the file to movie_music.mp3 and put it in some folder that I mention to it. Basically, all the files in the subdirectories are to be renamed and to be put in a new directory.
Any workaround for this?
This script receives two arguments: the source folder and the destination folder. It will move every file under any directory under the source directory to the new directory with the new filename:
#!/bin.sh
echo "Moving from $1 to $2"
for dir in "$1"/*; do
if [ -d "$dir" ]; then
for file in "$dir"/*; do
if [ -f "$file" ]; then
echo "${file} -> $2/`basename "$dir"`_`basename "${file}"`"
mv "${file}" "$2"/`basename "$dir"`_`basename "${file}"`
fi
done
fi
done
Here is a sample:
bash move.sh dir dir2
Moving from dir to dir2
dir/d1/f1 -> dir2/d1_f1
dir/d1/f2 -> dir2/d1_f2
dir/d2/f1 -> dir2/d2_f1
dir/d2/f2 -> dir2/d2_f2
Bash:
newdir=path/to/new_directory;
find . -type d |while read d; do
find "$d" -type f -maxdepth 1 |while read f; do
movie="$(basename "$d" |sed 's/\(\..*\)\?//')"
mv "$f" "$newdir/$movie_$(basename $f)";
done;
done
Assuming the following directory tree:
./movie1:
movie1.mp3
./movie2:
movie2.mp3
The following one-liner will create 'mv' commands you can use:
find ./ | grep "movie.*/" | awk '{print "mv "$1" "$1}' | sed 's/\(.*\)\//\1_/'
EDIT:
If your directory structure contains only the relevant directories, you can expand use the following grep instead:
grep "\/.*\/.*"
Notice it looks file anything with at least one directory and one file. If you have multiple inner directories, it won't be good enough.

Resources