Iterate over folders and move files up one level - macos

I have a directory structure that looks like this:
/images
/1
/.tmp
image1.jpg
image2.jpg
/2
.tmp
image1.jpg
image2.jpg
image3.jpg
/3
.tmp
image1.jpg
image2.jpg
What I need is to move all of those files in .tmp up one level, so their paths are images/1/image1.jpg rather than images/1/.tmp/image1.jpg. The issue is that I have hundreds or thousands of these numbered folders, so doing it by hand would take forever.
Is there an OS X or Unix shell command that I could iterate over each /.tmp folder and move the contents up a level, or something like:
mv images/*/.tmp/* images/< the current dir being iterated over>/*

If your find supports the -execdir command (which OSX's find apparently does), then you could do:
find . -iname '*.jpg' -execdir mv {} .. \;
-execdir runs the command from the directory where the file was found, so .. will refer to that file's directory's parent directory.
You can refine this to force matching a .tmp directory:
find . -path '*/.tmp/*.jpg' -execdir mv {} .. \;
However, * in -path also matches /, so this will also match, for example, images/.tmp/foo/image.jpg.
You could do:
find . -type d -name .tmp -print0 | xargs -0I_ find _ -maxdepth 1 -name '*.jpg' -execdir mv {} .. \;
Or, using find's regex support:
find . -regex '.*/\.tmp/[^/]*\.jpg' -execdir mv {} .. \;

From scratch without testing:
cd /images
for i in $(find ./ -name *.jpg)
do
d = $(dirname $i)
d = $(dirname $d)
echo $i $d
# test! before use the next
# mv $i $d
done

Related

Mac terminal how can i search in all folders and subfolders for image

How can i search for example all .png files on an external disk and copy them to another directory?
Have tried to use the cp command. Have try it but don't work for me
?
Monterey 2.2.1
cp /Volumes/Data *.png /Volumes/Data/pictures_png
cp command won't work if you need to recursively copy from the sub directories. You need to use find.
Syntax:
find $SOURCE -type f -name '*.type' -exec cp '{}' $DESTINATION ';'
In your case,
find /Volumes/Data -type f -name '*.png' -exec cp '{}' /Volumes/Data/pictures_png ';'
Here is how it works:
-type f means copy only files not directories.
-name is to provide the filename to find. Here *.png for pattern matching
-exec executes the following line for each result the above find returns.
{} will be replaced with the results from find
; terminates -exec command

Bash script to move all png files in folder and its subfolders to another directory?

In ~/Desktop/a/ , I have .png files, and there are also subfolders within this that also have .png files.
I'd like to move all of those .png files to another folder.
This is my code so far. It runs, but nothing is placed into the target folder. What is the problem?
#!/bin/bash
cd ~/Desktop/a/
for f in $(find . -type f -name "*.png")
do
mv $f ~/Desktop/new/
done
I guess that these image filenames maybe include spaces or other special characters.
find ~/Desktop/a/ -type f -name "*.png" -exec mv "{}" ~/Desktop/new/ \;
or
find ~/Desktop/a/ -type f -name "*.png" -print0 | xargs -0 -I{} mv "{}" ~/Desktop/new/
If your bash is new enough, you can also use globstar:
cd ~/Desktop/a || exit 1
shopt -s globstar
mv -- **/*.png ~/Desktop/new
Or (if there are too many files to fit in a single command line):
shopt -s globstar
for f in ~/Desktop/a/**/*.png; do
mv -- "$f" ~/Desktop/new
done

"dir/*: No such file or directory" with find -exec ... "{}/*"

The current directory contains files and directories. The directories have no sub-directories, but may contain zero or more files, for example:
./file1
./file2
./directory1/file3
./directory2/file4
./directory2/file5
./directory3/
When I execute find . -type d -maxdepth 1 I get a listing of the directories:
./directory1
./directory2
If I execute mv ./directory1/* . all files in directory1 are moved to the current level . so I thought I could use find -exec to do everything in one go:
find . -type d -maxdepth 1 -exec mv "{}/*" . \;
But I get this response:
mv: rename ./directory1/* to ./*: No such file or directory
How can I move all the files in subdirectories to the current level?
Globbing (replacing foo/* with foo/dirA, foo/dirB, etc) is performed by the shell, not by mv. find -exec doesn't start a shell unless you do so manually; for example:
find . -type d -mindepth 1 -maxdepth 1 \
-exec sh -c 'for dir; do mv -- "$dir"/* .; done' _ {} +
There's no real need to use find. You can do it with a single mv to move the files and rmdir to remove the now-empty directories.
mv */* .
rmdir */

Deleting files older than 10 days in wildcard directory loop

I would like to delete old files from multiple directories but there is a wild card for one of the path attributes. So I'm trying to loop through each of those directories without specifying each one. I think I'm almost there but I'm not sure how to cd into the specific directory to delete the relevant files.
#! /bin/bash
DELETE_SEARCH_DIR=/apps/super/userprojects/elasticsearch/v131/node*/elasticsearch-1.3.1/logs/
for entry in `ls $DELETE_SEARCH_DIR`; do
find $path -name "*super*" -type f -mtime +10 -print -delete
#find . -type f -name $entry -exec rm -f {} \;
done
Any ideas on how to get into the specific directory and apply the delete?
find can search in multiple directories. You can do it like this:
DELETE_SEARCH_DIR=/apps/super/userprojects/elasticsearch/v131/node*/elasticsearch-1.3.1/logs
find $DELETE_SEARCH_DIR -type f -name '*super*' -mtime +10 -print -delete

A script that iterates over all files in folder

There is a script on a server that I need to run over all the files in a folder. To run this script over one file I use this shell script:
for input in /home/arashsa/duo-bokmaal/Bokmaal/DUO_BM_28042.txt ; do
name=$(basename "$input")
/corpora/bokm/tools/The-Oslo-Bergen-Tagger/./tag-lbk.sh "$input" > "/home/arashsa/duo-bokmaal-obt/$name"
done
I'm terrible at writing shell scripts, and have not managed to found out how to iterate over files. What I want it is to make the script iterate over all files in a given folder that end with .txt and not those that end with _metadata.txt. So I'm thinking I would give it the folder path as argument, make it iterate over all the files in that folder, and run script on files ending with .txt and not _metadata.txt
Use find and the exec option.
$ find /path/to/dir -exec <command here> \;
Each file or directory can be obtained by using {}.
Example usage: $ find . -exec echo {} \;, this will echo each file name recursively or directory name in the current directory. You can use some other options to further specify the desired files and directories you wish to handle. I will briefly explain some of them. Note that the echo is redundant because the output of find will automatically print but I'll leave it there to illustrate the working of exec. This being said, following commands yield the same result: $ find . -exec echo {} \; and $ find .
maxdepth and mindepth
Specifying the maxdepth and mindepth allows you to go as deep down the directory structure as you like. Maxdepth determines how many times find will enter a directory and mindepth determines how many times a directory should be entered before selecting a file or dir.
Example usages:
(1) listing only elements from this dir, including . (= current dir).
(2) listing only elements from current dir excluding .
(3) listing elements from root dir and all dirs in this dir
(1)$ find . -maxdepth 1 -exec echo {} \;
(2)$ find . -mindepth 1 -maxdepth 1 -exec echo {} \;
# or, alternatively
(2)$ find . ! -path . -maxdepth 1 -exec echo {} \;
(3)$ find / -maxdepth 2 -exec echo {} \;
type
Specifying a type option allows you to filter files or directories only, example usage:
(1) list all files in this dir
(2) call shell script funtion func on every directory in the root dir.
(1)$ find . -maxdepth 1 -type f -exec echo {} \;
(2)$ find / -maxdepth 1 -type d -exec func {} \;
name & regex
The name option allows you to search for specific filenames, you can also look for files and dirs using a regex format.
Example usage: find all movies in a certain directory
$ find /path/to/dir -maxdepth 1 -regextype sed -regex ".*\.\(avi\|mp4\|mkv\)"
size
Another filter is the file size, any file or dir greater than this value will be returned. Example usage:
(1) find all empty files in current dir.
(2) find all non empty files in current dir.
(1)$ find . -maxdepth 1 -type f -size 0
(2)$ find . -maxdepth 1 -type f ! -size 0
Further examples
Move all files of this dir to a directory tmp present in .
$ find . -type f -maxdepth 1 -exec mv {} tmp \;
Convert all mkv files to mp4 files in a dir /path/to/dir and child directories
$ find /path/to/dir -maxdepth 2 -regextype sed -regex ".*\.mkv" -exec ffmpeg -i {} -o {}.mp4 \;
Convert all your jpeg files to png (don't do this, it will take very long to both find them and convert them).
$ find ~ -maxdepth 420 -regextype sed -regex '.*\.jpeg' -exec mogrify -format png {} \;
Note
The find command is a strong tool and it can prove to be fruitful to pipe the output to xargs. It's important to note that this method is superior to the following construction:
for file in $(ls)
do
some commands
done,
as the latter will handle files and directories containing spaces the wrong way.
In bash:
shopt -s extglob
for input in /dir/goes/here/*!(_metadata).txt
do
...
done

Resources