iOS通过shell脚本批量修改属性

背景

公司需要做一系列的壳版本,壳版本如果内容雷同提交到App Store会有被拒绝的风险,除了我在上一篇文章中说道的在壳版本中注入混淆的代码,防止被苹果检测到内容太过雷同而导致审核被拒绝。还有另一种可行的方法是批量修改源文件中的类名、属性、方法名称等会在二进制文件中留下符号标记的信息,绕过苹果的机器审核。
这篇文章介绍的是如何使用脚本批量修改属性名称,后续还有系列的包括使用脚本批量修改类名称、方法名称等信息的文章。

结果

下面是执行脚本替换了属性的结果图,脚本把所有需要替换的属性添加了abc后缀,当然依然是可以正常编译运行的

源码:https://gitee.com/dhar/YTTInjectedContentKit

分析

原理分析

objc代码中的类名、属性、方法、源文件路径等信息最终会被打包到二进制文件中,保存在二进制文件中的.sym符号表段中,可以使用objdump -t命令查看二进制符号信息,以下的命令把objdump -t的结果写入到文件InjectedContentKit_Example_Symbols中去。

objdump -t InjectedContentKit_Example > InjectedContentKit_Example_Symbols

文件的内容会很大,所以选择了几个代表性的内容说明:

0000000100026350 l d __TEXT,__text	__text
# 这里保存的是类源文件的路径符号信息
0000000000000000 l d *UND*	/Users/aron/PuTaoWorkSpace/project/sscatch/DevPods/InjectedContentKit/InjectedContentKit/Classes/Composer/PubSearchDataComposer.h

# 这里保存的是属性对应的var信息
0000000000000000 l d *UND*	_OBJC_IVAR_$_TextCardItem._title
0000000000000000 l d *UND*	_OBJC_IVAR_$_TextCardItem._showReact
0000000000000000 l d *UND*	_OBJC_IVAR_$_TextCardItem._topChart
0000000000000000 l d *UND*	_OBJC_IVAR_$_TextCardItem._reaction

# 这里保存的是属性信息对应的getter方法信息
00000001000264a0 l  F __TEXT,__text	-[TextCardItem title]
00000001000264c0 l  F __TEXT,__text	-[TextCardItem showReact]
00000001000264f0 l  F __TEXT,__text	-[TextCardItem topChart]
0000000100026510 l  F __TEXT,__text	-[TextCardItem setTopChart:]

# 这里保存的是属性信息对应的setter方法信息
00000001000028a0 l  F __TEXT,__text	-[SSCatchInviteScheduler setOrganizer:]
00000001000028e0 l  F __TEXT,__text	-[SSCatchInviteScheduler setInputCardBack:]
0000000100002920 l  F __TEXT,__text	-[SSCatchInviteScheduler setInputTextBack:]

# 这里保存的是类文件的文件名信息
0000000000000000 l d *UND*	PubSearchDataComposer.m
000000005a937587 l d __TEXT,__stub_helper	__stub_helper
00000001000251c0 l d __TEXT,__text	__text

从上面可以看出,二进制中保留了很多信息和源代码有很大关系,我们做个简单的猜测苹果后台机器审查二进制的时候会通过二进制中的符号进行对比,如果两个二进制(一个主版本、一个壳版本)代码中的符号重合度超过某个阈值,就会判定这是发布壳版本的行为,而这是苹果说不允许的,所以可行的方法是修改源文件中的这些信息来绕过苹果的审查机制。

另外猜测苹果应该是不会根据代码中的流程控制来判断的,因为二进制中的控制流程已经是机器码了,反编译出来也就是汇编代码,只要稍微做点改动二进制(.text段)就会变化很大。所以从这个方面来判断就难度很大了。

步骤分析

主要有以下几个步骤

  1. 寻找到需要替换的源文件中的所有的属性,处理之后保存在配置文件中
  2. 用户自定义一个黑名单配置文件
  3. 某部分需要隔离的代码中的属性生成黑名单配置文件
  4. 把需要替换的源文件中的所有匹配的属性做批量的替换

这里说明下为什么第一步需要保存在配置文件中,因为第三步的操作有部分和第一步是相同的,所有这部分单独出来一个模块共用,都是输入一个文件夹,最终保存在指定的文件中,后面的代码中可以看到这部分。

实现

单步实现

1、寻找到需要替换的源文件中的所有的属性,处理之后保存在配置文件中

这一步的功能是客户端输入一个需要处理的源码文件夹,递归遍历该源码文件夹获取所有源码文件(.h .m 文件)。使用正则匹配找到属性名称,暂时保存到数组中,最后经过黑名单过滤、去重过滤、其他过滤条件过滤,最终把待处理的属性保存到客户端输入的输出文件中。

可以分解为一下几个小步骤

  • 递归遍历文件夹获取源码文件
  • 正则匹配源码文件的属性
  • 过滤属性(可选)
  • 保存属性到文件

这部分功能的源码如下:

文件名: GetAndStoreProperties.sh

该脚本在多个地方都有用到,所以作为一个单独的模块,定义了一些参数,以适应不同的应用场景。在下面可以看到使用该脚本的地方。

#!/bin/bash
########################
# 脚本功能:从指定目录获取和保存属性到指定的文件
# 输入参数 -i 输入的文件夹
# 输入参数 -o 保存的文件
# 输入参数 -f 使用黑名单和自定义过滤条件的参数
# 输入参数 -c 自定义的黑名单文件
########################

####### 参数定义
param_input_dir=""
param_output_file=""
param_custom_filter_file=""
param_should_use_filter=0

####### 参数解析
while getopts :i:o:c:f opt
do
	case "$opt" in
		i) param_input_dir=$OPTARG
			echo "Found the -i option, with parameter value $OPTARG"
			;;
		o) param_output_file=$OPTARG
			echo "Found the -o option, with parameter value $OPTARG"
			;;
		c) param_custom_filter_file=$OPTARG
			echo "Found the -c option, with parameter value $OPTARG"
			;;
		f) echo "Found the -f option"
			param_should_use_filter=1
			;;
		*) echo "Unknown option: $opt";;
	esac
done

####### 配置

# 属性黑名单配置文件
blacklist_cfg_file="$(pwd)/DefaultBlackListPropertiesConfig.cfg"

####### 数据定义

# 定义保存源文件的数组
declare -a implement_source_file_array
implement_source_file_count=0

# 定义保存属性的数组
declare -a tmp_props_array
props_count=0

# mark: p384
# 递归函数读取目录下的所有.m文件
function read_source_file_recursively {
	echo "read_implement_file_recursively"
	if [[ -d $1 ]]; then
		for item in $(ls $1); do
			itemPath="$1/${item}"
			if [[ -d $itemPath ]]; then
				# 目录
				echo "处理目录 ${itemPath}"
				read_source_file_recursively $itemPath
				echo "处理目录结束====="
			else
				# 文件
				echo "处理文件 ${itemPath}"
				if [[ $(expr "$item" : '.*\.m') -gt 0 ]] || [[ $(expr "$item" : '.*\.h') -gt 0 ]]; then
					echo ">>>>>>>>>>>>mmmmmmm"
					implement_source_file_array[$implement_source_file_count]=${itemPath}
					implement_source_file_count=$[ implement_source_file_count + 1 ];
				fi
				echo ""
			fi
		done
	else
		echo "err:不是一个目录"
	fi
}

# 读取源码中的属性,保存到数组中
# 参数一: 源码文件路径
function get_properties_from_source_file {
	local class_file=$1;
	echo "class_file=${class_file}"

	properties=$(grep "@property.*" ${class_file})
	IFS_OLD=$IFS
	IFS=$'\n'
	for prop_line in $properties; do
		echo ">>>>>${prop_line}"

		asterisk_seperator_pattern="\*"
		if [[ ${prop_line} =~ ${asterisk_seperator_pattern} ]]; then
			# 从左向右截取最后一个string后的字符串
			prop_name=${prop_line##*${asterisk_seperator_pattern}}
			# 从左向右截取第一个string后的字符串
			seal_pattern=";*"
			seal_pattern_replacement=""
			prop_name=${prop_name//${seal_pattern}/${seal_pattern_replacement}}
			subsring_pattern="[ |;]"
			replacement=""
			prop_name=${prop_name//${subsring_pattern}/${replacement}}

			if [[ ${param_should_use_filter} -gt 0 ]]; then
				grep_result=$(grep ${prop_name} ${blacklist_cfg_file})
				echo "grep_result = >>${grep_result}<<"
				custom_grep_result=""
				if [[ -n ${param_custom_filter_file} ]]; then
					custom_grep_result=$(grep ${prop_name} ${param_custom_filter_file})
				fi
				if [[ -n ${grep_result} ]] || [[ -n ${custom_grep_result} ]]; then
					echo "--${prop_name}--存在配置文件中"
				else
					echo "--${prop_name}--XXX不存在配置文件中"

					tmp_props_array[$props_count]=$prop_name
					props_count=$[ props_count + 1 ]
					echo ">>>>>>>result_prop_name=${prop_name}"
				fi
			else
				tmp_props_array[$props_count]=$prop_name
				props_count=$[ props_count + 1 ]
			fi
		fi
	done
	IFS=$IFS_OLD
}

# 获取目录下的所有源文件,读取其中的属性
function get_properties_from_source_dir {

	local l_classed_folder=$1

	echo "获取需要处理的源文件... ${l_classed_folder}"
	# 读取需要处理目标文件
	read_source_file_recursively ${l_classed_folder}

	echo "读取源文件中的属性..."
	for(( i=0;i<${#implement_source_file_array[@]};i++))
	do
		class_file=${implement_source_file_array[i]};
		echo "处理源文件:${class_file}"
		get_properties_from_source_file ${class_file}
	done;
}

# 把获取到的属性过滤之后写入文件中
# 过滤步骤包含去重、去掉简单词汇、去掉长度少于多少的词汇
# 如果在执行的过程中遇到特殊情况,添加到黑名单配置(DefaultBlackListPropertiesConfig.cfg文件中添加配置)
function post_get_properties_handle {

	local prop_config_file=$1

	# 写入文件中
	echo "# Properties Configs" > ${prop_config_file}
	for key in $(echo ${!tmp_props_array[*]})
	do
	 # echo "$key : ${tmp_props_array[$key]}"
	 echo ${tmp_props_array[$key]} >> ${prop_config_file}
	done

	# 去重
	cfg_back_file="${prop_config_file}.bak"
	mv ${prop_config_file} ${cfg_back_file}
	sort ${cfg_back_file} | uniq > ${prop_config_file}

	# 过滤
	if [[ ${param_should_use_filter} -gt 0 ]]; then
		mv ${prop_config_file} ${cfg_back_file}
		echo "# Properties Configs Filtered" > ${prop_config_file}
		IFS_OLD=$IFS
		IFS=$'\n'
		# 上一行的内容
		lastLine="";
		for line in $(cat ${cfg_back_file} | sed 's/^[ \t]*//g')
		do
			if [[ ${#line} -le 6 ]] || [[ $(expr "$line" : '^#.*') -gt 0 ]]; then
				# 长度小于等于6或者注释内容的行不处理
				echo "less then 6 char line or comment line"
			else
				if [[ -n ${lastLine} ]]; then
					# 上一行是非空白行
					# 比较上一行内容是否是当前行的一部分,不是添加上一行
					if [[ ${line} =~ ${lastLine} ]]; then
						echo "${line} 和 ${lastLine} 有交集"
					else
						echo ${lastLine} >> ${prop_config_file}
					fi
				fi
				# 更新上一行
				lastLine=${line}
			fi
		done
		IFS=${IFS_OLD}
	fi

	# 删除临时文件
	rm -f ${cfg_back_file}
}

get_properties_from_source_dir ${param_input_dir}
post_get_properties_handle ${param_output_file}

使用以上脚本生成的配置文件 PropertiesConfigs.cfg 部分如下:

# Properties Configs Filtered
UserRestrictionLabel
aboutusButton
activitySamplers
addAddressPress
addressSamplers
addressTextBox
appealPress
appliedGroupedSamplers
appliedSamplers
applyPress
asyncArray
asyncListSampler
audioPlayer

2. 用户自定义一个黑名单配置文件

在实践的过程中,替换属性的符号有时候会把系统类的属性替换了,比如

  • 把 AppDelegate 中的 window 属性替换了,导致了编译链接没错,但是界面出不来了,因为初始的window对象找不到了
  • 把 UIButton 中的 titleLabel 属性替换了,直接导致了编译出错

对于这类问题,需要在黑名单中配置一些默认的过滤属性,对于黑名单中的这些属性不处理即可,在我的业务场景下,黑名单文件的配置如下:

文件名:DefaultBlackListPropertiesConfig.cfg

# BlackListPropertiesConfig.cfg
# 属性黑名单配置,在此配置文件中的属性不需要替换名称
window
name
title
titleLabel
layout
appealSamplers

在 GetAndStoreProperties.sh 脚本使用到的代码片段如下,其实就是使用了 grep 命来查找,判断时候有找到,如果有就不处理,具体的可以看上面提供的完整的 GetAndStoreProperties.sh 脚本代码

if [[ ${param_should_use_filter} -gt 0 ]]; then
	grep_result=$(grep ${prop_name} ${blacklist_cfg_file})
	echo "grep_result = >>${grep_result}<<"
	custom_grep_result=""
	if [[ -n ${param_custom_filter_file} ]]; then
		custom_grep_result=$(grep ${prop_name} ${param_custom_filter_file})
	fi
	if [[ -n ${grep_result} ]] || [[ -n ${custom_grep_result} ]]; then
		echo "--${prop_name}--存在配置文件中"
	else
		echo "--${prop_name}--XXX不存在配置文件中"

		tmp_props_array[$props_count]=$prop_name
		props_count=$[ props_count + 1 ]
		echo ">>>>>>>result_prop_name=${prop_name}"
	fi
else
	tmp_props_array[$props_count]=$prop_name
	props_count=$[ props_count + 1 ]
fi	

3. 某部分需要隔离的代码中的属性生成黑名单配置文件

这部分的功能其实就是调用 GetAndStoreProperties.sh 这个脚本,最终把文件输出的文件以追加的方式写入到用户自定义的黑名单属性文件中。

#...
# 黑名单类目录
declare -a custom_blacklist_search_dirs
custom_blacklist_search_dirs=("/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/SSCatchAPI"
	"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/Categories"
	"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/Components"
	"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/External"
	"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/HandyTools"
	"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/Macros" )
# ...

# 属性黑名单配置文件
custom_blacklist_cfg_file="$(pwd)/CustomBlackListPropertiesConfig.cfg"

# ...
# 获取自定义的黑名单属性并保存到文件中
echo "" > ${custom_blacklist_cfg_file}
for (( i = 0; i < ${#custom_blacklist_search_dirs[@]}; i++ )); do
	custom_blacklist_search_dir=${custom_blacklist_search_dirs[${i}]}
	./GetAndStoreProperties.sh \
		-i ${custom_blacklist_search_dir}\
		-o ${custom_blacklist_cfg_tmp_file}
	cat ${custom_blacklist_cfg_tmp_file} >> ${custom_blacklist_cfg_file}
done
#...

最终生成的用户自定义的黑名单文件部分如下

文件:CustomBlackListPropertiesConfig.cfg

# Properties Configs
DBFilePath
ValidityString
accessQueue
age
attributedNameString
avatarURLString
avatarUrlString
backColorString
bodyScheduler
bodyView
catchDateString
cellHeight
channelKey
cityName
conditionString
# ....

4. 把需要替换的源文件中的所有匹配的属性做批量的替换

这一步在前面三部的基础上,查找并替换源码目录中在 PropertiesConfigs.cfg 配置文件中出现的属性和属性的引用,查找使用grep命令、替换使用了sed命令。脚本代码如下

#!/bin/bash
# 属性重命名脚本

####### 配置
# classes类目录
classes_dir="$(pwd)/../InjectedContentKitx"
# 黑名单类目录
declare -a custom_blacklist_search_dirs
custom_blacklist_search_dirs=("/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/SSCatchAPI"
	"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/Categories"
	"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/Components"
	"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/External"
	"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/HandyTools"
	"/Users/aron/PuTaoWorkSpace/project/sscatch/sscatch/Classes/Macros" )
# 配置文件
cfg_file="$(pwd)/PropertiesConfigs.cfg"
# 属性黑名单配置文件
blacklist_cfg_file="$(pwd)/DefaultBlackListPropertiesConfig.cfg"
# 属性黑名单配置文件
custom_blacklist_cfg_file="$(pwd)/CustomBlackListPropertiesConfig.cfg"
custom_blacklist_cfg_tmp_file="$(pwd)/TmpCustomBlackListPropertiesConfig.cfg"
# 属性前缀,属性前缀需要特殊处理
class_prefix=""
# 属性后缀
class_suffix="abc"

# 检测文件是否存在,不存在则创建
checkOrCreateFile() {
	file=$1
	if [[ -f $file ]]; then
		echo "检测到配置文件存在 $file"
	else
		echo "创建配置文件 $file"
		touch $file
	fi
}

# 配置文件检查
checkOrCreateFile $cfg_file

# 循环检测输入的文件夹
function checkInputDestDir {
	echo -n "请输入需处理源码目录: "
	read path
	if [[ -d $path ]]; then
		classes_dir=$path
	else
		echo -n "输入的目录无效,"
		checkInputDestDir
	fi
}

# 需处理源码目录检查
if [[ -d $classes_dir ]]; then
	echo "需处理源码目录存在 $classes_dir"
else
	echo "请确认需处理源码目录是否存在 $classes_dir"
	checkInputDestDir
fi

####### 数据定义

# 定义属性保存数组
declare -a rename_properties_config_content_array
cfg_line_count=0

# 读取属性配置文件
function read_rename_properties_configs {
	IFS_OLD=$IFS
	IFS=$'\n'
	# 删除文件行首的空白字符 //www.jb51.net/article/57972.htm
	for line in $(cat $cfg_file | sed 's/^[ \t]*//g')
	do
		is_comment=$(expr "$line" : '^#.*')
		echo "line=${line} is_common=${is_comment}"
		if [[ ${#line} -eq 0 ]] || [[ $(expr "$line" : '^#.*') -gt 0 ]]; then
			echo "blank line or comment line"
		else
			rename_properties_config_content_array[$cfg_line_count]=$line
			cfg_line_count=$[ $cfg_line_count + 1 ]
			# echo "line>>>>${line}"
		fi
	done
	IFS=${IFS_OLD}
}

function print_array {
	# 获取数组
	local newarray
	newarray=($(echo "$@"))
	for (( i = 0; i < ${#newarray[@]}; i++ )); do
		item=${newarray[$i]}
		echo "array item >>> ${item}"
	done
}

# 重命名所有的属性
function rename_properties {

	# 读取属性配置文件
	read_rename_properties_configs
	# print_array ${rename_properties_config_content_array[*]}

	# 执行替换操作
	for (( i = 0; i < ${#rename_properties_config_content_array[@]}; i++ )); do
		original_prop_name=${rename_properties_config_content_array[i]};
		result_prop_name="${class_prefix}${original_prop_name}${class_suffix}"
		sed -i '{
			s/'"${original_prop_name}"'/'"${result_prop_name}"'/g
		}' `grep ${original_prop_name} -rl ${classes_dir}`
		echo "正在处理属性 ${original_prop_name}....."
	done
}

checkOrCreateFile ${custom_blacklist_cfg_tmp_file}

# 获取自定义的黑名单属性并保存到文件中
echo "" > ${custom_blacklist_cfg_file}
for (( i = 0; i < ${#custom_blacklist_search_dirs[@]}; i++ )); do
	custom_blacklist_search_dir=${custom_blacklist_search_dirs[${i}]}
	./GetAndStoreProperties.sh \
		-i ${custom_blacklist_search_dir}\
		-o ${custom_blacklist_cfg_tmp_file}
	cat ${custom_blacklist_cfg_tmp_file} >> ${custom_blacklist_cfg_file}
done

# 获取和保存属性到熟悉配置文件
./GetAndStoreProperties.sh \
	-i ${classes_dir}\
	-o ${cfg_file}\
	-f \
	-c ${custom_blacklist_cfg_file}

# 执行属性重命名
rename_properties

echo "done."

总结

以上就是基于shell脚本,以壳版本为场景,把属性的批量替换做了一个半自动化的实现步骤,如果不妥之处,还请不吝赐教。

(0)

相关推荐

  • Shell实现系统时间和BIOS时间同步校准脚本分享

    该脚本主要是从定义好的NTP服务器列表获取服务器地址进行同步,如果第一个不成功,会继续换下一个地址进行同步!不完善之处还请指出! 复制代码 代码如下: #!/bin/bash # NTP网络时间校正脚本 # 奔跑 #NTP服务器数组列表 ntpServer=( [0]=ntp.fudan.edu.cn [1]=asia.pool.ntp.org [2]=210.72.145.44 [3]=133.100.11.8 [4]=ntp.sjtu.edu.cn [5]=time.scau.edu.cn

  • shell脚本监控linux系统内存使用情况的方法(不使用nagios监控linux)

    一.安装linux下面的一个邮件客户端msmtp软件(类似于一个foxmail的工具) 1.下载安装: 复制代码 代码如下: # tar jxvf msmtp-1.4.16.tar.bz2# cd msmtp-1.4.16# ./configure --prefix=/usr/local/msmtp# make# make install 2.创建msmtp配置文件和日志文件(host为邮件域名,邮件用户名test,密码123456) 复制代码 代码如下: # vim ~/.msmtprcacc

  • Shell脚本实现批量生成nagios配置文件

    如果管理的站点和服务器较多的情况下,每次修改配置文件都相当痛苦.因而想到了用shell脚本来批量生成配置文件和配置数据.下面这个脚本是为了批量生成nagios监控配置文件的一个shell脚本程序.其原理是事先定义一个shell脚本模板,然后每个需要监控的站点复制一份模板替换掉模板文件里面的变量. 1.准备模板文件webcheck.template more webcheck.template ###################WEBURL define start##############

  • Shell脚本编写Nagios插件监控程序资源占用

    一般情况下,我们只需要监控程序进程在没在就可以了.但是这次遭遇了这样的事,公司开发的程序,程序进程还在,但是死锁了.导致大范围的影响,更要命的是根本不知道问题出在哪里,还是别的测试部同事帮忙发现的,真是丢尽运维的脸了- 为避免下次再遭遇到这样的情况,分析了这次进程死锁的现象,发现死锁会占用100%的cpu,正常情况下只占用10%以内.决定编写nagios插件,用来监控程序占用的资源,包括cpu,内存等. 一.shell脚本需求分析: 能设置cpu,mem的阈值,资源占用超过阈值就报警.    要

  • iOS通过shell脚本批量修改属性

    背景 公司需要做一系列的壳版本,壳版本如果内容雷同提交到App Store会有被拒绝的风险,除了我在上一篇文章中说道的在壳版本中注入混淆的代码,防止被苹果检测到内容太过雷同而导致审核被拒绝.还有另一种可行的方法是批量修改源文件中的类名.属性.方法名称等会在二进制文件中留下符号标记的信息,绕过苹果的机器审核. 这篇文章介绍的是如何使用脚本批量修改属性名称,后续还有系列的包括使用脚本批量修改类名称.方法名称等信息的文章. 结果 下面是执行脚本替换了属性的结果图,脚本把所有需要替换的属性添加了abc后

  • Shell脚本批量修改文件后缀名代码分享

    早上本想将一些照片上传到相册中,但是由于所有照片的扩展名都是JPG而不是小写的jpg,因此造成了"格式不正确"而不能上传照片.此刻就产生了这样一个问题:使用shell脚本如何批量将所有文件的扩展名JPG都改成小写的jpg? 既然要批量替换文件名,那么肯定得用一个for循环依次遍历指定目录下的每个文件.对于每个文件,假如该文件的名称为name.oldext,那么我们必须原始文件名中挖出name,再将它与新的文件扩展名newext拼接形成新的文件名name.newext.依照这样的思路,就

  • shell脚本批量复制及执行命令的示例详解

    平时在处理一个或几个机器运行环境时,一个机器一个机器处理也能接受,但是如果是一批机器,几十或几百台,要是一台一台去安装环境,光是输入同一的命令,估计你自己都想吐,所有聪明的人会想一些偷懒的办法,确实可以找到一些省时省力的方法,比如写一个批量处理shell脚本,这几天在处理一批(八九十台)机器环境,找了一些批量处理的脚本,包括批量传输(scp)文件到多台机器上.批量执行命令到多台机器.还有需要交互的命令,下面记录一些这些命令: 机器IP文件:ip.txt 192.168.10.201 192.16

  • 通过Shell脚本批量创建服务器上的MySQL数据库账号

    1.项目背景 因监控需要,我们需要在既有的每个MySQL实例上创建一个账号.公司有数百台 MySQL 实例,如果手动登入来创建账号很麻烦,也不现实.所以,我们写了一个简单的shell脚本,用来创建批量服务器的mysql 账号.  2.执行脚本内容; #!/bin/bash ## 此段shell 脚本的主要功能是实现在多个SQL Server IP实例上,创建账号.输入参数是两个,第一个是数据库所在的IPs,即多个Server IP构成的字符串,IP间用逗号隔开.第二个参数是 端口(3306 或

  • Linux通过Shell脚本命令修改密码的两种方式

    交互方式修改密码 1. ssh 远程到主机: 2. 切换到root账号: [一般都是切换到root进行密码修改,如果普通用户修改自己的密码,要输入原密码,然后新密码要满足复杂度才OK]: 3. passwd username 使用passwd username 修改 username 的密码: 使用该命令会有提示,即进入了交互界面,输入密码即可. 使用脚本修改密码 很多时候我们可能需要远程执行服务器上的脚本来修改账号密码,此时就没有办法进行交互了. 此时可以使用如下两种方式修改密码: 方式1:

  • 使用Shell脚本批量启停Docker服务

    最近日常测试中经常需要手动启动或停止docker,于是决定写一个Shell脚本来代替人工操作,另外该脚本,也可以通过Python脚本实行远程调用,详细如下所示: 目前该脚本是将Container ID写死在脚本中,当然也可以通过传参给脚本来进行控制,大家可以改造一下. 启动docker 启动脚本详细如下所示: #!/bin/bash containerIDs="ad3e4d7fc407 a228730a915f ad3e4d7fc4099" statusLived="live

  • touch shell脚本并修改为777权限的方法

    mksh.sh #!/bin/bash for i in "$@" do touch ${i} chmod 777 ${i} echo "#!/bin/bash">${i} done mkcpptest.sh #!/bin/bash for i in "$@" do touch ${i} echo '#include<iostream> #include<ctime> using namespace std; int

  • shell脚本批量创建用户的方法小结

    目录 shell脚本批量创建用户基本操作 附:用shell脚本批量创建50个用户并设置密码,批量删除用户 总结 shell脚本批量创建用户基本操作 案例:批量创建user01 ~ user20 一共二十个文件,密码为随机的六位数  并且将 用户名和密码 输出到 user.txt 文件中 #!/bin/bash #批量创建20个用户 user01 ~ user20 user=user #创建存储用户和密码的文件 touch user.txt txt=user.txt #循环遍历1到20 for i

  • BAT脚本批量修改文件名的两种方法

    目录 一,先进入想要修改文件名的文件夹中,使用命令: 1.以示例文档为例进行说明 2.双击1.bat来生成yuan.xls文件,如下图: 二.直接通过BAT脚本实现 通过BAT脚本批量修改文件名的办法有两种: 一,先进入想要修改文件名的文件夹中,使用命令: dir /b "*.后缀名" > yuan.xls 从生成含有所需文件名的xls文件(即Excel文件),然后在xls文件中通过 CONCATENATE("ren ",A2," ",B2

  • shell脚本批量将文件复制到指定的文件夹下

    由于线上文件比较多,选择特定的文件拿下线下进行语料标注,如果指定的文件数量太多,一个个复制就很麻烦.所以写一个shell脚本进行批量操作. 首先把需要下载的文件路径写入txt文件中,如果需要路径补全,则在每条路径上加上*号,这样就很简单cd到对应的目录下,(就这个小点,花费了我很久时间) 如图所示: 然后就是遍历txt文件进行路径下操作, cat 2022-05-07_path.txt | while read line do #echo $line dir=根目录/"$line" e

随机推荐