shell基础用法

shell基础知识

shell中的多行注释

:<<EOF
read
echo $REPLY  # read不指定变量,则默认写入$REPLY
EOF
# :<<EOF ...EOF  多行注释,EOF可以替换为!# 等

文件目录和执行目录

echo '$0='$0  # ./demo.sh
echo '$0的realpath='$(realpath $0)      # 完整路径
echo '$0的父目录='$(dirname $(realpath $0))    # 文件的父目录echo "BASH_SOURCE="${BASH_SOURCE}  # ./demo.sh
echo "BASH_SOURCE的realpath="$(realpath ${BASH_SOURCE}) # 完整路径echo '文件执行目录:'`pwd`  # 执行目录

当使用bash或./执行时,$0和${BASH_SOURCE}是一样的,但是通过.或source命令执行脚本时,$0就变为了/bin/bash ,无法达到想要的效果;

  • BASH_SOURCE
    如果你有一个脚本 script1.sh 调用了 script2.sh,而 script2.sh 又调用了 script3.sh,那么在 script3.sh 中,BASH_SOURCE 数组将包含以下路径:
    BASH_SOURCE[0] 将是 script3.sh 的路径。
    BASH_SOURCE[1] 将是 script2.sh 的路径。
    BASH_SOURCE[2] 将是 script1.sh 的路径。

shell变量

变量赋值

var=‘hello word’
unset var # 删除变量,不能删除只读变量
readonly var=89 # 只读变量
数组定义

var_array=(value1 value2 value3)    # 普通数组
declare -A  var_url_array=(['baidu']='www.baidu.com' ['taobao']='www.taobao.com' ['jd']='jingdong.com')  # 关联数组
echo ${var_array}  # value1
echo ${var_url_array}  # 空
echo ${var_array[1]}  # value2
echo ${var_url_array["taobao"]}  # www.baidu.com
echo ${var_array[*]}  # value1 value2 value3
echo ${var_array[@]}  # value1 value2 value3
echo ${var_url_array[*]}  # www.baidu.com jingdong.com www.taobao.com
echo ${var_url_array[@]}  # www.baidu.com jingdong.com www.taobao.com
echo ${#var_array[1]}  # 6  计算数组下标元素的长度
echo ${#var_array[*]}  # 3  计算数组的长度
echo ${#var_url_array[*]}  # 3
echo ${#var_url_array["taobao"]}  # 14
echo ${!var_array[*]}  # 0 1 2  获取数组的key
echo ${!var_url_array[*]}  # baidu jd taobao  获取数组的key
echo ${var_array[@]:0:2}  # value1 value2 切片访问,从0下标开始,长度是2
echo ${var_url_array[@]:1:2} # www.baidu.com jingdong.com
echo ${var_array[@]/value/num} # num1 num2 num3 数组内容替换
echo ${var_url_array[@]/www/} # .baidu.com jingdong.com .taobao.com
# 以上4条命令,同样可以指定下标,就是对数组下标元素操作
unset var_array[0]
echo ${var_array[*]}  # value2 value3
unset var_url_array["taobao"]
echo ${var_url_array[*]}  # www.baidu.com jingdong.com
# 需要注意的是,var_array删除下标0之后,并不会重置下标
echo ${!var_array[*]}  # 1 2
var_array[7]=78
echo ${!var_array[*]}  # 1 2 7
echo ${var_array[*]}  # value2 value3 78
var_array+=(10 20)
echo ${var_array[*]}  # value2 value3 78 10 20
var_array=(${var_array[*]} 1 2 3)
echo ${var_array[*]}  # value2 value3 78 10 20 1 2 3
unset var_array # 删除整个数组 
for i in ${!var_url_array[*]} ; do echo $i; done  # 遍历数组的key:baidu \n jd
for i in ${var_url_array[*]} ; do echo $i; done  # 遍历数组的值:www.baidu.com \n jingdong.com
test1(){echo "接收到的参数列表:$@"newarr=($@)echo "新数组的值为:${newarr[*]}"return $newarr   # 不建议这样写,其实只能返回数组第一个元素的整数值
}
# shell脚本调用也不能传入数组,所以建议直接在脚本中定义数组
arr=(10 2 3)
test1 ${arr[*]}
echo $?#接收到的参数列表:10 2 3
#新数组的值为:10 2 3
#10

shell内置变量

$MACHTYPE:机器类型
$OSTYPE:操作系统类型
$HOSTNAME:当前主机名
$HOME:当前用户家目录
$USER:当前用户名
$UID:当前用户ID
$SHELL:当前shell的路径
$PWD:当前目录
$IFS:字段分隔符
$RANDOM:随机数
$SECONDS:shell 脚本启动的秒数
$FUNCNAME:当前函数的名称

特殊参数变量

$0 --脚本名称
$n --位置参数
$# --参数个数
$* --所有参数
$@ --所有参数;
不加双引号时,$* 、$@相同,都是独立字符串;加上双引号时,$*是将所有参数作为整个字符串,而$@是将每个参数作为独立字符串;

特殊状态变量

$? – 退出状态码
$! – 上一个后台运行的程序的进程id
$$ – 当前脚本的pid
$_ – 上一个命令的最后一个参数

shell内置命令

date
date +'%Y-%m-%d %H:%M:%S'
# 2024-11-18 19:14:02
tee
# 同时写入标准输出和文件。
echo "hello"|tee a.txt  # 写入a.txt文件,同时也会到标准输出,-a参数代表追加;
echo "hello world"|tee a.txt b.txt c.txt  # 同时重定向到多个文件;
lsblk|tee a.txt > /dev/null # 这样可以不到标准输出
lsblk 2>&1|tee a.txt # 将标准错误也写入a.txt文件;
tee -i a.txt    # 从键盘获取输入写入文件,-i不会被ctrl+c打断,可用ctrl+d打断;
echo和printf
echo -e "\n hello" # 支持转义字符
echo -n "hello"  # 不换行
printf "hello \t world" # printf默认支持转义,默认不换行
printf "%d %s %.2f" 24 hello 4.5678  # 24 hello 4.57
exec 和 eval
eval "pwd"  # eval可以将字符串作为命令执行
exec date   # exec 执行命令后退出,相当于自动执行一次exit
cat 重定向
cat > b.txt <<EOF
first
second
third
EOF
local
function func(){
local name='inside var'
}
# 默认情况下,函数内部定义的变量也属于全局变量。但是只要在变量名前加local关键字,该变量就会变成局部变量。
trap

用于捕获和处理信号:

#!/bin/bash
# 定义错误处理函数,将错误信息记录到日志文件
handle_error() {echo "$(date): $BASH_COMMAND failed with exit code $?" 
}
# 设置 ERR 信号处理函数
trap handle_error ERR EXIT
# 其他代码逻辑
ls none.txt
# 运行 ./demo.sh 
# ls: 无法访问'none.txt': 没有那个文件或目录
# 2024年 11月 22日 星期五 21:30:53 CST: ls none.txt failed with exit code 0
# 2024年 11月 22日 星期五 21:30:53 CST: ls none.txt failed with exit code 0

捕获了ERR信号,同时也捕获了EXIT信号,所以打印了2遍;这里打印的退出码是0,其实是因为执行$(date)命令成功。

#!/bin/bash
trap cleanup INTcleanup() {echo "Caught interrupt signal. Cleaning up..."
}echo "Press Ctrl+C to interrupt me!"
while true; do sleep 1; done
declare

declare [+/-] [选项] 变量名, 其中,+表示取消设置,-表示设置。
‌声明变量类型‌:在默认情况下,Linux中的变量是弱类型,即默认都是字符串类型。使用declare可以声明变量的类型,例如使用-i选项将变量作为整数处理。参考

declare -i data1 data2 res
data1=10
data2=20
res=$data1+$data2
echo $res  # 30   如果不声明,那结果是10+20
declare +i data  # 去掉整数属性
declare -r name='test'  # 声明只读变量
name='zyy'  # 报错bash: name: 只读变量
readonly a=1   # 也是定义只读变量
declare -a arr=(1 2)    
#对于索引数组来说,declare是否声明似乎并无差别(索引数组顺序固定)
declare -A b=(["apple"]=1 ["boy"]=2 ["cat"]=3)    
# 但对于关联数组,则必须使用declare -A声明(关联数组顺序不定)
demo(){echo "demo function"
}
declare -f demo
# declare -f 函数名 显示函数的名称和定义代码; -F是只显示函数名称

declare -x APP=“mail” -x代表声明环境变量,完全等同于export。

export

直接执行export命令,会显示所有环境变量;
export APP=“mail” 其实相当于 declare -x APP=“mail”,设置后,在当前shell及其子shell中共用该环境变量。直接定义变量a=1,是不能在子shell中共用的。

set
set -xe
echo $var
ls none.txt
echo hello
# 运行./demo.sh 
# ++ echo# ++ ls none.txt
# ls: 无法访问'none.txt': 没有那个文件或目录

-x 代表打印每一行的执行结果;
-e代表遇到错误就退出;bash默认遇到错误会接着执行;
还有-u,如果指定了未定义的变量,会报错退出;

set first second third
echo $1
# 运行./demo.sh param1
# first
# set 会重新赋值给位置参数
env

env --显示所有环境变量,包括自己脚本中编写的export的变量;
export --显示所有环境变量,和env查询出的变量一致;

declare --显示所有变量,包括全局变量(包括环境变量)、局部变量;
set --显示所有变量,和declare查询出的变量一致;

ps --forest

ps -ef --forest      --展示父进程和子进程的关系

()开启子shell

利用括号,开启子she11的理念,以及检查,在she11脚本开发中,经常会用子she11进行多进程的处理,提高程序并发执行效率。

echo $BASH_SUBSHELL
0  # 0表示当前shell
(pwd;echo $BASH_SUBSHELL)
/home/xxx/Desktop
1  # 1 嵌套一层shell
(pwd;(echo $BASH_SUBSHELL))
/home/xxx/Desktop
2  # 2 嵌套2层shell
{}  # 代表当前shell执行
tr 转换和压缩
echo 1234abcd|tr 12 34   # 3434abcd
echo 1234abcd|tr [0-9] d  # ddddabcd
echo 1234abcd|tr [a-z] [A-Z]  # 1234ABCD
echo 1234abcd|tr "1234" "[A*]C"  # AAACabcd
echo 1234abcd|tr "1234" "AC" # ACCCabcd
echo 1234abcd|tr -t "1234" "AC"  # AC34abcd
# 以上这种由set1转换为set2,是一一对应的,如果个数对不上,默认重复set2的最后一个字符。加上-t,可以避免重复,而不替换;echo 1234abcd|tr -d [:digit:]  # abcd
echo 1234abcd|tr -dc [:digit:] # 1234
# -d 删除匹配的字符,-dc 保留匹配的字符(相当于反选)并且-c删除了末尾的换行符;echo aaa123bbbb123|tr -s 'ab'  # a123b123
echo aaa123bbbb123|tr -s '123' 'ccc'  # aaacbbbbc
# -s 代表压缩,第一个命令是对a和b字符压缩,第二个命令是先将123替换为ccc,再对ccc字符压缩;
mkdir
  1. mkdir a/{b, c} 指定目录下同时创建多个文件夹
  2. mkdir -p xx/xxx 代表无父目录可直接创建
解压和压缩
  1. tar czf a.tar.gz 1.txt 2.txt
  2. tar xzf a.tar.gz -C 目录
    都可以加-v,显示具体过程
sort和uniq
cat file|sort  # 默认按ASCII码排序
cat file|sort -u  # 去重排序
echo -e '2\n1\n-9'|sort -n  # 按数字排序
sort -r # 反向排序
sort -k2 file # 按第二列排序
uniq  # 按行去重,只会对相邻重复的行去重
uniq -d file # 列出重复的行
uniq -u file # 列出不重复的行
uniq -c # 统计行的重复次数
# 实现对文件中获取重复行的重复次数
sort example.txt | uniq -ic  # -i 不区分大小写
curl
curl -s -I http://example.com | grep HTTP
# -s 安静模式,不打印错误信息、进度信息、网速等;
# -I 发送一个 HEAD 请求,仅请求页面的 HTTP 头部信息,当仅需检查状态码时(HTTP/1.1 200 OK)可使用此命令。
shell内置和外置命令

内置命令通过compgen -b 查看;
“type + 命令” 可以判断是内置还是外置命令;
内置命令:在系统启动时就加载入内存,常驻内存,执行效率更高,但是占用资源,如pwd;
外置命令:系统需要从硬盘中读取程序文件,再读入内存加载,一定会开启子进程执行;

shell循环

# 循环读取test.txt中的每一行
while read x
doecho input is $x
done<test.txt
until [ ${i:=0} -gt 10 ]
dolet i++echo $i
done  # 1 2 ... 10 11

while循环在条件为真时继续执行,条件为假时停止。
until循环在条件为假时继续执行,条件为真时停止。

for((i=0;i<5;i++));do echo $i;done
for i in {0..4};do echo $i;done
for i in $(seq 0 4);do echo $i;done
for i in 0 1 2 3 4;do echo $i;done
# 0 1 2 3 4
num=3
if (($num==3));then echo succ;elif (($num>3));then echo bigger;else echo smaller;fi
# succ

shell计算

普通计算
res=$((10+67))
num=10
let num=num+10
echo $num  # 20
expr 10 + 9  # 19
# expr支持模式匹配,判断是否以.jpg为后缀,返回非0就是符合要求的;冒号代表计算匹配的字符长度
expr taohua.jpg : ".*\.jpg"  # 10
expr taohua.jpg : ".*"   # 10
expr length taohua.jpg   # 10
# bc命令,计算1+2+3+..+1000
echo {1..1000}|tr " " "+"|bc
# []计算
echo $[10-9]  # 1

需要注意的是,双小括号、let、expr、中括号只能进行整数计算,而bc可以支持小数计算。awk也可用于计算,虽然不是专门做计算的。

echo 2.4 4.5|awk '{print $1+$2}'  # 6.9

shell应用

统计字符串长度
# 方式一:
name="Katherine Pierce"
echo ${#name}# 方式二:
echo $name|wc -L# 方式三:
expr length "$name"# 方式四:
echo $name|awk '{print length($0)}'
统计命令执行时间
time for i in {1..3000};do str1=`seq -s ":" 100`;echo ${#str1}>/dev/null;done
# real    0m5.143s   实际执行时间
# user    0m3.506s   用户态执行时间
# sys     0m1.692s   内核态执行时间

一般shell内置命令和语法,执行效率最高,因为底层都是用C实现,尽量减少管道符实现;

获取目录下的所有文件的完整路径
# demo.sh
#!/bin/bashfunction get_file(){for file in `ls $1`dodir_file=$1/$fileif [ -d $dir_file ]thenget_file $dir_fileelseecho $dir_filefidone
}
get_file $1# ./demo.sh /home/uos  打印目录下所有的文件
字符串截取
name="Katherine Pierce"
echo ${name:2}  # therine Pierce
echo ${name:2:3}  # the  截取3个字符
echo ${name:2:-3} # therine Pie 如果是负数,则截取到-3的位置处
name="Katherine therce"
echo ${name#*th}  # erine therce 左边开始匹配,删除最短匹配
echo ${name##*th}  ## erce  左边开始匹配,删除最长匹配
echo ${name%th*}  ## Katherine  右边开始匹配,删除最短匹配
echo ${name%%th*}  ## Ka  右边开始匹配,删除最长匹配
echo ${name/th/boy}  ## 仅替换第一个,Kaboyerine therce
echo ${name//th/boy}  ## 替换所有匹配,Kaboyerine boyerce
# cut命令也可用于截取字符
echo $name|cut -c 3-5  # the  -c按字符位置切割
扩展变量
# 如果param变量为空,则word赋给$res,不会给$param;注意如果param不为空,则不进行相关处理;
res=${param:-word}
# 这里的冒号可以省略,有时候看到这样的写法也是可以的: ${BASE_SOURCE-$0}# 如果param变量为空,则word赋给$res和$param;注意如果param不为空,则不进行相关处理;
res=${param:=word}# 如果param变量为空,word作为stderr错误输出,否则输出变量值 ${param:?word}
${age:?无变量错误}
# bash: age: 无变量错误# 如果param变量为空,什么都不做,否则word返回 ${param:+word}
age=10
echo ${age:+jk}
# jk# 按目录查找文件,这样实现避免dir_path变量未赋值时报错
find ${dir_path:-./} -name "*.png"

待补充

参考

  1. shell课程;
  2. shell数组

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

制造业数据集成案例分享:3小时内实现MySQL到MySQL数据对接

ZZ刷新生产用料清单四化库存-制造一处-3小时&#xff1a;MySQL到MySQL数据集成案例分享 在现代制造业中&#xff0c;实时、准确的数据流动是确保生产效率和资源优化的关键。本文将分享一个实际运行的系统对接集成案例——“ZZ刷新生产用料清单四化库存-制造一处-3小时”&#…

OpenCV 图像基本操作

OpenCV快速通关 第一章&#xff1a;OpenCV 图像基本操作 第二章&#xff1a;OpenCV 图像基本操作 OpenCV 图像基本操作 OpenCV快速通关第二章&#xff1a;OpenCV 图像基本操作一、相关结构体与函数介绍&#xff08;一&#xff09;cv::Mat 结构体&#xff08;二&#xff09;cv:…

雨晨 2610(2)0.2510 Windows 11 24H2 Iot 企业版 LTSC 2024 极简 2in1

文件: 雨晨 2610(2)0.2510 Windows 11 24H2 Iot 企业版 LTSC 2024 极简 2in1 install.esd 索引: 1 名称: Windows 11 IoT 企业版 LTSC 极简 26100.2510 描述: Windows 11 IoT 企业版 LTSC 极简 26100.2510 By YCDISM RTM 2025 24-12-07 大小: 8,176,452,990 个字节 索引: 2 …

PHP保存base64编码图片,图片有一部分是灰色块儿,原因和解决办法

文章目录 场景原因解决方案完整的代码前端代码php代码 场景 我有个需求&#xff0c;移动端h5上传多张的图片。用input file可以上传多张&#xff0c;但是现在照片体积越来越大&#xff0c;同时上传多张会因为体积过大&#xff0c;导致上传失败。如果是小程序会好很多&#xff…

【CSP CCF记录】202212-2第28次认证 训练计划

题目 样例1输入 10 5 0 0 0 0 0 1 2 3 2 10 样例1输出 1 1 1 1 1 10 9 8 9 1 样例1解释 五项科目间没有依赖关系&#xff0c;都可以从第 1 天就开始训练。 10天时间恰好可以完成所有科目的训练。其中科目 1 耗时仅 1天&#xff0c;所以最晚可以拖延到第 10 天再开始训练&…

gitee

Git 是一个开源的 [ 分布式 ][ 版本控制系统 ] &#xff0c;用于敏捷高效地 处理任何或小或大的项目 Git 非常容易学习&#xff0c;低植入&#xff0c;高性能。因为拥有轻量的本地分支&#xff0c;易用的暂存区&#xff0c;和多工作流的特点&#xff0c;它超越了类似Subversio…

Spring——SpringBean初始接口

摘要 本文详细介绍了Spring框架中SpringBean的初始化接口和注解&#xff0c;包括BeanPostProcessor接口、InitializingBean接口和PostConstruct注解。文章解释了这些接口和注解的原理、作用、适用场景&#xff0c;并提供了示例代码。最后&#xff0c;对比了不同SpringBean初始…

「嵌入式系统设计与实现」书评:学习一个STM32的案例

本文最早发表于电子发烧友论坛&#xff1a;【新提醒】【「嵌入式系统设计与实现」阅读体验】 学习一个STM32的案例 - 发烧友官方/活动 - 电子技术论坛 - 广受欢迎的专业电子论坛!https://bbs.elecfans.com/jishu_2467617_1_1.html 感谢电子发烧友论坛和电子工业出版社的赠书。 …

操作系统——大容量存储结构

笔记内容及图片整理自XJTUSE “操作系统” 课程ppt&#xff0c;仅供学习交流使用&#xff0c;谢谢。 大容量存储结构概述 磁盘 磁盘为现代计算机系统提供大量外存。每个盘片为平的圆状&#xff08;类似CD&#xff09;&#xff0c;普通盘片直径为4.5~9.0厘米。盘片的两面都涂着…

Redis从入门到进阶(总结)

以下内容均以CentOS7为背景。 一、Redis安装及启动 mysql&#xff08;读&#xff1a;2000/s&#xff1b;写&#xff1a;600/s&#xff09; redis&#xff08;读&#xff1a;10w/s&#xff1b;写&#xff1a;8w/s&#xff09;通过官方给出的数据单机并发可以达到10w/s&#xf…

Java进阶(注解,设计模式,对象克隆)

Java进阶(注解&#xff0c;设计模式&#xff0c;对象克隆) 一. 注解 1.1 什么是注解 java中注解(Annotation)&#xff0c;又称java标注&#xff0c;是一种特殊的注释 可以添加在包&#xff0c;类&#xff0c;成员变量&#xff0c;方法&#xff0c;参数等内容上 注解会随同…

使用 Gin 框架构建 RESTful 博客 API

使用 Gin 框架构建 RESTful 博客 API 引言 在现代 Web 开发中&#xff0c;RESTful API 是一种非常流行的设计风格&#xff0c;它通过 HTTP 协议与客户端进行通信&#xff0c;提供了灵活且易于扩展的接口。Go 语言以其高效的并发处理能力和简洁的语法&#xff0c;成为了构建高…

Leecode刷题C语言之骑士在棋盘上的概率

执行结果:通过 执行用时和内存消耗如下&#xff1a; 代码如下&#xff1a; static int dirs[8][2] {{-2, -1}, {-2, 1}, {2, -1}, {2, 1}, {-1, -2}, {-1, 2}, {1, -2}, {1, 2}};double knightProbability(int n, int k, int row, int column){double dp[200][30][30];mem…

21. C++STL 7(8000字详解list及其迭代器的模拟实现)

⭐本篇重点&#xff1a;STL中的list及其迭代器的模拟实现和测试 ⭐本篇代码&#xff1a;c学习 橘子真甜/c-learning-of-yzc - 码云 - 开源中国 (gitee.com) 目录 一. list的节点 二. list的迭代器 2.1 迭代器框架 2.2 迭代器实现 三. list的实现 3.1 list的构造函数 3.…

Docker打包SpringBoot项目

一、项目打成jar包 在进行docker打包之前&#xff0c;先确定一下&#xff0c;项目能够正常的打成JAR包&#xff0c;并且启动之后能够正常的访问。这一步看似是可有可无&#xff0c;但是能避免后期的一些无厘头问题。 二、Dockerfile 项目打包成功之后&#xff0c;需要编写Doc…

零基础学鸿蒙开发--第九篇--网络请求

12. ⽹络请求 鸿蒙系统提供了 http 模块 ⽤于发送 http 请求&#xff0c;另外&#xff0c; OpenHarmony社区基于该模块将前端开发中常⽤的⽹络请 求库 axios 移植到了鸿蒙系统&#xff0c;因此我们也可以在鸿蒙系统中使⽤ axios 发送 http 请求&#xff0c;下⾯重点为⼤家介绍…

133.WEB渗透测试-信息收集-小程序、app(4)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;132.WEB渗透测试-信息收集-小程序、app&#xff08;3&#xff09; 输入命令&#xff1a;…

Pointnet++改进71:添加LFE模块|高效长距离注意力网络

简介:1.该教程提供大量的首发改进的方式,降低上手难度,多种结构改进,助力寻找创新点!2.本篇文章对Pointnet++特征提取模块进行改进,加入LFE模块,提升性能。3.专栏持续更新,紧随最新的研究内容。 目录 1.理论介绍 2.修改步骤 2.1 步骤一 2.2 步骤二 2.3 步骤三 1.理…

Android仿美团左右联动购物列表

Android仿美团左右联动购物列表 左右联动购物列表&#xff0c;不难。 一、思路&#xff1a; 两个RecycleView 二、效果图&#xff1a; 三、关键代码&#xff1a; public class MainActivity extends AppCompatActivity {private RecyclerView rl_left;private RecyclerVie…

Mitel MiCollab 企业协作平台 任意文件读取漏洞复现(CVE-2024-41713)

0x01 产品简介 Mitel MiCollab是加拿大Mitel(敏迪)公司推出的一款企业级协作平台,旨在为企业提供统一、高效、安全的通信与协作解决方案。通过该平台,员工可以在任何时间、任何地点,使用任何设备,实现即时通信、语音通话、视频会议、文件共享等功能,从而提升工作效率和…