处理用户输入

目录

一、传递参数

1.1 读取参数

1.2 读取脚本名

二、跟踪参数

三、移动参数

四、处理选项

4.1 查找选项

4.1.1 处理简单选项

4.1.2 分离参数和选项

 4.1.3 处理含值的选项

五、选项标准化

5.1 使用 getopt 命令 

5.1.1 命令格式

5.1.2 在脚本中使用getopt

 5.2 使用getopts命令

六、获取用户输入

6.1 read读取 

6.2 从文件中读取


一、传递参数

向 shell 脚本传递数据的最基本方法是使用命令行参数。bash shell 会将所有的命令行参数都指派给称作位置参数的特殊变量。这也包括shell脚本名称。位置变量的名称都是标准数字,$0 对应脚本名,$1 对应第一个命令行参数,$2 对应第二个命令行参数,以此类推,直到 $9,第9个之后,必须在变量名两侧加上花括号,比如 ${10} 。

1.1 读取参数

如果需要 输入更多的命令行参数,则参数之间必须用空格分开

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat position.sh
#! /bin/bashvalue=$[ ${10} * ${11} ]
echo "第10个参数是${10}"
echo "第11个参数是${11}"
echo $value
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# ./position.sh 1 2 3 4 5 6 7 8 9 10 12
第10个参数是10
第11个参数是12
120
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

1.2 读取脚本名

 可以使用位置变量 $0 获取在命令行中运行的脚本名。

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat position.sh
#! /bin/bashvalue=$[ ${10} * ${11} ]
echo "第10个参数是${10}"
echo "第11个参数是${11}"
echo $value
echo "脚本名是$0"
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash position.sh 1 2 3 4 5 6 7 8 9 10 12
第10个参数是10
第11个参数是12
120
脚本名是position.sh
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

 但是这会有一个问题,如果使用另一个命令运行shell脚本,则命令名会和脚本名混在一起

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat position.sh
#! /bin/bashvalue=$[ ${10} * ${11} ]
echo "第10个参数是${10}"
echo "第11个参数是${11}"
echo $value
echo "脚本名是$0"
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash position.sh 1 2 3 4 5 6 7 8 9 10 12
第10个参数是10
第11个参数是12
120
脚本名是position.sh
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# ./position.sh 1 2 3 4 5 6 7 8 9 10 12
第10个参数是10
第11个参数是12
120
脚本名是./position.sh
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

 这还不是唯一的问题,如果运行脚本时使用的是绝对路径,那么位置变量 $0 就会包含整个路径

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# pwd
/root
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# ~/position.sh
/root/position.sh: line 3: *  : syntax error: operand expected (error token is "*  ")
第10个参数是
第11个参数是脚本名是/root/position.sh
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

basename 命令可以返回不包含路径的脚本名 

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat position.sh
#! /bin/bashvalue=$[ ${10} * ${11} ]
echo "第10个参数是${10}"
echo "第11个参数是${11}"
echo $value
name=$(basename $0)
echo "脚本名是$name"
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# ./position.sh 1 2 3 4 5 6 7 8 9 10 12
第10个参数是10
第11个参数是12
120
脚本名是position.sh
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

 利用此技术可以编写一个脚本,生成能标识脚本运行时间的日志信息

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat position.sh
#! /bin/bashvalue=$[ ${10} * ${11} ]
echo "第10个参数是${10}"
echo "第11个参数是${11}"
echo $value
name=$(basename $0)
echo "脚本名是$name"
echo "The $name ran at $(date)" >> ~/scripttrack.log
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# date
Fri May 10 15:42:54 CST 2024
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# ./position.sh 1 2 3 4 5 6 7 8 9 10 12
第10个参数是10
第11个参数是12
120
脚本名是position.sh
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# ll
total 12
-rwxr--r-- 1 root root  210 May 10 15:42 position.sh
drwxr-xr-x 2 root root 4096 May  9 23:36 script
-rw-r--r-- 1 root root   52 May 10 15:43 scripttrack.log
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat scripttrack.log
The position.sh ran at Fri May 10 15:43:01 CST 2024
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

二、跟踪参数

特殊变量 $# 含有脚本运行时携带的命令行参数的个数,${!#} 代表最后一个位置变量,$*$@ 各自包含了所有的命令行参数。$* 将所有参数视为一个整体,$@ 会将所有命令行参数视为同一字符串中的多个独立的单词,可以用 for 遍历所有参数。

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat params.sh
#! /bin/bash
echo
echo "\$*用法:$*"
count=1
for param in "$*"
doecho "\$* #$count = $param"count=$[ $count + 1 ]
done########
echo
echo "\$@用法:$@"
count=1
for param in "$@"
doecho "\$@ #count = $param"count=$[ $count +1 ]
done
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash params.sh 1 2 3$*用法:1 2 3
$* #1 = 1 2 3$@用法:1 2 3
$@ #count = 1
$@ #count = 2
$@ #count = 3
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

三、移动参数

shift 命令会根据命令行参数的相应位置进行移动,默认情况下会将每个位置的变量值都向左移动一个位置。因此,变量 $3 的值会移入 $2 ,变量 $2 的值会移入 $1 ,而变量 $1 的值会被删除(变量 $0 是脚本名,不会变)。这是遍历命令行参数的另一种好方法,尤其在不知道到底有多少参数的时候。

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat shift.sh
#! /bin/bash
echo
echo "shift的用法"
count=1
while [ -n "$1" ]
doecho "#$count = $1"count=$[ $count + 1 ]shift
done
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash shift.sh ab cd 12 34 dfshift的用法
#1 = ab
#2 = cd
#3 = 12
#4 = 34
#5 = df
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

四、处理选项

4.1 查找选项

在命令行中,选项紧跟在脚本名之后,就跟其他命令行参数一样 。你可以像处理命令行参数一样处理命令行选项

4.1.1 处理简单选项

 在提取单个参数时,使用  case 语句来判断某个参数是否为选项

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# vim option.sh
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat option.sh
#! /bin/bash
echo
while [ -n "$1" ]
docase "$1" in-a) echo "a是一个选项";;-b) echo "b是一个选项";;-c) echo "c是一个选项";;*) echo "$1不是一个选项";;esacshift
done
echo
exit
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash option.sh -b -a e -fb是一个选项
a是一个选项
e不是一个选项
-f不是一个选项[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

4.1.2 分离参数和选项

 经常会碰到同时使用选项和参数的情况。在Linux系统中,处理这个问题的标准做法是使用特殊字符将两者分隔,该字符告诉脚本选项何时结束,普通字符何时开始。在Linux系统中这个特殊字符是双连字符(--)。

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat option.sh
#! /bin/bash
echo
while [ -n "$1" ]
docase "$1" in-a) echo "-a是一个选项";;-b) echo "-b是一个选项";;-c) echo "-c是一个选项";;--) shiftbreak;;*) echo "$1不是一个选项";;esacshift
done
#
echo
count=1
for param in "$@"
doecho "参数#$count = $param"count=$[ $count + 1 ]
done
echo
exit
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash option.sh -b -a -d -- 1 21 6-b是一个选项
-a是一个选项
-d不是一个选项参数#1 = 1
参数#2 = 21
参数#3 = 6[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

在遇到双连字符时,脚本会用break命令跳出while循环。由于提前结束了循环,因此需要再加入另一个shift命令将双连字符移除位置变量 

 4.1.3 处理含值的选项

有些选项需要一个额外的参数值,像下面这样

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat option.sh
#! /bin/bash
echo
while [ -n "$1" ]
docase "$1" in-a) echo "-a是一个选项";;-b) echo "-b是一个选项"echo "参数$2"shift ;;-c) echo "-c是一个选项";;--) shiftbreak;;*) echo "$1不是一个选项";;esacshift
done
#
echo
count=1
for param in "$@"
doecho "参数#$count = $param"count=$[ $count + 1 ]
done
echo
exit
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash option.sh -b test -a -d -- 1 21 6-b是一个选项
参数test
-a是一个选项
-d不是一个选项参数#1 = 1
参数#2 = 21
参数#3 = 6[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

在这个例子中,case语句定义了3个要处理的选项。-b选项还需要一个额外的参数。由于要处理的选项位于 $1 ,因此额外的参数值就应该位于 $2 ,因为选项占用了两个位置,所以还需要使用 shift 命令多移动一次。 

五、选项标准化

现在shell脚本已经拥有处理命令行选项的基本能力了,但是当你想合并多个选项时,脚本就不管用了。

5.1 使用 getopt 命令 

5.1.1 命令格式

getopt 命令可以接受一系列任意形式的命令行选项和参数,并自动将其转换为适当格式。getopt 格式如下:getopt optstring parameters 

首先,在 optstring 中列出要在脚本中用到的每个命令行选项字母。然后,在每个需要参数值的选项字母后面加一个冒号。如下:

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# getopt ab:cd -a -b value -cd 1 2-a -b value -c -d -- 1 2
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

 如果 optstring 未包含制定的选项,则会输出一条错误信息,如果想忽略,可以使用 -q 选项。

5.1.2 在脚本中使用getopt

 1)可以在脚本中使用 getopt 格式化脚本所携带的任何命令行选项或参数。难点在于使用 getopt 格式化版本替换已有的命令行选项和参数。这得求助于 set 命令;

2)set 命令有个选项是双连字符(--),可以将位置变量的值替换为 set 命令所指定的值;

3)具体做法是将脚本的命令行参数传递给getopt命令,然后将getopt命令的输出传递给set命令,用getopt格式化后的命令行参数来替换原始的命令行参数,如下所示: 

set -- $(getopt -q ab:cd "$@")

 现在,位置变量原先的值会被getopt命令的输出替换掉,后者已经是格式化好的命令行参数。

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat option.sh
#! /bin/bashset -- $(getopt -q ab:cd "$@")
echo
while [ -n "$1" ]
docase "$1" in-a) echo "-a是一个选项";;-b) echo "-b是一个选项"echo "参数$2"shift ;;-c) echo "-c是一个选项";;--) shiftbreak;;*) echo "$1不是一个选项";;esacshift
done
#
echo
count=1
for param in "$@"
doecho "参数#$count = $param"count=$[ $count + 1 ]
done
echo
exit
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash option.sh -ab bvalue 12 3-a是一个选项
-b是一个选项
参数bvalue参数#1 = 12
参数#2 = 3[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

 5.2 使用getopts命令

getopt 命令存在一个问题,如下代码所示:就是不擅长处理带空格和引号的参数值。它会将空格当做参数值的分隔符,而不是根据双引号将二者当做一个参数。这是 getopts 就派上用场了。

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash option.sh -ab bvalue 12 3 -c-a是一个选项
-b是一个选项
参数bvalue
-c是一个选项参数#1 = 12
参数#2 = 3[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash option.sh -ab bvalue "12 3" -c-a是一个选项
-b是一个选项
参数bvalue
-c是一个选项参数#1 = 12
参数#2 = 3

 getopts 命令格式如下:

getopts optstring variable

1) optstring 值与 getopt 命令中使用的类似。有效的选项字母会在 optstring 中列出,如果选项要求参数,就在其后面加一个冒号。不想显示错误消息的话,可以在 optstring 之前加一个冒号。getopts 会将当前参数保存在命令行中定义的varibales中。

2)getopts 会用到两个环境变量。如果选项需要加带参数值,那么 OPTARG 环境变量保存的就是这个值。OPTIND 环境变量保存着参数列表中 getopts 正在处理的参数位置。这样在处理完当前选项之后就能继续处理其他命令行参数了。

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat getopts.sh
#! /bin/bash
echo
while getopts :ab:cd opt
docase "$opt" ina) echo "-a是选项";;b) echo "-b是一个选项"echo "-b选项的参数是$OPTARG";;c) echo "-c是一个选项";;*) echo "未知选项$opt";;esac
done
exit
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash getopts.sh -ab2-a是选项
-b是一个选项
-b选项的参数是2
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

1) 在解析命令行选项时,getopts 会移除起始的连字符,所以 case 语句中不用连字符;

2)可以在参数值中加空格;

3)可以将选项字母和参数值写在一起,两者之间不加空格;

4)在处理每个选项时,getopts 会将OPTIND 环境变量的值增1.处理完选项后,可以使用shift命令和OPTIND值来移动参数。

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat getopts.sh
#! /bin/bash
echo
while getopts :ab:cd opt
docase "$opt" ina) echo "-a是选项";;b) echo "-b是一个选项"echo "-b选项的参数是$OPTARG";;c) echo "-c是一个选项";;*) echo "未知选项$opt";;esac
done
#
shift $[ $OPTIND -1 ]
echo
count=1
for param in "$@"
doecho "参数#$count = $param"count=$[ $count + 1 ]
done
exit

六、获取用户输入

尽管命令行选项和参数是从脚本用户处获取输入的一种重要方式,但有时候脚本还需要更多

的交互性。为此,bash shell 提供了 read 命令。 

6.1 read读取 

read -p "Please enter your age:" age
read -t 5 -p "Please enter your age:" age # 超时
read -s "Enter your password:" password # 无显示输出

6.2 从文件中读取

[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# cat read.sh
#! /bin/bash
echo
count=1
cat ~/getopts.sh | while read line
doecho "#$count = $line"count=$[ $count + 1 ]
done
echo "处理完文本的所有行"
exit
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]# bash read.sh#1 = #! /bin/bash
#2 = echo
#3 = while getopts :ab:cd opt
#4 = do
#5 = case "$opt" in
#6 = a) echo "-a是选项";;
#7 = b) echo "-b是一个选项"
#8 = echo "-b选项的参数是$OPTARG";;
#9 = c) echo "-c是一个选项";;
#10 = *) echo "未知选项$opt";;
#11 = esac
#12 = done
#13 = #
#14 = shift $[ $OPTIND -1 ]
#15 = echo
#16 = count=1
#17 = for param in "$@"
#18 = do
#19 = echo "参数#$count = $param"
#20 = count=$[ $count + 1 ]
#21 = done
#22 = exit
处理完文本的所有行
[root@iZbp1ir1vzqwzkdy7mvjthZ ~]#

while 循环会持续通过read命令处理文件中的各行,直到read命令以非0退出状态码退出

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/10084.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++ 搜索二叉树

目录 1.二叉搜索树概念 2. 实现二叉搜索树 2.1. 二叉搜索树的插入 2.2查找 2.3删除节点 3.二叉树的应用(KV结构) 1.二叉搜索树概念 二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树: 若它的左子树不为…

Web界面加持!数据库备份神器,助你轻松备份数据!

使用这款带有Web界面的数据库备份神器,你可以轻松设置定时备份,确保数据安全无忧。备份结果即时通知,让你随时掌握备份状态。备份完成后,你将收到备份结果通知。无论是成功备份还是出现错误,你都能及时了解备份情况&am…

自适应熔断限流揭秘

原创 Chasen 拍码场 前言 自适应熔断与限流是在分布式系统中常用的机制,用于保护系统免受服务雪崩效应与突发流量影响。它能够根据系统的负载情况和性能指标自动调整限流策略,以确保系统能提供稳定可靠的服务,目前在业内已经有了不少的探索…

Mysql面试高频问题

MySQL中,如何定位慢查询? 可以部署运维的监控系统Skywalking ,在展示的报表中可以看到是哪一个接口比较慢,并且可以分析这个接口哪部分比较慢,这里可以看到SQL的具体的执行时间,所以可以定位是哪个sql出了问题如果&a…

代码无界,创新无限!华为云开发者日 · 广州站来了!

5月23日,2024年首场华为云开发者日HDC.Cloud Day将在广州盛大举行。这场技术派对将为开发者们带来一场无与伦比的技术盛宴。在这里,开发者们将有机会现场聆听行业专家的精彩分享,深度了解众多前沿产品的最新技术和功能,并与行业专…

Python文件转exe文件

要将Python脚本(.py 文件)转换为可执行文件(.exe 文件),你通常会使用第三方工具,如 PyInstaller。下面是一个简单的步骤说明,演示如何使用 PyInstaller 将Python脚本转换为Windows上的可执行文件…

正则多个不同的值并替换成对应的不同目标值

一、需求 87101010 替换为86411010,88101010替换为86421010,89101010替换为86431010,96101010替换为86441010,如何查找和替换一个表达式实现。 不想要在编辑器里单独查找87101010 替换为86411010,这样要操作五次&…

Sass语法介绍-运算

04 【Sass语法介绍-运算】 1.前言 运算是一种通过已知量可能的组合,获得新的量的行为。Sass 中也为我们提供了各种各样的运算,以便我们更好的处理样式代码。本节我们将学习 Sass 中的数字运算、关系运算、除法运算、颜色运算、字符串运算等等… 2.什么…

OSPF虚链路

原理概述 通常情况下,一个OSPF网络的每个非骨干区域都必须与骨干区域通过ABR路由器直接连接,非骨干区域之间的通信都需要通过骨干区域进行中转。但在现实中,可能会因为各种条件限制,导致非骨干区域和骨干区域无法直接连接&#x…

[muduo网络库]——muduo库三大核心组件之 Poller/EpollPoller类(剖析muduo网络库核心部分、设计思想)

接着上文,[muduo网络库]——muduo库三大核心组件之Channel类(剖析muduo网络库核心部分、设计思想),本章我们来学习muduo网络库中第二大核心组件Poller/EpollPoller类。 先回顾一下三大核心组件之间的关系。 接着我们进入正题。 P…

解决VScode -正在本地下载 VS Code 服务器

不知道怎么回事再次连接服务器的时候一直卡在这里了,查看输出信息发现一直卡在下载处,报错信息如图1,输出信息如图2。 1.报错信息 图1 报错信息 图2 输出信息 2.尝试 【已解决】设置SSH主机:VS Code-正在本地下载 VS Code 服务器…

javascript入门基础(一)

js是什么:是一门运行在客户端(浏览器)的编程语言,实现人机交互的效果 js组成是什么:ECMAScript和web APIs(DOM文档对象模型和BOM浏览器对象模型) console.log():控制台输出语法,程…

代码随想录算法训练营第二十五天 | 669. 修剪二叉搜索树、108.将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树

669. 修剪二叉搜索树 题目链接/文章讲解: 代码随想录 视频讲解: 你修剪的方式不对,我来给你纠正一下!| LeetCode:669. 修剪二叉搜索树_哔哩哔哩_bilibili 解题思路 在上一题的删除二叉树节点中,我们通过在…

无线收发模块家电控制实验

zkhengyang可申请加入数字音频系统研究开发交流答疑群(课题组) 当然可以先用固定电平发送,可以实现,0/1数据发送,接收。 可以使用51单片机来编码码,解码,或者任何MCU或者SOC,DSP,FPGA。 注意G…

初识指针(4)<C语言>

前言 前面的文章,已经对指针的基础概念以及运用有了初步了解,我们可以进一步探究指针比较深入的知识,下文将主要介绍:使用指针数组模拟二维数组、字符指针变量、数组指针、二维数组传参的本质、函数指针、typedef关键字等。 目录…

F. Circle Perimeter

思路&#xff1a;线性时间复杂度就可以解决&#xff0c;不用二分&#xff0c;我们枚举横坐标&#xff0c;然后看当前横坐标情况下多少个纵坐标满足条件。 代码&#xff1a; void solve(){int r;cin >> r;int y r, ans 0;for(int x 0;x < r;x ){ //枚举横坐标x&am…

只需3步,使用Stable Diffusion无限生成AI数字人视频(附安装包)

基本方法 搞一张照片&#xff0c;搞一段语音&#xff0c;合成照片和语音&#xff0c;同时让照片中的人物动起来&#xff0c;特别是头、眼睛和嘴。 语音合成 语音合成的方法很多&#xff0c;也比较成熟了&#xff0c;大家可以选择自己方便的&#xff0c;直接录音也可以&#…

Codeforces Round 944 (Div. 4) A - G

div.4只写部分题解了&#xff0c;都比较基础&#xff0c;数学偏多一点&#xff0c;几乎没有算法&#xff0c;有不懂的欢迎评论区提问&#xff01; A. My First Sorting Problem #include<bits/stdc.h> using namespace std ; typedef long long ll ; const int maxn 2…

org.hsqldb.jdbcDriver 类,导致 ClassNotFoundException 异常如何解决?

确保JDBC驱动包存在&#xff1a;检查系统是否已经安装了HSQLDB JDBC驱动。如果没有安装或驱动没有正确放置在类路径中&#xff0c;需要下载并添加它。你可以从 HSQLDB官网 下载JDBC驱动包。 添加JDBC驱动到类路径&#xff1a;将下载的HSQLDB JDBC驱动&#xff08;通常是一个JA…

2024年,Web开发新趋势!

随着我们迈入新的一年&#xff0c;现在正是审视2024年网页开发领域开始流行哪些趋势的绝佳时机。回顾2023年的一系列更新&#xff0c;以下是来年一些热门话题的概览。 自主托管有回归的趋势 近些年&#xff0c;自主托管一直是网页开发者和公司托管其应用程序的默认方式。开发…