Copy nested folders contents to one folder recursively (terminal) - bash

I have a Wordpress upload folder that is structured using subfolders for months.
wolfr2:uploads wolfr$ tree .
.
|-- 2007
| |-- 08
| | |-- beautifulkatamari.jpg
| | |-- beautifulkatamari.thumbnail.jpg
| | |-- beetle.jpg
| | |-- beetle.thumbnail.jpg
How do I use terminal to copy all the images recursively into another folder? I can't seem to wildcard folders like you can wildcard filenames. (e.g. *.jpg or *) (I'm on Mac OSX)
cp -R ./*.jpg .
?

This will copy all *.jpg files from the current folder to a new folder and preserve the directory structure.
tar cvfp `find . -name "*.jpg"` | (cd <newfolder>; tar xfp -)
To copy without preserving the directory structure:
cp `find . -name "*.jpg"` <newfolder>

Off the top of my head:
find . -type f -name \*.jpg -exec cp \{\} $TARGETFOLDER \;
If that doesn't work, comment and I'll try again, but find is definitely the way to go.

None of the above commands worked for me as such on macOS 10.15. Here's the one that worked:
find . -name "*.jpg" -exec cp /{/} [target folder path] \;

Related

Using "find" remove .js files and maintain .min.js files

Having this folder (its a minified example):
js/
- folder/
test.js
test.min.js
- inner folder/
nice.js
nice.min.js
- another_folder/
hello.js
hello.min.js
config.js
config.min.js
I want to get rid of the .js files (and maintain the min.js files). This would be my desired output:
js/
- folder/
test.min.js
- inner folder/
nice.min.js
- another_folder/
hello.min.js
config.min.js
How can I archieve this using find?
find . -iname '*.js' ! -iname '*.min.js'
Output
./js/another_folder/hello.js
./js/folder/inner folder/nice.js
./js/folder/test.js
./config.js
And to remove these files you just pipe them to rm
find . -iname '*.js' ! -iname '*.min.js' -exec rm -f {} +
Out of the blue, untested:
#!/bin/bash
find . -type f -name "*.min.js" |
while read path
do
other="${path%.min.js}.js"
rm -v "$other"
done

Rename certain portion of filepaths in current directory recursively

Let's assume I have following directory tree:
.
|-- foo
`-- foodir
|-- bardir
| |-- bar
| `-- foo
|-- foo -> bardir/foo
`-- foodir
|-- bar
`-- foo
3 directories, 6 files
How can I rename all foo into buz, including symlinks? like:
.
|-- buz
`-- buzdir
|-- bardir
| |-- bar
| `-- buz
|-- buz -> bardir/buz
`-- buzdir
|-- bar
`-- buz
3 directories, 6 files
I thought it is relatively easy at the first glance, but it turns out that was unexpectedly tough.
Firstly, I tried to mv around all files using git ls-files:
$ for file in $(git ls-files '*foo*'); do mv "$file" "${file//foo/buz}"; done
This gave me a bunch of errors said that I have to create new directories before doing so:
mv: cannot move 'foodir/bardir/bar' to 'buzdir/bardir/bar': No such file or directory
mv: cannot move 'foodir/bardir/foo' to 'buzdir/bardir/buz': No such file or directory
mv: cannot move 'foodir/foo' to 'buzdir/buz': No such file or directory
mv: cannot move 'foodir/foodir/bar' to 'buzdir/buzdir/bar': No such file or directory
mv: cannot move 'foodir/foodir/foo' to 'buzdir/buzdir/buz': No such file or directory
I didn't want to care about cleaning up empty directories after copy, so I tried find -exec expecting it can handle file renaming while finding files based on its names.
$ find . -path .git -prune -o -name '*foo*' -exec bash -c 'mv "$0" "${0//foo/buz}"' "{}" \;
But find seems still tried renaming files from renamed path.
find: ./foodir: No such file or directory
My final solution is to find the first file/directory for every single mv commands.
#!/bin/bash
# Rename file paths recursively
while :; do
path=$(find . -path .git -prune -o -name '*foo*' -print -quit)
if [ -z "$path" ]; then
break
fi
if ! mv "$path" "${path/foo/buz}"; then
break
fi
done
# Change symlink targets as well
find . -path .git -prune -o -type l -exec bash -c '
target=$(readlink "$0")
if [ "$target" != "${target//foo/buz}" ]; then
ln -sfn "${target//foo/buz}"
fi
' "{}" \;
This, kinda lame, but works as I expected. So my questions are:
Can I assume find always output directories before its sub directories/files?
Is there any chance to avoid using find multiple times?
Thank you.

Moving all files recursively from subfolders to parent

I have a folder called "Music" which is my parent folder. This folder contains alot of .mp3- and .m4a-files, some in the root of the folder and others in subfolders that can have several subfolders.
How do I use terminal on my Mac to recursively move all files from the subfolder, up into the root of my Music folder and replace existing/duplicate files?
Thanks!
have a good backup ;-) Seriously!
cd to your Music folder
type find . -type f -iname '*.mp3' -mindepth 2 -print0 | xargs -0 -I{} mv -n '{}' .

Creating empty files in new location

I have a directory full of files. The tree looks something like this:
|-- test1a
| |-- test1b
| |-- foo.txt
| |-- bar.txt
|-- test2a
| |-- test2b
Where the directory names match the regular expression test[1-9][ab].
Using find in bash, I'm trying to create blank files in test2b with the same filenames and extensions as those in test1b.
So far, I've tried the following:
find test1a/test1b -type f -exec touch test2a/test2b {} \;
This, however, does not work. I don't have much experience with bash, so I'm not sure where to go from here. Where am I going wrong?
I was able to solve this problem using the following:
$ cd test2a/test2b
$ find ../../test1a/test1b -type f -exec sh -c 'touch $(basename {})' \;
I believe the problem was resulting from {} giving the full path rather than the filename. It was then trying to create a file that already existed, so it left it alone and did nothing.
Here is a second approach:
find test1a/test1b -type f -execdir echo touch test2a/test2b/{} \; > adhoc.sh
sh adhoc.sh

Regex find and copy in bash (preserving folder structure)?

I have a folder with a bunch of log files. Each set of log files is in a folder detailing the time and date that the program was run. Inside these log folders, I've got some video files that I want to extract. All I want is the video files, nothing else. I tried using this command to only copy the video files, but it didn't work because a directory didn't exist.
.rmv is the file extension of the files I want.
$ find . -regex ".*\.rmv" -type f -exec cp '{}' /copy/to/here/'{}'
If I have a folder structure such as:
|--root
|
|--folder1
| |
| |--file.rmv
|
|--folder2
|
|--file2.rmv
How can I get it to copy to copy/to/here with it copying the structure of folder1 and folder2 in the destination directory?
cp has argument --parents
so the shortest way to do what you want is:
find root -name '*.rmv' -type f -exec cp --parents "{}" /copy/to/here \;
I would just use rsync.
The {} represents the full path of the found file, so your cp command evaluate to this sort of thing:
cp /root/folder1/file.rmv /copy/to/here/root/folder1/file.rmv
If you just drop the second {} it will instead be
cp /root/folder1/file.rmv /copy/to/here
the copy-file-to-directory form of cp, which should do the trick.
Also, instead of -regex, yor could just use the -name operand:
find root -name '*.rmv' -type f -exec cp {} /copy/to/here \;
Assuming src is your root and dst is your /copy/to/here
#!/bin/sh
find . -name *.rmv | while read f
do
path=$(dirname "$f" | sed -re 's/src(\/)?/dst\1/')
echo "$f -> $path"
mkdir -p "$path"
cp "$f" "$path"
done
putting this in cp.sh and running ./cp.sh from the directory over root
Output:
./src/folder1/file.rmv -> ./dst/folder1
./src/My File.rmv -> ./dst
./src/folder2/file2.rmv -> ./dst/folder2
EDIT: improved script version (thanks for the comment)

Resources