一.shell基本知识

news2025/2/6 3:11:53

目录

1.1为什么学习和使用Shell编程

1.2什么是Shell

1.2.1 shell的起源

1.2.2shell的功能

1.3shell的分类

1.4作为程序设计的语言一—shell

1.5如何学好shell

1.6shell脚本的基本元素

1.7 shell脚本编写规范

1.8 shell脚本的执行方式

1.9 执行脚本的方法

1.10 shell脚本的退出状态


1.1为什么学习和使用Shell编程

对于一个合格的系统管理员来说,学习和掌握Shell编程是非常重要的。通过编程,可以在很大程度上简化日常的维护工作,使得管理员从简单的重复劳动中解脱出来。

shell程序的特点:

1、简单易学

2、解释性语言,不需要编译即可执行

1.2什么是Shell

在学习Shell编程之前,必须弄清楚什么是Shell。为了能够使读者在学习具体的Shell编程之前对Shell有个基本的了解,本节将对Shell进行概括性的介绍,包括Shell的起源和功能。

1.2.1 shell的起源

(1)1964年,美国AT&T公司的贝尔实验室、麻省理工学院及美国通用电气公司共同参与开始研发一套可以安装在大型主机上的多用户、多任务的操作系统,该操作系统的名称为Multics。

(2)1970年,丹尼斯·里奇和汤普逊启动了另外一个新的多用户、多任务的操作系统的项目,他们把这个项目称之为UNICS。

(3)1973年,使用C语言重新编写了Unix。通过这次编写,使得Unix得以移植到其他的小型机上面。

(4)1979年,第一个重要的标准UNIX Shell在Unix的第7版中推出,并以作者史蒂夫·伯恩(Stephen Bourne)的名字命名,叫做Bourne Shell,简称为sh。

(5)20世纪70年代末,C Shell作为2BSD UNIX的一部分发布,简称csh。

(6)之后又出现了许多其他的Shell程序,主要包括Tenex C Shell (tcsh) , Korn Shell(ksh)以及GNU Bourne-Again shell (bash)

1.2.2shell的功能

shell又称命令解释器,它能识别用户输入的各种命令,并传递给操作系统。它的作用类似于Windows操作系统中的命令行,但是,Shell的 功能远比命令行强大的多。在UNIX或者localhost中,Shell既是用户交互的界面,也是控制系统的脚本语言。

1.3shell的分类

Bourne Shell:标识为sh,该Shell由Steve Bourne在贝尔实验室时编写。在许多Unix系统中,该Shell是root用户的默认的Shell。

Bourne-Again Shell:标识为bash,该Shell由Brian Fox在1987年编写,是绝大多数localhost发行版的默认的Shell。

Korn Shell:标识为ksh,该Shell由贝尔实验室的David Korn在二十世纪八十年代早期编写。它完全向上兼容Bourne Shell并包含了C Shell 的很多特性。

c Shell:标识为csh,该Shell由Bill Joy在BSD系统上开发。由于其语法类似于C语言,因此称为C Shell。

查看当前系统支持的shell

[root@server ~]# cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash

查看当前系统默认shell

[root@server ~]#  echo $SHELL
/bin/bash

1.4作为程序设计的语言一—shell

Shell不仅仅是充当用户与UNIX或者localhost交互的角色,还可以作为一种程序设计语言来使用。通过Shell编程,可以实现许多非常实用的功能,提高系统管理的自动化水平。

如果有一系列经常需要使用的命令,把它存储在一个文件里,shell可以读取这个文件并顺序执行其中的命令,我们把这样的文件就叫shell脚本。shell脚本按行解释文件里的命令。

1.5如何学好shell

学好shell编程基础知识:

1.熟练使用vi (vim)编辑器

⒉.熟练掌握Linux基本命令

3.熟练掌握文本三剑客工具(grep、 sed、awk)

4熟悉常用服务器部署、优化、日志及排错

如何学好shell编程:

1、掌握Shell脚本基本语法

2、形成自己的脚本开发风格

3、从简单做起,简单判断,简单循环

4、多模仿,多参考资料练习,多思考

5、学会分析问题,逐渐形成编程思维

6、编程变量名字要规范,采用驼峰语法表示

7、不要拿来主义,特别是新手

什么是驼峰语法?

骆驼式命名法就是当变量名或函数名是由一个或多个单词连结在一起,而构成的唯一识别字时,第一个单词以小写字母开始;从第二个单词开始以后的每个单词的首字母都采用大写字母,例如: myFirstName.myLastName,这样的变量名看上去就像骆驼峰一样此起彼伏,故得名。

除了驼峰命名法,另外还有匈牙利命名法。基本原则是:变量名=属性+类型+对象描述。匈牙利命名法关键是:标识符的名字以一个或者多个小写字母开头作为前缀;前缀之后的是首字母大写的一个单词或多个单词组合,该单词要指明变量的用途。比如m _lpszStr,表示指向一个以O字符结尾的字符串的长指针成员变量。

另外,有些程序员喜欢用下划线。比如file_name。

1.6shell脚本的基本元素

对于一个基本的Shell程序来说,应该拥有以下基本元素:

1.声明:声明用哪个命令解释器来解释并执行当前脚本文件中的语句,一般写的解释器为# ! / bin/bash。

# ! / bin/bash

⒉.命令:可执行语句,实现程序的功能。

3.注释:说明某些代码的功能,通过在代码中增加注释可以提高程序的可读性。

(1)单行注释:#开头的一整行都是注释,例如:

#comment1
#comment2
#comment3
...

(2)多行注释,使用冒号“:"配合here document可实现多行注释,例如:

:<<'xxxx'
comment1
comment2
comment3
……
xxxx

xxxx 可以是字符或数字,单引号可以不加,但以防出现莫名其妙的意外发生,比如发生字符扩展,命令替换

4.赋予rx的权限

1.7 shell脚本编写规范

(1)脚本文件名应见名知意,例如backup_mysql.sh

(2)文件开头指定脚本解释器# !/bin/sh或# !/ bin/ bash

(3)开头加版本特权等信息

# Date:创建日期
# Author:作者
# Mail:联系方式
# Function:功能
# Version:版本

(4)脚本中尽量不要用中文注释

别吝啬添加注释,必要的注释方便自己别人理解脚本逻辑和功能;

尽量用英文注释,防止本机或切换系统环境后中文乱码的困扰;

单行注释,可以放在代码行的尾部或代码行的上部;

多行注释,用于注解复杂的功能说明,可以放在程序体中,也可以放在代码块的开始部分。

(5)多使用内部命令

常用的内部命令有: echo、eval、exec、export、read、shift、exit

1.echo是用于终端打印的基本命令,默认情况下,echo在每次调用后会添加一个换行符

[root@server ~]# echo hehe
hehe
[root@server ~]# echo haha
haha
[root@server ~]# echo "Welcome to bash"
Welcome to bash
[root@server ~]# echo 'Welcome to bash'
Welcome to bash

上面的方法看起来效果一样,但是在某些场合会得到不一样的结果

[root@kittod ~]# echo "the current directory is `pwd`"
the current directory is /root
[root@kittod ~]# echo 'the current directory is `pwd`'
the current directory is `pwd`
[root@kittod ~]# echo "hehe;hehe"
hehe;hehe
[root@kittod ~]# echo hehe;hehe
hehe
-bash: hehe: command not found

echo参数

-n 不换行输出内容

-e 解析转义字符

\n 换行

\r 回车

\t 制表符

\b 退格

\v 纵向制表符

示例

[root@kittod ~]# echo -n i have a cat
i have a cat[root@kittod ~]#
[root@kittod ~]# echo -e i\thave\ta\tcat
ithavetatcat
[root@kittod ~]# echo -e "i\thave\ta\tcat"
i have a cat
[root@kittod ~]# echo "1 2 3"
1 2 3
[root@kittod ~]# echo -e "1\t2\t3"
1 	2 	3
[root@kittod ~]# echo -e "1 2 3"
1 2 3

#设置字体颜色
[root@kittod ~]# echo -e "\e[1;31m This is red test \e[0m"
 This is red test
 
\e[1;31m 将颜色设置为红色, \e[0m 将颜色重置,使用时只需要更换颜色代码即可
颜色代码
重置 0
黑色 30
红色 31
绿色 32
黄色 33
蓝色 34
洋红 35
青色 36
白色 37

#设置背景颜色
[root@kittod ~]# echo -e "\e[1;42m This is red test bg \e[0m"
This is red test bg

颜色代码
重置 0
黑色 40
红色 41
绿色 42
黄色 43
蓝色 44
洋红 45
青色 46
白色 47

2.eval

命令格式:eval args

功能:当shell程序执行到eval语句时,shell读入参数args,并将它们组合成一个新的命令,然后执行。

[root@server ~]# a='shuju;head -1 /etc/passwd'
[root@server ~]# echo $a
shuju;head -1 /etc/passwd
[root@server ~]# eval echo $a
shuju
root:x:0:0:root:/root:/bin/bash

3.exec命令能够在不创建新的子进程的前提下,转去执行指定的命令,当指定的命令执行完毕后,该进程就终止了。

4.export设置或者显示环境变量

[root@kittod ~]# mingzi=hehe
[root@kittod ~]# echo $mingzi
hehe

[root@kittod ~]# bash
[root@kittod ~]# echo $mingzi
[root@kittod ~]# exit
exit

[root@kittod ~]# export mingzi
[root@kittod ~]# bash
[root@kittod ~]# echo $mingzi
hehe

此变量只在当前终端使用,切换其他终端则不会有该变量,想要其他终端有该变量则要把命令写在文件中。

vim /etc/bashrc

5.read 命令可从标准输入读取字符串等信息,传给shell程序内部定义的变量。

read 是一个重要的 bash 命令,用于从键盘或标准输入读取文本,我们可以使用 read 命令以交互形式读取来自用户的输入,不过 read 能做的远不止这些通常我们按下回车键表示命令输入完成,但是很特殊情况下,我们需要基于字符数或者特定字符来表示命令输入完成

-p prompt:设置提示信息

-t timeout:设置输入等待时间,单位默认为秒

[root@kittod ~]# read -t 10 -p "please input your name:" name
please input your name:hehe
[root@kittod ~]# echo $name
hehe
[root@kittod ~]# echo -n "please input your name:"read name1 name2
please input your name:: read name1 name2[root@kittod ~]#
[root@kittod ~]#
[root@kittod ~]#
[root@kittod ~]# echo -n "please input your name:"; read name1 name2
please input your name:hehe haha
[root@kittod ~]# echo $name1
hehe
[root@kittod ~]# echo $name2
haha

#读取指定个数字符
[root@kittod ~]# read -n 3 var1
sss[root@kittod ~]#
[root@kittod ~]# echo $var1
sss

#小案例
[root@kittod ~]# cat read01.sh
#!/bin/bash
#read yes or no

read -n1 -p "Do you want to continue [Y/N]?" answer
case $answer in
        Y|y)
                echo "fine,contine";;
        N|n)    
                echo "ok,good bye";;
        *)      
                echo "error choice";;
esac            
exit 0


[root@kittod ~]# bash read01.sh
Do you want to continue [Y/N]?yfine,continue
[root@kittod ~]# bash read01.sh
Do you want to continue [Y/N]?nok,good bye
[root@kittod ~]# bash read01.sh
Do you want to continue [Y/N]?ferror choice

#输入不回显
[root@kittod ~]# read -s var
[root@kittod ~]# echo $var
heheheheheheh

#用定界符输入
[root@kittod ~]# read -d ":" haha
heheheheeheheheh:[root@kittod ~]#
[root@kittod ~]# echo $haha
heheheheeheheheh

6.shift,在程序中每使用一次shift语句,都会使所有的位置参数依次向左移动一个位置,并使位置参数$#减1,直到减到0为止。

7.exit,退出shell程序。在exit之后可以有选择地指定一个数作为返回状态

(6)没有必要使用cat命令

eg:cat /etc/passwd | grep guru
使用以下方式即可
eg:grep guru etc/passwd

(7)代码缩进

#!/bin/bash
 
i=1
while [ $i -le 10 ]
do
 if [ $i -le 9 ]
 then
   username=user0$i
 else
   username=user$i
 fi
 ! id $username &>/dev/null && {
   useradd $username
   echo $username | passwd --stdin $username &>/dev/null
 }
 let i++
done

(8)仔细阅读出错信息

有时候我们修改了某个错误并再次运行后,系统依旧会报错。然后我们再次修改,但系统 再次报错。这可能会持续很长时间。但实际上,旧的错误可能已经被纠正,只是由于出现了其它一些新错误才导致系统再次报错。

如何快速如何快速生成脚本开头的版本版权注释信息

[root@localhost ~]# vim ~/.vimrc
autocmd BufNewFile *.py,*.cc,*.sh,*.java exec ":call SetTitle()"

func SetTitle()
if expand("%:e") == 'sh'
   call setline(1,"#!/bin/bash")
   call setline(2,"#########################")
   call setline(3,"#File name:".expand("%"))
   call setline(4,"#Version:v1.0")
   call setline(5,"#Email:admin@test.com")
   call setline(6,"#Created time:".strftime("%F %T"))
   call setline(7,"#Description:")
   call setline(8,"#########################")
   call setline(9,"")
 endif
endfunc

1.8 shell脚本的执行方式

(1)交互式执行

[root@localhost ~]# for filename in `ls /etc`
> do
> if echo "$filename" | grep "passwd"
> then
> echo "$filename"
> fi
> done

(2)作为程序文件执行(常用)

对于一组需要经常重复执行的Shell语句来说,将它们保存在一个文件中来执行。我们通常称这种包含多个Shell语句的文件为Shell脚本,或者Shell脚本文件。脚本文件是普通的文本文件,可使用任何的文本编辑器查看或修改Shell脚本。

[root@localhost ~]# mkdir /test
[root@localhost ~]# cd /test
[root@localhost test]# vim test1.sh
#!/bin/bash
for filename in `ls /etc`
do
  if echo "$filename" | grep "passwd"
  then
    echo "$filename"
  fi
done

1.9 执行脚本的方法

(1)bash ./filename.sh(产生子进程,再运行,使用当前指定的bash shell去运行)

(2)./filename.sh(产生子进程,再运行,使用脚本里面指定的shell去运行。使用该种方式执行需要x权限)

(3)source ./filename.sh(source命令是一个shell内部命令,其功能是读取指定的shell程序文件,并且依次执行其中的所有的语句,并没有创建新的子shell进程,所以脚本里面所有创

建的变量都会保存到当前的shell里面)

(4). filename.sh(和source一样,也是使用当前进程执行)

示例一:

[root@localhost test]# vim test2.sh
#!/bin/bash
cd /tmp
pwd
[root@localhost test]# ls -l test2.sh
-rw-r--r--. 1 root root 24 Apr 30 20:09 test2.sh
(1)[root@localhost test]# bash test2.sh
/tmp
(2)[root@localhost test]# ./test2.sh
-bash: ./test2.sh: Permission denied
[root@localhost test]# chmod a+rx test2.sh
[root@localhost test]# ./test2.sh
/tmp
(3)[root@localhost test]# source test2.sh
/tmp
[root@localhost tmp]#
(4)[root@localhost test]# . test2.sh
/tmp
[root@localhost tmp]#

执行shell脚本时,如果使用1和2这种方式执行会在当前的进程下产生一个新的bash子进程,所以子进程切换到了/tmp目录,当脚本结束,子进程也就结束了,所以当前进程的目录不会发生变化;3和4方式执行时,不会产生新的进程,所以脚本执行结束后当前的目录会变成/tmp。

示例二:

[root@localhost test]# echo 'userdir=`pwd`' > test3.sh
[root@localhost test]# cat test3.sh
userdir=`pwd`
(1)[root@localhost test]# bash test3.sh
[root@localhost test]# echo $userdir

[root@localhost test]#
(2)[root@localhost test]# chmod a+rx test3.sh
[root@localhost test]# ./test3.sh
[root@localhost test]# echo $userdir

[root@localhost test]#
(3)[root@localhost test]# source test3.sh
[root@localhost test]# echo $userdir
/test
(4)[root@localhost test]# . test3.sh
[root@localhost test]# echo $userdir
/test

1.10 shell脚本的退出状态

在UNIX或者Linux中,每个命令都会返回一个退出状态码。退出状态码是一个整数,其有效范围为0~255。通常情况下,成功的命令返回0,而不成功的命令返回非0值。非0值通常都被解释成一个错误码。行为良好的UNIX命令,程序和工具都会返回0作为退出码来表示成功。

Shell脚本中的函数和脚本本身也会返回退出状态码。在脚本或者是脚本函数中执行的最后的命令会决定退出状态码。另外,用户也可以在脚本中使用exit语句将指定的退出状态码传递给Shell。

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

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

相关文章

ThinkPHP V5.1框架源码

源码下载地址&#xff1a;ThinkPHP V5.1.zip www WEB部署目录&#xff08;或者子目录&#xff09; ├─application 应用目录 │ ├─common 公共模块目录&#xff08;可以更改&#xff09; │ ├─module_name 模块目录 │ │ ├─common.php 模块函数文件 │ │ ├─controll…

首届《综合品酒师》培训规模创大世界基尼斯,云仓酒庄再上新台阶

近日&#xff0c;云仓酒庄举办的首届《综合品酒师》培训活动圆满落幕&#xff0c;其盛大的规模与参与度不仅成功刷新了大世界基尼斯纪录&#xff0c;更为云仓酒庄的发展注入了新的活力与动能。这次培训不仅彰显了云仓酒庄在酒类培训领域的专业化与系统化&#xff0c;更为其未来…

vs配置opencv运行时“发生生成错误,是否继续并运行上次的成功生成”BUG解决办法

vs“发生生成错误&#xff0c;是否继续并运行上次的成功生成” 新手在用vs配置opencv时遇到这个错误时&#xff0c;容易无从下手解决。博主亲身经历很有可能是release/debug模式和配置文件不符的问题。 在配置【链接器】→【输入】→【附加依赖项】环节&#xff0c;编辑查看选择…

北京大学快手发布统一的图文视频生成大模型Video-LaVIT

随着多模态大语言模型&#xff08;LLMs&#xff09;的新发展&#xff0c;人们越来越关注如何将它们从图像-文本数据扩展到更具信息量的真实世界视频。与静态图像相比&#xff0c;视频为有效的大规模预训练带来了独特的挑战&#xff0c;因为需要对其时空动态进行建模。 针对视频…

企业微信对接金蝶KIS生成总账凭证

客户介绍&#xff1a; 某集团企业是一家致力于打造更全面的农产业链条&#xff0c;生产更高品质的农业产品的国际现代化农业公司。集团布局四川、贵州、广东、中国香港、澳大利亚等地&#xff0c;业务涉足大肥牛交易平台、超级牧场、肉牛养殖、蜂产业、饲草、智能化装备、有机…

多模态之ALBEF—先对齐后融合,利用动量蒸馏学习视觉语言模型表征,学习细节理解与论文详细阅读:Align before Fuse

Align before Fuse: Vision and Language Representation Learning with Momentum Distillation &#xff08;ALBEF&#xff09;在融合之前对齐&#xff1a;利用动量蒸馏进行视觉与语言表示学习 Paper: arxiv.org/pdf/2107.07651.pdf Github: https://github.com/salesforce/…

【计算机毕业设计】游戏售卖网站——后附源码

&#x1f389;**欢迎来到琛哥的技术世界&#xff01;**&#x1f389; &#x1f4d8; 博主小档案&#xff1a; 琛哥&#xff0c;一名来自世界500强的资深程序猿&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 琛哥在深度学习任务中展现出卓越的能力&a…

基于springboot实现房屋租赁管理系统设计项目【项目源码+论文说明】

基于springboot实现房屋租赁管理系统设计演示 摘要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对房屋租赁信息管理混乱&…

Linux应用开发笔记(五)网络编程(二)多线程编程

文章目录 前言一、线程和进程1. 进程&#xff08;Process&#xff09;2. 线程&#xff08;Thread&#xff09;3. 二者的比较 二、多线程和多进程三. 代码编写1. 相关函数pthread_create( )函数pthread_exit( )函数pthread_join( )函数 2. 线程同步3. 互斥量4. 条件变量5. 实验代…

微服务之分布式链路追踪

一、概述 1.1背景 在微服务框架中&#xff0c;一个由客户端发起的请求在后端系统中会经过多个不同的的服务节点调用来协同产生最后的请求结果&#xff0c;每一个前段请求都会形成一条复杂的分布式服务调用链路&#xff0c;链路中的任何一环出现高延时或错误都会引起整个请求最…

MyBatis操作数据库(3)

其它查询操作 #{}和${} MyBatis参数赋值有两种方式, 咱们前面使用了#{}进行赋值, 接下来来看两者的区别: #{}和${}的使用 1.先看Integer类型的参数: Select("select username, password, age, gender, phone from userinfo where id #{id}") UserInfo queryByI…

OSI七层网络攻击行为及防范手段

2020年3月3日&#xff0c;360安全大脑披露美国中央情报局攻击组织&#xff08;APT-C-39&#xff09;对我国大型互联网公司、政府部门及相关企业进行长达11年的网络攻击渗透&#xff0c;该组织所使用的网络武器和CIA“Vault7”项目中的网络武器完全吻合。如今随着互联网技术的蓬…

RocketMQ 事件驱动:云时代的事件驱动有啥不同?

作者&#xff1a;林清山&#xff08;隆基&#xff09; 前言&#xff1a; 从初代开源消息队列崛起&#xff0c;到 PC 互联网、移动互联网爆发式发展&#xff0c;再到如今 IoT、云计算、云原生引领了新的技术趋势&#xff0c;消息中间件的发展已经走过了 30 多个年头。 目前&a…

图片懒加载的三种方式

方法一:滚动监听 + scrollTop + offsetTop + innerHeight scrollTop:指网页元素被滚动条卷去的部分。 offsetTop:元素相对父元素的位置 innerHeight:当前浏览器窗口的大小。需要注意兼容性问题。 <!DOCTYPE html> <html lang="en"><head>&…

IDEA: Unable to resolve table ‘xxx‘

描述&#xff1a; 在 IDEA 连接到数据库后&#xff0c;SQL 语句提示 Unable to resolve table 表名&#xff0c;且其它字段也飘红报错。 解决&#xff1a; 右键点击数据库&#xff0c;选择 Tools -> Manage Shown Schemas... 勾选你所使用的数据库即可&#xff1a; 1、2、3…

软考 系统架构设计师系列知识点之大数据设计理论与实践(6)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之大数据设计理论与实践&#xff08;5&#xff09; 所属章节&#xff1a; 第19章. 大数据架构设计理论与实践 第3节 Lambda架构 本文部分内容参考&#xff1a; Lambda架构 - 简书 特此致谢&#xff01; 19.3.3 Lambda…

【Ansible自动化运维】Ansible入门基础信息【安装配置、常用命令与模块】

介绍安装配置注意事项yum安装验证安装配置host配置主机清单配置主控端被控端 常用模块命令组成command模块shell模块copy模块script模块 日志信息最后 介绍 Ansible 是一个开源 IT 自动化引擎&#xff0c;可自动执行供应、配置管理、应用程序部署、编排和许多其他 IT 流程。它可…

深入浅出学习切片LOD——ArcGIS server模拟缓存切片(影像快显)

一、第一次实践 原理 免切片实现影像服务的模拟切片&#xff0c;主要原理是接收前端传过来的xyz(行列层级)以及切片方案&#xff0c;计算出该请求的切片的四至经纬度信息&#xff0c;通过mapserver的exportImage接口&#xff0c;传入每个模拟切片的四至经纬度信息得到图片返回…

小程序视频下载器

下载高手&#xff0c;让小程序视频下载变得前所未有的简单&#xff01;专为非编程专业人士设计&#xff0c;该工具免去了繁琐的抓包软件学习过程&#xff0c;无需深入研究Fiddler或Charles的配置。它优化了视频、图片和音频资源的下载&#xff0c;提供直观的操作界面&#xff0…

大世界基尼斯见证辉煌,云仓酒庄首届酒类培训新高度诞生

近日&#xff0c;一场规模盛大的酒类培训盛会&#xff0c;在云仓酒庄的精心组织下圆满落幕。此次培训活动以其卓着的成果和盛大的规模&#xff0c;创下了大世界基尼斯纪录&#xff0c;为酒类培训领域树立了新的标杆。这一成就的取得&#xff0c;背后是云仓酒庄团队无数的心血与…