Openstack 开发人员安装脚本解读 [stack.sh]

这是转载的ju136的专栏的文章。
http://devstack.org/

有一份英文版的源代码解读,不过如果对于Openstack没有过接触的人来说,可能看起来会有一些困难。

http://devstack.org/stack.sh.html

stack.sh 简介

stack.sh是一个openstack开发人员可以选择的一种安装脚本。


这个脚本的主要作用是安装并配置nova, glance, horizon, 以及keystone。


使用这个脚本,你可以设置一些配置选项,比如使用哪个git源,开启哪些服务,网络如何配置,以及各种密码的设置与管理。通过这个脚本,也可以灵活地在多台机器上运行,通过一个类似的资源进行一种共享式的配置。比如mysql, rabbitmq,从而可以组建一个多机环境下的云。


为了保证这个脚本足够简单,我们假设你是在一个Ubuntu 11.10 Oneiric的机器上执行。当然,这个脚本也可以在VM上运行,或是在物理机上运行。此外,我们把一些apt, pip安装包放到别的文件中去了,那么开始了解这个脚本,以及这些依赖的包。


如果你想进一步的学习或了解,可以去网站

http://devstack.org

Sanity Check 

这个脚本首先来说是针对于Oneiric来写的,如果要在Ubuntu的其他版本上执行,当然也可以通过执行命令

[cpp]  view plain copy
  1. $FORCE=yes ./stack.sh  

,从而达到要求。但是我个人而言,不建议采用这种方式进来安装部署。在其他版本上的安装部署,后面再补上。


版本检查

下面这段代码就是主要是在检查一下OS的版本。

# DISTRO变量就是拿到OS的版本。在Ubuntu上执行,会得到发行版的名字。oneiric。

# 接下来的几句只是在进行检查是否是oneiric版本的Ubuntu

[cpp]  view plain copy
  1. DISTRO=$(lsb_release -c -s)  
  2.   
  3. if [[ ! ${DISTRO} =~ (oneiric) ]]; then  
  4.     echo "WARNING: this script has only been tested on oneiric"  
  5.     if [[ "$FORCE" != "yes" ]]; then  
  6.         echo "If you wish to run this script anyway run with FORCE=yes"  
  7.         exit 1  
  8.     fi  
  9. fi  

目录设定

stack.sh把很多需要安装的包与其他一些依赖都放到别的文件里面的,你可以在/devstack/files中找到这些文件。但是在脚本中,我们是通过FILES变量来引用到这些配置文件的位置。


#首先是拿到顶级目录所在位置。比如/root/devstack,那么$TOP_DIR="/root/devstack"

#生成FILES变量的值。再去检查一下这个目录是否存在。

[cpp]  view plain copy
  1. TOP_DIR=$(cd $(dirname "$0") && pwd)  
  2.   
  3. FILES=$TOP_DIR/files  
  4. if [ ! -d $FILES ]; then  
  5.     echo "ERROR: missing devstack/files - did you grab more than just stack.sh?"  
  6.     exit 1  
  7. fi   


设定

这个脚本的可定制性体现在,一些环境变量可以放到别的文件中,或者是通过设定环境变量来进行处理。比如

[cpp]  view plain copy
  1. $export MYSQL_PASSWORD=anothersecret  
  2. $./stack.sh  

可以达到效果。当然,你也可以把这些语句放在同一行,比如

[cpp]  view plain copy
  1. $MYSQL_PASSWORD=simple ./stack.sh  
也可以把这些设定放到$TOP_DIR/localrc文件中。

比如:

[cpp]  view plain copy
  1. $ cat $TOP_DIR/localrc  
  2. .........  
  3. MYSQL_PASSWORD=anothersecret  
  4. MYSQL_USER=hellaroot  
一般来说,我们都使用的是一些很敏感的设定,所以为了省事,你也可以运行

[cpp]  view plain copy
  1. $./stack.sh  
(一般而言,如果这样运行,脚本会在发现没有密码的时候,叫你输出密码,如果输入密码为空,那么会随机生成一个密码)。


环境变量

对于环境变量而言,一般我们都是放到stackrc文件中。这个文件是随着devstack一起发布的,并且主要是包含了需要使用的git源。如果你想要使用别的源,或者是别的分枝,那么你可以加入你自己的设定,并把这些设定写到localrc文件中。


一般而言localrc里面的设置会把stackrc文件中的变量给覆盖掉。这个是很有用的,特别是当你想使用别的源或者分枝的时候。你也可以更改掉一些已有设定,比如

[cpp]  view plain copy
  1. MYSQL_PASSWORD  
  2. ADMIN_PASSWORD  

否则devstack脚本会给你随机生成一个(这个随机生成的密码还有点长)。


#首先是从stackrc中引入环境变量

#$DEST变量是指的是安装目录,一般默认安装目录是/opt/stack

#下面是定义了一个函数,其实也就是使用apt-get命令。

[cpp]  view plain copy
  1. source ./stackrc  
  2.   
  3. DEST=${DEST:-/opt/stack}  
  4.   
  5. function apt_get() {  
  6.     [[ "$OFFLINE" = "True" ]] && return  
  7.     local sudo="sudo"  
  8.     [ "$(id -u)" = "0" ] && sudo="env"  
  9.     $sudo DEBIAN_FRONTEND=noninteractive apt-get \  
  10.         --option "Dpkg::Options::=--force-confold" --assume-yes "$@"  
  11. }  


这里检查是否已经有stack.sh程序在运行。

[cpp]  view plain copy
  1. if screen -ls | egrep -q "[0-9].stack"; then  
  2.     echo "You are already running a stack.sh session."  
  3.     echo "To rejoin this session type 'screen -x stack'."  
  4.     echo "To destroy this session, kill the running screen."  
  5.     exit 1  
  6. fi  

因为Openstack在设计的时候,是用的一般用户来运行的,主要原因是因为dashboard在运行的时候底层使用的是apache2的服务器,而apache2不可以在root权限下执行。

如果在运行脚本的时候是使用的是root权限,那么会自动新建一个stack用户来执行操作。并且这个用户会给足够的权限来进行很多操作

[cpp]  view plain copy
  1. if [[ $EUID -eq 0 ]]; then  
  2.     ROOTSLEEP=${ROOTSLEEP:-10}  
  3.     echo "You are running this script as root."  
  4.     echo "In $ROOTSLEEP seconds, we will create a user 'stack' and run as that user"  
  5.     sleep $ROOTSLEEP  
  6.   
  7.     # 因为这个脚本接下来是按照一个普通用户来执行的,那么需要给这个普通用户以sudo的权限。  
  8.     #   
  9.     dpkg -l sudo || apt_get update && apt_get install sudo  
  10.   
  11.     if ! getent passwd stack >/dev/null; then  
  12.         echo "Creating a user called stack"  
  13.         useradd -U -G sudo -s /bin/bash -d $DEST -m stack  
  14.     fi  
  15.   
  16.     echo "Giving stack user passwordless sudo priviledges"  
  17.     # some uec images sudoers does not have a '#includedir'. add one.  
  18.     grep -q "^#includedir.*/etc/sudoers.d" /etc/sudoers ||  
  19.         echo "#includedir /etc/sudoers.d" >> /etc/sudoers  
  20.     ( umask 226 && echo "stack ALL=(ALL) NOPASSWD:ALL" \  
  21.         > /etc/sudoers.d/50_stack_sh )  
  22.   
  23.     echo "Copying files to stack user"  
  24.     STACK_DIR="$DEST/${PWD##*/}"  
  25.     cp -r -f -T "$PWD" "$STACK_DIR"  #这里在拷文件,额外的拷文件的动作也可以加在这里。  
  26.     chown -R stack "$STACK_DIR"  
  27.     if [[ "$SHELL_AFTER_RUN" != "no" ]]; then  
  28.         exec su -c "set -e; cd $STACK_DIR; bash stack.sh; bash" stack  
  29.     else  
  30.         exec su -c "set -e; cd $STACK_DIR; bash stack.sh" stack  
  31.     fi  
  32.     exit 1  
  33. else  
  34.     # Our user needs passwordless priviledges for certain commands which nova  
  35.     # uses internally.  
  36.     # Natty uec images sudoers does not have a '#includedir'. add one.  
  37.     sudo grep -q "^#includedir.*/etc/sudoers.d" /etc/sudoers ||  
  38.         echo "#includedir /etc/sudoers.d" | sudo tee -a /etc/sudoers  
  39.     TEMPFILE=`mktemp`  
  40.     cat $FILES/sudo/nova > $TEMPFILE  
  41.     sed -e "s,%USER%,$USER,g" -i $TEMPFILE  
  42.     chmod 0440 $TEMPFILE  
  43.     sudo chown root:root $TEMPFILE  
  44.     sudo mv $TEMPFILE /etc/sudoers.d/stack_sh_nova  
  45. fi  

接下来这个就是设置是否是用离线的方式来进行安装,或者是用上网的方式来进行安装。

[cpp]  view plain copy
  1. function trueorfalse() {  
  2.     local default=$1  
  3.     local testval=$2  
  4.   
  5.     [[ -z "$testval" ]] && { echo "$default"return; }  
  6.     [[ "0 no false False FALSE" =~ "$testval" ]] && { echo "False"return; }  
  7.     [[ "1 yes true True TRUE" =~ "$testval" ]] && { echo "True"return; }  
  8.     echo "$default"  
  9. }  
  10. OFFLINE=`trueorfalse False $OFFLINE`   

一般来说,安装方式都是在线的方式来安装的。如果设置的值是True,那么就是使用离线的方式来进行安装。

配置服务


接下来是设置一些环境变量:这些环境变量主要都是为了安装某个部件而设置的。

[cpp]  view plain copy
  1. NOVA_DIR=$DEST/nova  
  2. HORIZON_DIR=$DEST/horizon  
  3. GLANCE_DIR=$DEST/glance  
  4. KEYSTONE_DIR=$DEST/keystone  
  5. NOVACLIENT_DIR=$DEST/python-novaclient  
  6. OPENSTACKX_DIR=$DEST/openstackx  
  7. NOVNC_DIR=$DEST/noVNC  
  8. SWIFT_DIR=$DEST/swift  
  9. SWIFT_KEYSTONE_DIR=$DEST/swift-keystone2  
  10. QUANTUM_DIR=$DEST/quantum  

下面的设置是为了Quantum而进行设置。

[cpp]  view plain copy
  1. # Default Quantum Plugin  #看样子这个应该是一个网络服务  
  2. Q_PLUGIN=${Q_PLUGIN:-openvswitch}  
  3. # Default Quantum Port  #这里设置端口  
  4. Q_PORT=${Q_PORT:-9696}  
  5. # Default Quantum Host  #这里设置主机  
  6. Q_HOST=${Q_HOST:-localhost}  

接下来设置哪些服务要开启。注意这里用到的都是缩写。

[cpp]  view plain copy
  1. ENABLED_SERVICES=${ENABLED_SERVICES:-g-api,g-reg,key,n-api,n-cpu,n-net,n-sch,n-vnc,horizon,mysql,rabbit,openstackx}  

下面是设置volume服务

[cpp]  view plain copy
  1. # Name of the lvm volume group to use/create for iscsi volumes  
  2. VOLUME_GROUP=${VOLUME_GROUP:-nova-volumes}  
  3. VOLUME_NAME_PREFIX=${VOLUME_NAME_PREFIX:-volume-}  
  4. INSTANCE_NAME_PREFIX=${INSTANCE_NAME_PREFIX:-instance-}  

接下来是设置需要使用到的底层的服务

[cpp]  view plain copy
  1. VIRT_DRIVER=${VIRT_DRIVER:-libvirt}  
  2. LIBVIRT_TYPE=${LIBVIRT_TYPE:-kvm}  
一般而言我们都是使用kvm+libvirt。如果kvm没有驱动起来,我们会使用qemu。


设置要使用到的Scheduler

[cpp]  view plain copy
  1. SCHEDULER=${SCHEDULER:-nova.scheduler.simple.SimpleScheduler}  

接下来是拿到主机IP地址。

[cpp]  view plain copy
  1. if [ ! -n "$HOST_IP" ]; then  
  2.     HOST_IP=`LC_ALL=C /sbin/ifconfig eth0 | grep -m 1 'inet addr:'| cut -d: -f2 | awk '{print $1}'`  
  3.     if [ "$HOST_IP" = "" ]; then  
  4.         echo "Could not determine host ip address."  
  5.         echo "If this is not your first run of stack.sh, it is "  
  6.         echo "possible that nova moved your eth0 ip address to the FLAT_NETWORK_BRIDGE."  
  7.         echo "Please specify your HOST_IP in your localrc."  
  8.         exit 1  
  9.     fi  
  10. fi  

这里是设置一下是否使用系统日志:

[cpp]  view plain copy
  1. SYSLOG=`trueorfalse False $SYSLOG`  
  2. SYSLOG_HOST=${SYSLOG_HOST:-$HOST_IP}  
  3. SYSLOG_PORT=${SYSLOG_PORT:-516}  

服务超时设定

[cpp]  view plain copy
  1. SERVICE_TIMEOUT=${SERVICE_TIMEOUT:-60}  

接下这个函数就是为了读取密码而写的。

[cpp]  view plain copy
  1. function read_password {  
  2.     set +o xtrace  
  3.     var=$1; msg=$2  
  4.     pw=${!var}  
  5.   
  6.     localrc=$TOP_DIR/localrc  
  7.   
  8.     # If the password is not defined yet, proceed to prompt user for a password.  
  9.     if [ ! $pw ]; then  
  10.         # If there is no localrc file, create one  
  11.         if [ ! -e $localrc ]; then  
  12.             touch $localrc  
  13.         fi  
  14.   
  15.         # Presumably if we got this far it can only be that our localrc is missing  
  16.         # the required password.  Prompt user for a password and write to localrc.  
  17.         echo ''  
  18.         echo '################################################################################'  
  19.         echo $msg  
  20.         echo '################################################################################'  
  21.         echo "This value will be written to your localrc file so you don't have to enter it again."  
  22.         echo "It is probably best to avoid spaces and weird characters."  
  23.         echo "If you leave this blank, a random default value will be used."  
  24.         echo "Enter a password now:"  
  25.         read -e $var  
  26.         pw=${!var}  
  27.         if [ ! $pw ]; then  
  28.             pw=`openssl rand -hex 10`  
  29.         fi  
  30.         eval "$var=$pw"  
  31.         echo "$var=$pw" >> $localrc  
  32.     fi  
  33.     set -o xtrace  
  34. }  

你如果想设置成为一个固定的密码,那么这里可以改为如下:

[cpp]  view plain copy
  1. # Presumably if we got this far it can only be that our localrc is missing  
  2. # the required password.  Prompt user for a password and write to localrc.  
  3. echo "Set password to nova:"  
  4. pw="nova"  
  5. eval "$var=$pw"  
  6. echo "$var=$pw" >> $localrc  

为了简单起见,后面会一直使用nova做为密码。(自己安装着玩的时候这么干比较好,如果正式用的话,还是用一个靠谱的密码吧。)


设置网络:这些设定应该最后都会写到/etc/nova/nova.conf文件中的。不过这里好像是放在安装目录中。

[cpp]  view plain copy
  1. PUBLIC_INTERFACE=${PUBLIC_INTERFACE:-eth0}  
  2. FIXED_RANGE=${FIXED_RANGE:-10.0.0.0/24}  
  3. FIXED_NETWORK_SIZE=${FIXED_NETWORK_SIZE:-256}  
  4. FLOATING_RANGE=${FLOATING_RANGE:-172.24.4.224/28}  
  5. NET_MAN=${NET_MAN:-FlatDHCPManager}  
  6. EC2_DMZ_HOST=${EC2_DMZ_HOST:-$HOST_IP}  
  7. FLAT_NETWORK_BRIDGE=${FLAT_NETWORK_BRIDGE:-br100}  
  8. VLAN_INTERFACE=${VLAN_INTERFACE:-$PUBLIC_INTERFACE}  


下面是设置是否使用多节点模式,如果使用多节点模式,那么每个计算节点都会与网络节点都是一体的,访问相应的VM的时候,直接访问相应的物理机则可。

这种模式可以解除SPOF和带宽的制约。

[cpp]  view plain copy
  1. MULTI_HOST=${MULTI_HOST:-False}  

下面一段话比较长,不过也可以看一下:

如果你是在多台物理机上使用FlatDHCP模式,设置"FLAT_INTERFACE"变量,并且要保证这个interface并没有一个IP。否则你可能会破坏一些东西。


"*DHCP Warning" 这种情况,如果你使用flat interface设备使用DHCP,发生下面这种情况的时候,可能会让你呆住,

网络从flat interface上移到flat network bridge。这种情况完全有可能发生。结果是当你创建第一台虚拟机的时候。这种情况会导致你的虚拟机出问题,比如连接不上,或者登陆失败。


如果你只是装着单机版的来玩玩,那么你可以使用FLAT_NETWORK_BRIDGE。

[cpp]  view plain copy
  1. FLAT_INTERFACE=${FLAT_INTERFACE:-eth0}  

quantum网络

你需要确保quantum是在Enabled_services中是开启的。假如network manager设置成为QuantumManager。

假如你想在这台机器上运行Quantum,那么最好在ENABLED_SERVICES中要找到q-svc字段。

如果你想在quantum中使用openswitch插件,那么把 Q_PLUGIN 的值设置为openvswitch。并且需要保证q-agt服务也是在开启的服务之列。


如果你设置了Quantum网络,那么NET_MAN变量的值就被忽略了。


MYSQL 和 RabbitMQ

在这里,我们配置的目标是让Nova, Horizon, Glance, 与Keystone都使用MySQL做为其数据库。这里他们主要是使用一个单一的服务,但是每个服务都是有他们自己的表数据库与表单。

一般而言,这里的脚本会安装和配置MySQL。如果你想用一个已经有的服务,那么你需要传递/user/password/host参数。也同时需要传递MYSQL_PASSWORD参数到每个节点(当你使用的单台机器的时候,这一步就省了吧)。一般而言我都是采用一种最简单的方式,也就是一台服务器,也都使用这个服务器上的MySQL数据库。


下面的三行代码分别是设置MySQL的机器地址与用户名与密码。在这里是需要手动输入密码。

[cpp]  view plain copy
  1. MYSQL_HOST=${MYSQL_HOST:-localhost}  
  2. MYSQL_USER=${MYSQL_USER:-root}  
  3. read_password MYSQL_PASSWORD "ENTER A PASSWORD TO USE FOR MYSQL."  

下面再给出一个链接,这个链接主要是用于连接MYSQL数据库。一般不需要去加上所需要连接的DataBase,因为其他几个服务都是需要通过这个链接去连MySQL。但是每个服务连接的数据库是不一样的。所以如果是用同一样的链接+数据库名,肯定会出错。一句话,保持下面这个不变就可以了。

[cpp]  view plain copy
  1. BASE_SQL_CONN=${BASE_SQL_CONN:-mysql://$MYSQL_USER:$MYSQL_PASSWORD@$MYSQL_HOST}  

设置RabbitMQ的密码

[cpp]  view plain copy
  1. RABBIT_HOST=${RABBIT_HOST:-localhost}  
  2. read_password RABBIT_PASSWORD "ENTER A PASSWORD TO USE FOR RABBIT."  

接下来指出Glance的主机与端口,值得注意的是这里需要给出端口。这个端口按照原文好像是需要指定的,特定的这么个端口(总之保持不变就是了).。

[cpp]  view plain copy
  1. GLANCE_HOSTPORT=${GLANCE_HOSTPORT:-$HOST_IP:9292}  

Swift设置

需要完善的功能:

TODO:实现对于glance的支持。

TODO:   加入对于不同存储位置的日志功能。

一般来说,swift驱动的位置与存储对象是放在swift源码的内部文件夹中。当然也可以自已指定一个目录。

[cpp]  view plain copy
  1. SWIFT_DATA_LOCATION=${SWIFT_DATA_LOCATION:-${SWIFT_DIR}/data}  

同样的道里,swift的配置文件也是放在源码内部的。当然也可以根据你自己的情况做出调整。

[cpp]  view plain copy
  1. SWIFT_CONFIG_LOCATION=${SWIFT_CONFIG_LOCATION:-${SWIFT_DIR}/config}  

接下来,devstack需要建立一个loop-back的磁盘,这个磁盘会被格式化为XFS格式,并且用来存储swift数据。一般来说,这个磁盘的大小是1G。变量SWIFT_LOOPBACK_DISK_SIZE指出了这个磁盘的大小。当然你也可以根据自己的情况来调整大小。

[cpp]  view plain copy
  1. SWIFT_LOOPBACK_DISK_SIZE=${SWIFT_LOOPBACK_DISK_SIZE:-1000000}  
接下来是swift需要读入一个密码。这一段比较难以理解(主查涉及到swift内部的一些原理与实现)。

[cpp]  view plain copy
  1. # The ring uses a configurable number of bits from a path’s MD5 hash as  
  2. # a partition index that designates a device. The number of bits kept  
  3. # from the hash is known as the partition power, and 2 to the partition  
  4. # power indicates the partition count. Partitioning the full MD5 hash  
  5. # ring allows other parts of the cluster to work in batches of items at  
  6. # once which ends up either more efficient or at least less complex than  
  7. # working with each item separately or the entire cluster all at once.  
  8. # By default we define 9 for the partition count (which mean 512).  
  9. SWIFT_PARTITION_POWER_SIZE=${SWIFT_PARTITION_POWER_SIZE:-9}  
  10.   
  11. # We only ask for Swift Hash if we have enabled swift service.  
  12. if [[ "$ENABLED_SERVICES" =~ "swift" ]]; then  
  13.     # SWIFT_HASH is a random unique string for a swift cluster that  
  14.     # can never change.  
  15.     read_password SWIFT_HASH "ENTER A RANDOM SWIFT HASH."  
  16. fi  

Keystone

服务口令:Openstack的组件需要有一个管理员口令来认证用户的口令。

下面则是读入一个密码:

[cpp]  view plain copy
  1. read_password SERVICE_TOKEN "ENTER A SERVICE_TOKEN TO USE FOR THE SERVICE ADMIN TOKEN."  

同样也需要为Horizon输入一个密码。

[cpp]  view plain copy
  1. read_password ADMIN_PASSWORD "ENTER A PASSWORD TO USE FOR HORIZON AND KEYSTONE (20 CHARS OR LESS)."  

接下来是设置日志

[cpp]  view plain copy
  1. LOGFILE=${LOGFILE:-"$PWD/stack.sh.$.log"}  
  2. (  
  3. # So that errors don't compound we exit on any errors so you see only the  
  4. # first error that occurred.  
  5. trap failed ERR  
  6. failed() {  
  7.     local r=$?  
  8.     set +o xtrace  
  9.     [ -n "$LOGFILE" ] && echo "${0##*/} failed: full log in $LOGFILE"  
  10.     exit $r  
  11. }  

接下来的这个命令比较有用,就是打出每个命令,以及这个命令的输出,主要是用于查看在哪里出错了。以便及时查错。

[cpp]  view plain copy
  1. set -o xtrace  

创建目标目录,并且要保证stack用户对于这个目录是可写的。

[cpp]  view plain copy
  1. sudo mkdir -p $DEST  
  2. if [ ! -w $DEST ]; then  
  3.     sudo chown `whoami` $DEST  
  4. fi  

前面讲了这么多,其实都是在讲怎么配置。接下来才是要进行人工操作阶段。


安装依赖包

Openstack用了很多其他的工程。所以需要安装其他工程。


注意的是:

我们只安装对于我们的服务来说是必须的包。





脚本学习---得到操作系统发行版的方法

方法一

[python]  view plain copy
  1. DISTRO=$(lsb_release -c -s)  

可以用来得到操作系统的发行版。

缺点

虽然这种方法可以在ubuntu上得到类似于oneiric的字样(如果你的发行版不同,那么得到的也肯定不一样)。

至于其他版本,则得到的结果就有点奇怪了。比如我在suse上运行,是得不到相应的结果的。

[python]  view plain copy
  1. $ lsb_release -c -s  
  2. n/a  

方法二

其实操作系统发行版的信息是写在/etc/issue 和/etc/issue.net文件中。为了方便我们用如下命令去查看吧。

[python]  view plain copy
  1. $ cat /etc/issue*  
在ubuntu下的运行结果如下:
[python]  view plain copy
  1. $ cat /etc/issue*  
  2. Ubuntu 11.10 \n \l  
  3.   
  4. Ubuntu 11.10  

在SUSE下面运行有如下结果:


[python]  view plain copy
  1. $ cat /etc/issue*  
  2. Welcome to SUSE Linux Enterprise Server 11 SP1  (x86_64) - Kernel \r (\l).  
  3.   
  4.   
  5. Welcome to SUSE Linux Enterprise Server 11 SP1  (x86_64) - Kernel %r (%t).  

为了简单起见,我们需要把这些字符串转化成为小写,再进行判断。因为有时候有的发行版在说明的时候会写SUSE, 而Ubuntu的写的是Ubuntu,并不是全部都是大写。

我们用如下脚本来进行测试是否是suse系统

[python]  view plain copy
  1. <span style="color:#3333FF;">if [[ `cat $DISTRO | grep suse | wc -l` -eq 0 ]]; then  
  2.   echo "Not suse"  
  3.   exit 1  
  4. fi</span>  


脚本学习----如何得到脚本运行时所在目录。

当在问这个问题的时候,很有可能会回答说是pwd,在C语言里用的是getcwd()函数。实际上,pwd这个命令返回值是运行命令时所在路径,而不是脚本所在目录。

如果对于这个问题不是很了解。那么换个问题就是,一个脚本,如何知道自己所在的目录?这个脚本可以被放在很多位置。

方法一

第一种方法就是这个stack.sh脚本中所提到的,

[python]  view plain copy
  1. TOP_DIR=$(cd $(dirname "$0") && pwd)  
一开始看时,可能不太明白其含义,现解释如下:
[python]  view plain copy
  1. $0 这个只能是在脚本里面使用,在命令行里面只会输出-bash。$0表示的意思是运行脚本的全路径。  
  2.   
  3. dirname这个命令就是截取全路径中的目录部分比如  
  4. $ dirname /root/stack/nova.sh  
  5. /root/stack  
  6.   
  7. 实际上,在脚本中,使用dirname $0可能就已经够用了。之所以再用cd && pwd我觉得主要的原因是怕$0给出的不是全路径。  

方法二

来个简洁版的,

[python]  view plain copy
  1. TOP_DIR=`dirname $0`  
从而可以确定脚本所在目录。


脚本学习---如何取得全路径中的路径

方法一

dirname $par

方法二

pkg_dir=${par%/*}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值