function help_message() {
cat <<EOF
Description: 根据GT的csv文件在Android上测试SDK,并保存日志
1. 关于sdk,默认上传到/data/local/tmp目录下。若已经存在,则直接使用。
2. 关于设备,存在多个在线设备时,必须指定一个设备序列号。
3. 关于并发数,下载、推送文件、执行test_×××等都会并发。
4. 关于日志,默认保存在当前目录下,目录名称包含GT文件名。
5. 关于初始行号,会从初始行号开始执行测试到最后一行。
6. 关于测试数据,优先使用当前目录下同路径的文件,如果没有则会从服务器上下载。
7. 关于推送到设备上的测试数据,每次执行完成后都会删除。
Usage: bash $(basename "$0") -g <gt_file> -s <sdk> [options...]
Options:
-g <gt_file> # GT的csv文件。必需
-s <sdk> # 本地sdk目录。必需
-d <device_serial> # 设备序列号。非必需。有多个在线设备时必需指定
-c <concurrent_num> # 并发数。非必需。默认为1
-l <log_path> # 日志保存路径。非必需。默认当前目录
-i <init_line> # GT文件初始行号。非必需。默认为2,跳过第一行表头
-h # 帮助信息
EOF
}
if [[ $# -eq 0 ]]; then help_message && exit 1; fi # $#表示传入的参数个数
# 日志打印
function log_error() {
printf "%b\n" "\033[1;31m$*\033[0m"
}
function log_info() {
printf "%b\n" "\033[0;32m$*\033[0m"
}
function log_warn() {
printf "%b\n" "\033[0;33m$*\033[0m"
}
concurrent_num=1 # 并发数默认值
init_line=2 # 初始行号默认值
#url=http://10.151.5.190:7670/ # 下载测试文件的地址
url=http://savvcenter.sensetime.com/resource
while getopts ":g:s:d:c:l:i:h" opt; do # 脚本参数
case $opt in
g)
gt_file=${OPTARG} # GT的csv文件
if [[ ! -f "${gt_file}" ]]; then
log_error "文件不存在: ${gt_file}" && exit 1
fi
;;
s) sdk=${OPTARG} ;; # 本地sdk目录
d)
device_serial=${OPTARG} # 设备序列号
if ! adb devices | awk '$NF~/device/{print $1}' | grep -iwq "${device_serial}"; then
log_error "设备未连接或不在线: ${device_serial}" && exit 1
fi
;;
c) concurrent_num=${OPTARG} ;; # 并发数
l) log_path=${OPTARG} ;; # 日志保存路径
i) init_line=${OPTARG} ;; # GT文件初始行号
h) help_message && exit ;; # 帮助
*) ;;
esac
done
# 补充校验传入脚本的参数,并初始化变量
function check_param_and_init() {
# GT文件和sdk目录不能为空
if [[ -z ${gt_file} ]] || [[ -z ${sdk} ]]; then help_message && exit 1; fi
# 支持不指定脚本参数中的设备,且只在线一个设备时,默认使用该设备
if [[ -z ${device_serial} ]]; then # 如果该变量为空,即没有传该参数
device_serial=$(adb devices | awk '$NF~/device/{print $1}') # 获取连接的设备
device_num=$(wc -l <<<"${device_serial}") # 统计连接的设备数量
if [[ ${device_num} -eq 0 ]]; then
log_error "没有在线设备" && exit 1
elif [[ ${device_num} -gt 1 ]]; then
log_error "当前连接了多个设备,需指定一个设备:" "\n${device_serial}" && exit 1
fi
fi
# 日志保存路径。${gt_file##*/}表示截取文件名
if [[ -z ${log_path} ]]; then
_time=$(date +'%Y_%m%d')
log_path="${_time}_${gt_file##*/}_log"
mkdir -p "${log_path}"
fi
remote_sdk="/data/local/tmp/$(basename "${sdk}")" # 设备上的sdk路径
total_line=$(awk 'END {print NR}' "${gt_file}") # GT文件总行数
}
# 上传初始化sdk
function init_sdk() {
adbs root && adbs remount # 获取root权限和system分区可写权限
log_info "开始推送sdk..."
local env_file='env.sh'
if ! adbs exec-out "ls ${remote_sdk}" | grep -iwq 'No such file or directory'; then # 检查sdk是否已推送过
log_info "sdk已在设备上: ${remote_sdk}"
elif [[ ! -d ${sdk} ]]; then
log_error "sdk不存在: ${sdk}" && exit 1
elif ! find "${sdk}" -name "${env_file}" | grep -iwq -m 1 "${env_file}"; then # 查找是否有env.sh文件
log_error "sdk缺少${env_file}文件: ${sdk}" && exit 1
elif ! adbs push "${sdk}" "${remote_sdk}"; then # 推送sdk到设备上
log_error "sdk推送失败: ${sdk}" && exit 1
fi
# 获取设备上env.sh文件路径,即sdk运行根路径
if env_path=$(adbs exec-out "find ${remote_sdk} -name ${env_file}" | sort -n | grep -w -m 1 "${env_file}"); then
pre_command="cd $(dirname "${env_path}") && source env.sh && ./samples_CAPI/bin/" # 执行测试的前置命令
else
log_error "设备上的sdk缺少${env_file}文件: ${remote_sdk}" && exit 1
fi
adbs exec-out "chmod 777 -R ${remote_sdk}" # 修改权限,否则无法执行
}
# 打印任务信息
function print_task_info() {
printf "%s\n" "
|设备: ${device_serial}
|并发数: ${concurrent_num}
|GT文件: ${gt_file}
|测试行数: ${init_line} - ${total_line}
|sdk路径: ${remote_sdk}
|log路径: ${log_path}
"
}
# 初始化并发数
function init_concurrent() {
_fifo_file="$$.fifo" # $$表示当前执行文件的PID
mkfifo ${_fifo_file} # 创建FIFO管道文件
exec 6<>${_fifo_file} # 将文件描述符fd6和管道文件绑定
rm -rf ${_fifo_file} # 删除FIFO管道文件
# 通过循环对文件描述符fd6写入空行,每行代表一个可并发的后台进程数量
for _ in $(seq "${concurrent_num}"); do printf "\n" >&6; done
}
# 关闭文件描述符和管道的读写
function close_pipe() {
exec 6>&- # 关闭文件描述符的写
exec 6<&- # 关闭文件描述符的读
}
# 简化adb -s的使用
function adbs() {
adb -s "${device_serial}" "$@"
}
# 清理日志等,释放硬盘空间
function clear_android_log() {
log_warn "开始清理设备上的日志..."
adbs exec-out "rm -rf /storage/emulated/0/*log"
adbs exec-out "rm -rf /data/media/0/*log"
adbs exec-out "rm -rf /mnt/runtime/*/emulated/0/*log" # 第一个*包含default、read、write
adbs exec-out "rm -rf /mnt/runtime/*/emulated/0/bufferIn.pcm"
adbs exec-out "rm -rf /mnt/runtime/*/emulated/0/Car*Video"
}
# 将文件推送到设备上,如果本地没有则下载
function push_file() {
local local_file="./$1" # 默认为当前目录下的本地文件
if [[ -f $1 ]]; then # 如果绝对路径的文件存在,则使用绝对路径
local_file=$1
elif [[ ! -f "./$1" ]]; then # 如果绝对路径不存在,且当前目录下文件也不存在
mkdir -p "${local_file%/*}" # 创建文件夹。${local_file%/*}表示截取文件目录
log_info "${progress} 开始下载文件: $1"
if ! curl -f --connect-timeout 60 "${url}/${local_file}" -o "${local_file}"; then
log_error "${progress} 文件下载失败: $1"
rm -rf "${local_file}" # 删除未下载完成的本地文件。有trap时,即使按Ctrl+C也会执行
return 1
fi
fi
log_info "${progress} 开始推送文件: ${local_file}"
if ! adbs push "${local_file}" "$2"; then
log_error "${progress} 文件推送失败: ${local_file}" && exit 1
# clear_android_log
fi
}
# 根据GT文件的第二列classification生成完整的测试命令
function generate_command() {
local keywords file
keywords=$(tr '[:upper:]' '[:lower:]' <<<"$1") # 转换为全小写
file="$2"
if [[ ${keywords} == *register* ]]; then
# test_command="test_FaceID ${file} 30 0 0 face.db" # B561 主驾注册
# test_command="test_FaceID ${file} 30 0 1 face.db" # B561 副驾注册
test_command="test_FaceID 0 ${file} 30 face.db . 30 0" # P789 注册
elif [[ ${keywords} == *login* ]]; then
# test_command="test_FaceID ${file} 30 1 0 face.db" # B561 主驾登录
# test_command="test_FaceID ${file} 30 1 1 face.db" # B561 副驾登录
# test_command="test_FaceID ${file} 30 1 3 face.db" # B561 车外登录
test_command="test_FaceID 1 ${file} 30 face.db . 30 0" # P789 登录
elif [[ ${keywords} == *liveness* ]]; then
# 活体必须要人脸特征值的文件,且文件中的值正确
# adbs exec-out "printf %s\n test_liveness > ${remote_sdk}/liveness.db" # 创建face.db文件,并写入数据
test_command="test_FaceID ${file} 1 1 2 liveness.db" # B561 活体-图片-前排
# test_command="test_FaceID 1 ${file} 1 liveness.db . 1 1" # JMC 活体-图片
# test_command="test_FaceID 1 ${file} 30 liveness.db . 30 0" # JMC 活体-图片
else
test_command="test_OMS ${file} 30 oms" # B561 OMS
# test_command="test_DMS ${file} 30" # JMC DMS
fi
log_info "${progress} ${test_command}"
full_command="${pre_command}${test_command}"
}
# 脚本异常时的安全退出操作
function safe_exit() {
sleep 1
log_warn "--------------------------------- 收到终止信号,安全退出中 ---------------------------------"
sleep 2
exit
}
# 执行测试 --------------------------------------------------------------------------------------------------------------
check_param_and_init
print_task_info
# INT 按下Ctrl+C发出;HUP 终端断开或用户退出时发出;QUIT 按下Ctrl+/发出;TERM 系统关机时发出
trap "safe_exit" SIGINT SIGHUP SIGQUIT SIGTERM
trap "wait; close_pipe; print_task_info" EXIT
init_sdk
init_concurrent
# 读取文件的每一行进行测试
awk "NR>=${init_line}" "${gt_file}" | while IFS=, read -r test_file classification _; do
progress="[${init_line}/${total_line}]"
read -r -u6 # 读取一个空行,代表消耗1个并发数
{
function teardown() {
adbs exec-out "rm -rf ${remote_file}" # 删除推送的视频
printf "\n" >&6 # 写入一个空行,代表添加1个并发数
}
trap "" SIGINT SIGHUP SIGQUIT SIGTERM # 忽略异常退出的信号
trap "teardown" EXIT
# 替换路径后缀名为.log,并替换/为^。${test_file%.*}表示截取从左往右最后一个.左边的所有内容
log_file="${log_path}/$(sed -e 's/\//^/g' <<<"${test_file%.*}.log")"
if [[ -f "${log_file}" ]]; then # 日志文件已存在,跳过
log_warn "${progress} 日志已存在: ${log_file}" && exit
fi
remote_file="${remote_sdk}/${test_file/\&/\\&}" # 处理特殊字符,将&替换为\&
push_file "${test_file}" "${remote_file}" || exit 1
generate_command "${classification}" "${remote_file}" # 生成完整的测试命令
if ! adbs exec-out "${full_command}" >"${log_file}"; then # 执行测试并保存日志文件到本地
log_warn "${progress} 删除未完成日志: ${log_file}"
rm -rf "${log_file}"
fi
} & # &表示将{}中的命令放到后台执行
if [[ $(((init_line++))) -eq ${total_line} ]]; then wait; fi # 每次执行后行号加1,且最后一行时等待所有后台进程执行完毕
done
【windows+android】shell批量推送数据+保存
最新推荐文章于 2024-07-21 20:23:03 发布