Bash Shell Script to search for files using mdfind - macos

I have a folder of about 350 images (they are scanned recipes). To make finding them easier, I have written a search program in bash shell script. I have bash version 3.2 on Mac OS X 10.8 Mountain Lion.
The basic idea of my program:
Ask the user for search terms (using osascript).
Search the file (names) for the search terms, using mdfind.
Send the matching files to ln (through xargs).
Make a hardlink of matching files in a "results" directory. This directory is contained within the same folder of images (this directory is cleaned at the beginning of the program).
What the directories look like:
+Recipes
-files
-files
-files
+.search
-search (shell script)
+results
-links
-links
Here's my code:
#!/bin/bash
#
# Search program for recipes.
# version 2
# Clean out results folder
rm -rf ~/Google\ Drive/Recipes/.search/results/*
# Display the search dialog
query=$(osascript <<-EOF
set front_app to (path to frontmost application as Unicode text)
tell application front_app to get text returned of (display dialog "Search for:" default answer "" with title "Recipe Search")
EOF)
# If query is empty (nothing was typed), exit
if [ "$query" = "" ]
then
exit
fi
echo "Searching for \"$query\"."
# Search for query and (hard) link. The output from 'ln -v' is stored in results
# 'ln' complains if you search for the same thing twice... mdfind database lagging?
results=$(mdfind -0 -onlyin ~/Google\ Drive/Recipes/ "$query" | xargs -0 -J % ln -fv % ~/Google\ Drive/Recipes/.search/results)
if [ "$results" ]
then
# Results were found, open the folder
open ~/Google\ Drive/Recipes/.search/results/
else
# No results found, display a dialog
osascript <<-EOF
beep
set front_app to (path to frontmost application as Unicode text)
tell application front_app to display dialog "No results found for \"" & "$query" & "\"." buttons "Close" default button 1 with icon 0
EOF
fi
It works fine—the first time. If you search for the same thing twice, it breaks.
Explanation: let's say I search for "chicken". 34 files match, and hard links are made in the results directory.
Now, I run the program again, and search for the same thing—"chicken". The directory is emptied (by rm). But now, the finding/linking stops working—only 6 or 7 recipes will be linked. What seems to be happening is that mdfind is finding the results in the search directory, after they have been deleted, and then ln can't make links. But it doesn't find the main files...
I get
ln: ~/Google Drive/Recipes/.search/results/recipe.jpg: no such file or directory
I looked at mdfind used for creating symlinks not working as expected; they have a similar problem (but no help).
Thanks for any help... this has been annoying me for a long time.

You can re-index directories or files with mdimport.
$ touch aa
$ mdfind -onlyin . -name aa
/Users/lauri/Desktop/aa
$ rm aa
$ mdimport .
$ mdfind -onlyin . -name aa
$
Spotlight doesn't index directories that start with a period, so you could just rename the .search directory.

The Spotlight commands use a cached index of the file contents. I don't know of any way to force mdfind not to do so. The brute force approach is to clear the cache with mdutil -E after removing the files, but you may not want to do that.

Related

How to get name of folder for Shell Script in Automator?

I'm using the newest version of macOS Monterey.
Sometimes I have to merge/combine all files that are in a specific folder into one txt file.
I currently do that by typing this in Terminal:
cd /Users/my_name/Desktop/test_folder ; cat * >merged.txt
This will merge/combine all files in folder test_folder into one file called merged.txt. The file merged.txt will get saved into the folder test_folder.
Every time I need this I have to open Terminal copy/paste the command and replace test_folder with the right folder name, since it's not always the same.
I want to make this easier by just make a right click on a folder, go to Quick Actions and select e.g. Merge all files to merge/combine all files inside the folder I just clicked on.
But I stuck at getting the folder name. How can I dynamically get the folder name and path I clicked on to start this Quick Action instead of the hard coded /Users/my_name/Desktop/test_folder?
Or, is there another and easier solution?
This is what I have so far:
I wouldn’t do this with AppleScript, especially if all it’s ultimately doing is calling out to a shell script.
Stick with the Run Shell Script action except change the option for passing the input as arguments rather than to stdin.
The folders selected in Finder will then be available to your script via $#, so you can do something like:
for d in "$#"; do
cat "$d"/* > "$d/merged.txt"
open -R "$d/merged.txt"
done 2>/dev/null
This loops through the selected directories and concatenates the files to merged.txt in the respective directory. The open -R line reveals the merged.txt file in Finder.
Errors are written to /dev/null, i.e. discarded, as cat will throw an error if any of the directories, themselves, contain directories.
Instead of adding a Run Shell Script to your workflow, try adding a Run AppleScript command instead. Copy this following AppleScript code to the Run AppleScript command.
on run {input, parameters}
try
do shell script "cd " & quoted form of POSIX path of input & " && cat *.txt > merged.txt"
on error
try
do shell script "cd " & quoted form of POSIX path of input & " && rm merged.txt"
end try
end try
end run

Locate a file anywhere on the hard drive and transfer it to a different folder

I'm trying to create an app to transfer any files with a certain name a different folder. So far, I have the following:
tell application "Finder"
move (every item of (get path to home folder) whose name is "extended image name.jpg") to ((get path to home folder) & "Pictures" as string)
end tell
While this doesn't return any errors, it doesn't do what I want it to either. I am also aware that this only searches the home folder, so if there is any way to do a broader search of the whole drive without having to enter a username, that would be great (I want this to be able to run on more computers than one without them having to edit the script.)
-Thanks
The code considers only files in the home folder but not in its subfolders, to consider all subfolders you have to add entire contents
tell application "Finder"
move (every item of entire contents of home whose name is "extended image name.jpg") to folder "Pictures" of home
end tell
But be aware that entire contents is extremely slow. The shell command find or a spotlight search with mdfind is much faster for example
set homeFolder to POSIX path of (path to home folder)
set picturesFolder to POSIX path of (path to pictures folder)
do shell script "mdfind -onlyin " & quoted form of homeFolder & " -0 'kMDItemDisplayName = \"extended image name.jpg\"' | xargs -0 -J {} mv {} " & quoted form of picturesFolder
Important Note:
As you are moving multiple files with the same name the Finder version will ask for overwriting and the shell version will overwrite all files with the same name.
When you use the Finder to search, if your folder contains only few hundreds files that's OK. But if you want to search in a folder containing thousands of files, Finder will take too long. In this case, it is better to use the shell command 'find' which is much faster.
Syntax of find command is : find directory/ -name target_file_name
Even more, you can chain that command with -exec fonction which will used result of the find to do something: in your case to copy files found in Pictures folder.
In -exec command the {} means the file found. The shell copy command is cp.
This ends with : find /Users/myUserName/ -name 'extended image name.jpg' -exec cp {} \;
(note: the \; tells the system that's the end of the -exec command)
Overall you can run this command in Applescript via do shell script :
set Source to POSIX path of (path to home folder)
set Dest to POSIX path of (path to pictures folder)
set TargetName to "extended image name.jpg"
set BackSlash to ASCII character 92
set SemiCol to ASCII character 59
try
do shell script "find " & Source & " -name " & quoted form of TargetName & " -exec cp {} " & Dest & " " & BackSlash & SemiCol
end try
It is much longer than Finder syntax, but also much faster to run !
Notes:
1) the POSIX path convert the Finder path with : to shell path with /
2) the Backslash and SemiCol are set to \ and ; . this is a work around to avoid that Applescript compiler misinterpret the \ during compilation
3) the do shell script is in try/end try block to avoid error. "Find" gives errors when you try to access to files without permission. Such errors will be ignored.
4) with this method, you can replace Source by "/". Doing so, the find will search in all directories of the main drive (it may take some time !). If you want to search for all users, set Source to "/Users/"

(AppleScript) Remove files from subfolders with shell script

I have a script that moves files to Trash, but I work with tens of thousands of files so I need it to do fast. Current script gets Finder stuck and I need to relaunch Finder every time I use the script.
set source_folder to POSIX path of (path to pictures folder) & "4K Stogram"
do shell script "/usr/bin/find " & quoted form of source_folder & " -type f -exec /bin/mv {} ~/.Trash \\;"
display notification "All images were processed." with title "New" sound name "Glass.aiff"
tell me to quit
Wouldn't the shell script's 'rm -f' command make it faster?
How the script should look like if yes?
I don't need those files in Trash anyway, I delete them right after the script finishes the job.
Add the -delete option to the find command.
Like this:
set source_folder to POSIX path of (path to pictures folder) & "4K Stogram"
do shell script "/usr/bin/find " & (quoted form of source_folder) & " -type f -delete"
You should be able to pipe your find results to the rm command. Understand that although rm should be faster, it is permanent. Be sure that your code is correct before invoking it.

How can I select files from list on Mac-OS

I've got a bunch of .cr2 and .jpg files with exact same names. ".cr2" is a file format for digital negative, every .jpg is a converted to 8bit version of cr2-photo. Every photographer with canon camera get this kind of structure: take one picture - get one .cr2 version and one .jpg version. Cr2-s weights a lot, thats why I send jpegs to my clients to filter. After they send back best hundred photos from thousands, I must pick cr2 versions of this photos, one by one. There is gotta be a better way. In windows I find a way to select this files in totalcommander through copy selection to buffer - modifing(replace in text editor every ".jpg" to ".cr2") and then restore selection from buffer. This problem is seemed dumn, but I can't find an answer to mac, it took forever to select this files one by one and all photographers I know doing the same, which is driving me crazy.
Here is a bash script that should do what you want simply by double-clicking it. First you need to edit the first 2 lines to tell it 1) where the CR2 files can be found and 2) where the JPEGs are that the client has accepted.
Initially, I am assuming the CR2 files are somewhere under your login/home directory and that the JPEGs accepted by the client are on your Desktop in a folder called accepted. Edit lines 3 and 4 to reflect the actual situation, then save the following script on your Desktop as GetCR2s.command.
#!/bin/bash
#
# You can edit the next two lines to match your setup
CR2LOCATION="$HOME"
ACCEPTEDLOC="$HOME/Desktop/accepted"
shopt -s nocasematch # Match JPG and jpg and CR2 and cr2 regardless of case
shopt -s nullglob # Don't die if there are no files
# Tell user what is going on...
echo Locating CR2s in $CR2LOCATION, to match accepted files in $ACCEPTEDLOC...
# Go where the JPEGs are, or give up
cd "$ACCEPTEDLOC" 2> /dev/null
if [ $? -ne 0 ]; then echo ERROR: Unable to change directory to $ACCEPTEDLOC; exit 1; fi
# Consider all JPEGs in this directory
for f in *.jpg; do
cr2="${f%%jpg}cr2" # Replace "jpg" extension with "cr2"
echo Processing \"$f\", looking for \"$cr2\"...
find "$CR2LOCATION" -iname "$cr2" -not -path "$ACCEPTEDLOC" -exec cp "{}" . \; 2> /dev/null
done
Then, start the Terminal application, and do the following just one time before use in order to make the script executable:
cd Desktop
chmod +x GetCR2s.command
Now you should be able to save the images your client has accepted (by clicking Save Attachments in your Email program) into the Desktop/accepted folder, then simply double-click on the script GetCR2s.command on your Desktop and it will gather all the matching CR2 files into that folder for you.

Add text at the end of document fragments on a Mac

On a Mac, I have a directory of html files that are all document fragments. Using the TexFinderX app, I was easily able to do a find/replace and add everything at the top of the documents that I wanted (i.e. etc.) .
Now I need to find a way to add the closing tags to all of the documents (i.e. ). TexFinderX does not have a way to do this since the documents do not have anything in common at the end of the files.
Is there a Terminal command that can do this for all html files in a directory and it's subdirectories?
Thanks,
Linda
EDIT:
Well i was trying to keep it simple and avoid Bash scripting but it seems find doesnt allow for output redirection... so try this instead:
for f in ~/html/*.html; do echo "Processing $f file.." && cat ~/close.html >> $f; done
Put your closing tags in a file... well call it close.html and we'll jsut put it in your home directory /Users/youruser/close.html. Well assume your docs are in /Users/youruser/html
Open Terminal.app and do the following command:
find ~/html -type f -name "*.html" -exec cat ~/close.html >> {} \;
youll want to test that first... my find kung-fu is rusty

Resources