通过Shell脚本

时间:2019-01-28 18:37:08

标签: bash macos shell

我正在尝试创建一个脚本,该脚本将删除OS X中的应用程序列表。 我的一般想法:

  1. 应用程序只是OS X中的目录
  2. 这些目录的Contents子目录包含一个Info.plist,可用于标识该应用程序。

所以核心逻辑将是

  1. 行走驱动器
  2. 如果当前文件夹有一个名为Contents的子文件夹,其中包含一个名为Info.plist的文件,其中包含某些文本,请删除当前文件夹。

我一直在使用find -exec,但是对其他方法持开放态度。

这是我找到的-exec不能正常工作的

find /Applications -maxdepth 3 -type f -name Info.plist -exec sh -c "grep '<string>Example</string>' | xargs dirname | xargs echo rm --" 2>/dev/null

我意识到

  

默认读取$ dir / Contents / Info CFBundleExecutable

可能比grep更好地提取软件包名称,但是不要认为这就是为什么以上都不起作用的原因(测试“ echo rm”根本没有输出,并插入tee命令以输出到文件也没有执行任何操作)。

上面的行(如果可行)将循环运行,以删除每个应用程序,并在变量中列出应用程序名称。

我完全欢迎其他方法,特别是如果有更有效的方法可以做到这一点。

2 个答案:

答案 0 :(得分:1)

将逻辑保留在父外壳中而不是将其改组到子进程是比较明智​​的选择:

#!/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)

请注意,<(...)-process substitution是仅bash功能,因此,如果脚本以sh而不是{{1 }}。

这也可以很容易地修改以使用更好的做法,因此很容易修改为:

bash

但是,如果您真的希望#!/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) 成为父进程,则可以执行以下操作:

find

使用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 ' _ {} + 会将尽可能多的结果传递给-exec ... {} +的每个副本。 bash填充了_,因此在命令行中找到并放置的文件名被放置在$0$1等中;这就是$2在没有给出明确列表的情况下循环显示的内容。

答案 1 :(得分:0)

由于将深度限制为3,实际上也是最小深度,因此路径的其余部分将受到严格限制(必须为{{1} }),您实际上并不需要beta.39。一个简单的glob模式就足够了,而且也应该更有效(因为它不必搜索*.app/Contents/):

find