I'm trying to create a script that will remove a list of applications in OS X. My general thinking:
So the core logic would be
I've been playing around with find -exec, but am open to other approaches.
This is what I have for find -exec that isn't quite working
find /Applications -maxdepth 3 -type f -name Info.plist -exec sh -c "grep '<string>Example</string>' | xargs dirname | xargs echo rm --" 2>/dev/null
I realize
defaults read $dir/Contents/Info CFBundleExecutable
is probably better than grep to extract the package name, but don't think that is why the above isn't working (no output from the test "echo rm" at all, and inserting a tee command to output to a file didn't do anything either).
The above line, if it worked, would go in a loop running for each app to be removed with a list of app names in a variable.
I am totally open to other approaches, especially if there's a more efficient way to do this.
It's saner to keep the logic in your parent shell, rather than shuffling it off to a subprocess:
#!/usr/bin/env bash
# works correctly with names echo doesn't handle -- ones containing spaces, backslashes, etc
log_command() { printf '%q ' "$@" && echo; }
while IFS= read -r -d '' plist; do
if grep -e '<string>Example</string>' "$plist"; then
log_command rm -r -- "${plist%/*}"
fi
done < <(find /Applications -maxdepth 3 -type f -name Info.plist -print0)
Note that <(...)
-- process substitution -- is a bash-only feature, so it isn't guaranteed to work if your script is started with sh
instead of bash
.
This also can be easily modified to use better practices, so it's easy to modify to:
#!/usr/bin/env bash
log_command() { printf '%q ' "$@" && echo; }
while IFS= read -r -d '' plist; do
dir=${plist%/*}
if [[ "$(defaults read "$dir"/Contents/Info CFBundleExecutable)" = example ]]; then
log_command rm -r -- "$dir"
fi
done < <(find /Applications -maxdepth 3 -type f -name Info.plist -print0)
However, if you really want find
to be the parent process, you can do that:
find /Applications -maxdepth 3 -type f -name Info.plist -exec bash -c '
log_command() { printf '%s\n' "$@" && echo; }
for plist do
if grep -e "<string>Example</string>" "$plist"; then
log_command rm -r -- "${plist%/*}"
fi
done
' _ {} +
The use of -exec ... {} +
passes as many results as possible to each copy of bash
. The _
fills in $0
, so the filenames that were found and placed on the command line are put in $1
, $2
, etc; this is what for
loops over when not given an explicit list.
Since you're limiting it to a depth of 3, and that's also practically the minimum depth, and the rest of the path is going to be highly constrained (it must be *.app/Contents/
) you don't really need find
. A simple glob pattern should suffice, and should also be more efficient (since it doesn't have to search eg Contents/Resources
):
for plist in /Applications/*.app/Contents/Info.plist; do
if [ "$(defaults read "${plist%.plist}" CFBundleExecutable)" = Example ]; then
echo rm -R -- "${plist%/Contents/Info.plist}" # remove "echo" to actually do it
fi
done
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.