如何过滤传给xargs的文件中带shebang的Swift文件
我尝试编写一个简单脚本,编译指定文件夹及其所有子文件夹内的所有.swift文件,但需要排除所有开头带shebang的文件。
我计划开发一个简易工具,无需依赖高级IDE或项目配置文件,即可将多个Swift文件编译为单个输出产物。项目以找到main.swift文件的位置为根目录,目录结构本身即为项目的定义依据。
假设我已经定位到main.swift并进入对应目录,首先使用find命令查找所有相关Swift文件,命令如下:
find * -type f -name "*.swift"
追加-print0参数后将结果通过管道传给xargs -0,即可一次性将所有文件传给Swift编译器,命令如下:
find * -type f -name "*.swift" -print0 | xargs -0 xcrun -sdk macosx swiftc -o "$OUTPUT_FILE"
注意:由于find命令使用了
-print0参数,xargs侧使用-0参数匹配,所有n个参数会一次性传给单次执行的xcrun,而非分n次执行xcrun每次传1个参数,整个过程仅执行一次编译,生成单个输出文件。
目前遇到的问题是:如果查找到的Swift文件是Swift脚本文件(即开头带shebang的文件),编译器会拒绝编译。这类文件通常还包含顶层语句,除非是main.swift,否则编译器同样不支持该写法。
举例来说,如果名为someutil的文件内容如下:
#! /usr/bin/env swift print("Hello from Swift!")
执行chmod +x someutil为文件添加可执行权限后,只要该文件在PATH路径下,就可以像其他Shell脚本一样直接执行:
echo Here we go! someutil echo Done!
执行输出为:
Here we go! Hello from Swift Done!
但如果将该文件传给编译器,编译器会同时抛出两类错误:一是文件包含shebang,二是非main.swift文件包含顶层语句。
因此我需要过滤掉所有开头带shebang的文件,将该特征作为需要排除的Swift脚本的识别标识。
我目前查到的grep教程大多只说明如何筛选包含指定内容的文件,没有找到排除这类文件的方案,尤其现有代码逻辑依赖find命令的-print0参数,无法很方便地在其他环节做过滤。
目前收到的部分回答看似可行,但实际无法正确处理参数。为验证方案正确性,我推荐先编写如下名为'argchecker'的工具:创建包含以下内容的文件,执行chmod +x添加可执行权限后加入系统PATH路径。测试时将你认为可行的方案中原本传给xcrun编译器的内容替换为传给argchecker即可,很多错误方案最终不会传入n个独立参数,而是将所有参数拼接为单个参数传入。
'argchecker'文件内容
#! /usr/bin/env bash echo "There are $# arguments:" for ((i=1; i<=$#; i++)) do echo " $i: ${!i}" done
之后执行如下命令:
chmod +x argchecker
将其加入PATH路径后,在添加过滤逻辑的位置使用如下命令测试脚本即可:
find * -type f -name "*.swift" -print0 | xargs -0 argchecker
全程保留null分隔符传递逻辑,不会破坏参数结构,可直接通过argchecker验证,提供两种实现方式:
方案1:纯find内置逻辑实现(兼容性最好)
不需要依赖grep的特殊参数,直接在find遍历阶段完成过滤:
find * -type f -name "*.swift" -exec sh -c ' for file do IFS= read -r first_line < "$file" case "$first_line" in "#!"*) continue ;; *) printf "%s\0" "$file" ;; esac done ' sh {} + | xargs -0 xcrun -sdk macosx swiftc -o "$OUTPUT_FILE"
- 全程使用null字符分隔文件名,所有文件会作为独立参数传入编译器,不会出现参数拼接错误
- 仅读取每个文件的第一行做判断,性能开销极低
- 自动兼容带空格、特殊字符、甚至换行符的异常文件名
- 无额外依赖,macOS系统自带工具即可直接运行
方案2:grep简洁实现(代码更短)
利用grep的反向匹配参数实现过滤,代码更精简:
find * -type f -name "*.swift" -print0 | xargs -0 grep -L -m1 "^#!" -Z | xargs -0 xcrun -sdk macosx swiftc -o "$OUTPUT_FILE"
参数说明:
-m1:匹配到第一行就停止读取文件,不需要遍历整个文件内容-L:输出不包含匹配规则的文件名,刚好实现排除shebang文件的需求-Z:输出文件名时使用null字符分隔,和前后的-print0、xargs -0逻辑完全兼容,不会破坏参数传递
内容的提问来源于stack exchange,提问作者Mark A. Donohoe

