Linux文本处理之awk、tr命令妙用:解析合并经纬度参数输出到终端窗口,方便一键复制

stevehe 2022年10月15日 143次浏览

场景概述

场景:很多时候我们从数据源(数据库、文本文件、Excel表格等)取到经纬度数据,需要拿到其他地方查询或者使用,各个平台需要的参数格式可能大不相同,比如有的平台需要经度+纬度的格式,有的地方需要纬度+经度的格式(eg:Google地图搜索),复制的文本携带的分隔符也五花八门,手动用记事本类的软件过滤亦可达到目的,但比较繁琐,不够快捷,平日在终端工作的情况下,可以借助Bash脚本实现一键过滤和组合后输出,直接选中复制即可(mintty窗口下还可以直接双击鼠标左键进行当前行整行复制!)

下文使用lngandlat函数实现同等目的,传递需要过滤和组合的经纬度参数即可;

优点:

传递的参数先后顺序不敏感,代码一定程度对参数进行判别:哪一个是经度,哪一个是纬度;
传递的参数格式较为宽松;分隔符数量不限,分隔符允许重叠,程序代码能对其进行智能缩减和过滤;
允许参数前或参数后包含多余的分隔符,分隔符支持常见特殊字符,支持分隔符夹分隔符的特殊情况处理;
awk本身语法强大,允许后续增强和拓展其余功能;

借助tr、awk命令过滤分隔符,分隔字段域等,当前默认输出时以逗号作为分隔符;

函数 lngandlat 实现代码:

lngandlat() {
	#经纬度格式转换,传入任意格式的经纬度数据,转换为需要的格式输出,方便一键复制
	#经纬度参数可以分开传递也可以合在一个参数传递(lngandlat 111,222 与 lngandlat 111 222效果相同);
	#支持 , # ;| ||  $ @ 等特殊符号作为经纬度分隔符,不限制分隔符字符数(重复无所谓),程序会自动缩减和处理
	# 参数中包含井号(#)和分号(;)时,参数需要用双引号或单引号包裹,其他情况下,引号可以省去
	# 注:awk没有内置求绝对值的函数,借用sqrt曲线救国;
	# See Also:https://zhidao.baidu.com/question/1801649363884678947.html
	# See Also2:https://zhidao.baidu.com/question/304949582658546204.html
	# eg:
	#	 lngandlat -102.995824 22.706189
	#	 lngandlat -102.995824,22.706189
	#    lngandlat 22.706189,-102.995824,
	#    lngandlat ";;102.995824##22.706189"
	#    lngandlat ";;-102.995824##22.706189"
	#------------------------------	
	_print_usage() { #打印帮助信息
		echo -e "lngandlat:\n\t纬度格式转换,传入任意格式的经纬度数据,转换为多种或许需要的格式输出;"
		echo -e "\t【目   的】:有的场景需要经度+纬度的组合,而有的需要纬度+经度组合,手动调换参数较为麻烦,故编写此快捷函数;"
		echo -e "\t【快捷操作】:输出数据后,mintty窗口下可以双击鼠标左键复制当前行文本;"
		echo -e "\t传入经纬度时先后顺序不敏感,分隔符可以为空格、逗号、#号或其他常见特殊字符,字符个数不限,程序会自动过滤处理;"
		echo -e "\t经纬度参数可以分开传递也可以合在一个参数传递(\`lngandlat 111,222\` 与 \`lngandlat 111 222\` 效果相同);"
		echo -e "\t注:参数数中包含井号(#)和分号(;)时,参数需要用双引号或单引号包裹,其他情况下,引号可以省去;"
		echo -e "\nUsage:\n\tlngandlat  *longitude~and~latitude~paramter\n"
		echo -e "--------------------------------------------------------------"
		echo -e "\nExample:\n\tlngandlat -102.995824 22.706189"
		echo -e "\tlngandlat 22.706189 -102.995824  #参数顺序不敏感,经度可前可后"
		echo -e "\tlngandlat -102.995824,22.706189  #两个参数可以合并传递"
		echo -e "\tlngandlat 22.706189,-102.995824"
		echo -e "\tlngandlat \"22.706189,,#,,-102.995824\"   #分隔符字数不限"
		echo -e "\tlngandlat \"-102.995824;22.706189\"  #参数包含分号需加引号"
		echo -e "\tlngandlat \"#102.995824##22.706189\"  #参数可以包含任意特殊字符作为经度和纬度的分隔符,且分隔符个数不限"
		echo -e "\tlngandlat \";;|\\\$-102.995824##22.706189\\$\\$\" #包含杂乱的分隔符不影响处理,注意包含\$需要转义"
		echo -e "\tlngandlat ';;|\$-102.995824##22.706189\$\$' #使用单引号无需转义\$"
	}
	[ $# -eq 0 ] && echo -e "缺少参数!"
	if [[ $# == 0 || "${*,,}" == "-h" || "${*,,}" == "--help" ]];then	
		_print_usage && return
	fi
	
	#这里借助awk统一在位置1输出经度,位置2输出纬度:
	mapfile -t Coords <<<$(\
		echo "$*"|tr -s ',;#|@$ '|awk -F '[,#;\\|\\$@ ]' '{
			sub(/[^0-9]+?$/,""); /*替换参数结尾的分隔符,否则会影响栏位$NF定位*/
			/*依次向前取到倒数第二个非空字段*/
			/*为了处理分隔符夹分隔符的情况:lngandlat "22.706189,,#,,-102.995824"*/
			findex=NF-1;
			while($findex==""){
				findex=findex-1;
			}
			previous=$findex;
			if(previous<0 && $NF<90){ /*第一个数字小于零,第二个数字小于90,则认定为 经度、纬度格式*/ 
				print previous;
				print $NF;
			}else if($NF<0 && previous<90) { /*第二个数字小于零,第一个数字小于90,则认定为 纬度、经度格式*/ 
				print $NF;
				print previous;
			}else if(sqrt(previous*previous)>65 && sqrt($NF*$NF)>65) { /*两个数字绝对值过大,则认定为错误的经纬度数据(纬度最高为冰岛国的雷克雅未克:64°09′)*/ 
				print "ERROR";
				print "ERROR";
			}else if(sqrt(previous*previous)<65 && sqrt($NF*$NF)>90) { /*对一个参数和第二个参数模糊求绝对值,推测为 纬度、经度格式*/ 
				print $NF;
				print previous;
			}else if(sqrt($NF*$NF)<65) { /*对二个参数模糊求绝对值,默认情况认定为 经度、纬度格式*/ 
				print previous;
				print $NF;
			}
		/*此处不屏蔽错误,保留awk命令原始错误输出结果到屏幕终端,以便于查找原因!*/
		/*复现报错提示可以使用:lngandlat "" */
		}' 2>/dev/tty||echo -e "\033[41;37m输入数据解析有错误,请检查参数!\033[0m" >/dev/tty
	)

	[[ "${Coords[@]}" == "" ]] && return #有错误则直接退出!
	
	local lng="${Coords[0]}" #经度
	local lat="${Coords[1]}" #纬度
	
	printf "原始数据:\n经度:%s\n纬度:%s\n\n" "$lng" "$lat"
	printf "经纬度:\n%s,%s\n\n" $lng $lat
	printf "纬经度:\n%s,%s\n\n" $lat $lng
	#jq命令存在则同时输出格式化的JSON数据:
	[ ! -z $(type -t jq) ] && printf '{"lng":"%s","lat":"%s"}' $lng $lat|jq '.'
}

使用帮助截图:

image.png

使用效果截图:

image.png
处理复杂分隔符的情况:
image.png

Gitee代码共享:

https://gitee.com/hexiyou/shell-scripts/blob/master/lngandlat.sh