bash语法

一、变量

1.1 变量的使用

  • 变量的定义
variable=name

    注意,=两边不能有空格。一切变量都会被视为字符串。如果要使用局部变量则要加关键字local。

  • 输出变量的值
echo ${variable}

    其实这是参数和变量扩展。关于echo的详细用法在下面章节。

  • 删除变量
unset variable

    注意,不能在变量前加$。

  • 将变量转化为全局的环境变量:
export variable

    同样不能加$。环境变量将在子Shell中可见,但是当前Shell的环境变量不会影响父Shell。

1.2 环境变量

    环境变量存储了有关Shell会话和工作环境的信息。一般情况下,用户登录成功后,会启动其Shell,这个Shell叫做登录Shell。这个Shell自带一些全局的环境变量,并且它会运行相应的配置文件,初始化部分环境变量。所以当我们要定义一些自己的变量时就要定义在这些配置文件中,这样启动时便会定义了这些环境变量。另外环境变量在Shell脚本中是可以直接引用的。下面讲解会运行脚本。
    登录Shell:

配置文件说明
/etc/profile全局配置文件。如果存在则读取
~/.bash_profile如果存在则读取
~/.bash_login如果上个文件不存在,则读取,否则不读取
~/.profile如果上个文件不存在,则读取,否则不读取

    可以发现,用户的当前目录下的三个隐藏的配置文件有优先级,且且只读取第一个找到的配置文件。这里先看下全局配置文件。

1.3 全局配置文件/etc/profile

# /etc/profile: system-wide .profile file for the Bourne shell (sh(1))
# and Bourne compatible shells (bash(1), ksh(1), ash(1), ...).

if [ "$PS1" ]; then
  if [ "$BASH" ] && [ "$BASH" != "/bin/sh" ]; then
    # The file bash.bashrc already sets the default PS1.
    # PS1='\h:\w\$ '
    if [ -f /etc/bash.bashrc ]; then
      . /etc/bash.bashrc
    fi
  else
    if [ "`id -u`" -eq 0 ]; then
      PS1='# '
    else
      PS1='$ '
    fi
  fi
fi

# The default umask is now handled by pam_umask.
# See pam_umask(8) and /etc/login.defs.

if [ -d /etc/profile.d ]; then
  for i in /etc/profile.d/*.sh; do
    if [ -r $i ]; then
      . $i
    fi
  done
  unset i
fi
export JAVA_HOME=/usr/lib/jvm/jdk1.8.0_45
export CLASSPATH=.:$JAVA_HOME/lib
export MYSQL_HOME=/usr/local/mysql
export CATALINA_BASE=/opt/apache-tomcat-8.0.28
export CATALINA_HOME=/opt/apache-tomcat-8.0.28
export TOMCAT_HOME=/opt/apache-tomcat-8.0.28
export SQLITE_HOME=/home/gzx/sqlite3
export M2_HOME=/home/gzx/apache-maven-3.3.9
export PATH=$JAVA_HOME/bin:$MYSQL_HOME/bin:$TOMCAT_HOME/bin:$SQLITE_HOME/bin:$M2_HOME/bin:$PATH

    这个文件是所有用户公用的,所以定义在这里的环境变量所有用户Shell都有效。
    在上面的脚本中,在PS1变量存在的情况下,判断是否已经有BASH这个环境变量,而且该环境变量不是/bin/sh,则在当前Shell执行存在的文件/etc/bash.bashrc(这个文件的内容在下一部分解释)。否则设置PS1:对于超级用户,则PS1为#,对于普通用户则为$。本人是第一种情况。
    接着执行/etc/profile.d下的sh脚本,最后导出一些自定义的全局环境变量。/etc/profile.d目录下可以自定义一些脚本。

1.4 用户自定义配置.profile

    在本人的用户目录下,只有一个.profile文件。

# ~/.profile: executed by the command interpreter for login shells.
# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login
# exists.
# see /usr/share/doc/bash/examples/startup-files for examples.
# the files are located in the bash-doc package.

# the default umask is set in /etc/profile; for setting the umask
# for ssh logins, install and configure the libpam-umask package.
#umask 022

# if running bash
if [ -n "$BASH_VERSION" ]; then
    # include .bashrc if it exists
    if [ -f "$HOME/.bashrc" ]; then
    . "$HOME/.bashrc"
    fi
fi

# set PATH so it includes user's private bin if it exists
if [ -d "$HOME/bin" ] ; then
    PATH="$HOME/bin:$PATH"
fi

    在BASH_VERSION变量非空且~/.bashrc存在的情况下,执行该脚本。本人的机子会执行该脚本(留到下一节)。如果~/bin目录存在,则将该目录加到PATH环境变量中。

1.5 交互式Shell

    当我们在桌面系统中打开一个伪模拟终端时,会启动一个交互式bash。这个bash也会执行配置文件/etc/bash.bashrc和~/.bashrc。而这两个文件刚好也会在登录Shell中执行。
    /etc/bash.bashrc这个文件基本没做什么。

# System-wide .bashrc file for interactive bash(1) shells.

# To enable the settings / commands in this file for login shells as well,
# this file has to be sourced in /etc/profile.

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize

# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
    debian_chroot=$(cat /etc/debian_chroot)
fi

# set a fancy prompt (non-color, overwrite the one in /etc/profile)
PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '

# Commented out, don't overwrite xterm -T "title" -n "icontitle" by default.
# If this is an xterm set the title to user@host:dir
#case "$TERM" in
#xterm*|rxvt*)
#    PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}: ${PWD}\007"'
#    ;;
#*)
#    ;;
#esac

# enable bash completion in interactive shells
#if ! shopt -oq posix; then
#  if [ -f /usr/share/bash-completion/bash_completion ]; then
#    . /usr/share/bash-completion/bash_completion
#  elif [ -f /etc/bash_completion ]; then
#    . /etc/bash_completion
#  fi
#fi

# sudo hint
if [ ! -e "$HOME/.sudo_as_admin_successful" ] && [ ! -e "$HOME/.hushlogin" ] ; then
    case " $(groups) " in *\ admin\ *|*\ sudo\ *)
    if [ -x /usr/bin/sudo ]; then
    cat <<-EOF
    To run a command as administrator (user "root"), use "sudo <command>".
    See "man sudo_root" for details.

    EOF
    fi
    esac
fi

# if the command-not-found package is installed, use it
if [ -x /usr/lib/command-not-found -o -x /usr/share/command-not-found/command-not-found ]; then
    function command_not_found_handle {
            # check because c-n-f could've been removed in the meantime
                if [ -x /usr/lib/command-not-found ]; then
           /usr/lib/command-not-found -- "$1"
                   return $?
                elif [ -x /usr/share/command-not-found/command-not-found ]; then
           /usr/share/command-not-found/command-not-found -- "$1"
                   return $?
        else
           printf "%s: command not found\n" "$1" >&2
           return 127
        fi
    }
fi

    最后再看下.bashrc这个配置文件。

# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

# don't put duplicate lines or lines starting with space in the history.
# See bash(1) for more options
HISTCONTROL=ignoreboth

# append to the history file, don't overwrite it
shopt -s histappend

# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
HISTSIZE=1000
HISTFILESIZE=2000

# check the window size after each command and, if necessary,
# update the values of LINES and COLUMNS.
shopt -s checkwinsize

# If set, the pattern "**" used in a pathname expansion context will
# match all files and zero or more directories and subdirectories.
#shopt -s globstar

# make less more friendly for non-text input files, see lesspipe(1)
[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"

# set variable identifying the chroot you work in (used in the prompt below)
if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
    debian_chroot=$(cat /etc/debian_chroot)
fi

# set a fancy prompt (non-color, unless we know we "want" color)
case "$TERM" in
    xterm-color) color_prompt=yes;;
esac

# uncomment for a colored prompt, if the terminal has the capability; turned
# off by default to not distract the user: the focus in a terminal window
# should be on the output of commands, not on the prompt
#force_color_prompt=yes

if [ -n "$force_color_prompt" ]; then
    if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
    # We have color support; assume it's compliant with Ecma-48
    # (ISO/IEC-6429). (Lack of such support is extremely rare, and such
    # a case would tend to support setf rather than setaf.)
    color_prompt=yes
    else
    color_prompt=
    fi
fi

if [ "$color_prompt" = yes ]; then
    PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
else
    PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
fi
unset color_prompt force_color_prompt

# If this is an xterm set the title to user@host:dir
case "$TERM" in
xterm*|rxvt*)
    PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
    ;;
*)
    ;;
esac

# enable color support of ls and also add handy aliases
if [ -x /usr/bin/dircolors ]; then
    test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
    alias ls='ls --color=auto'
    #alias dir='dir --color=auto'
    #alias vdir='vdir --color=auto'

    alias grep='grep --color=auto'
    alias fgrep='fgrep --color=auto'
    alias egrep='egrep --color=auto'
fi

# colored GCC warnings and errors
#export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'

# some more ls aliases
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'

# Add an "alert" alias for long running commands.  Use like so:
#   sleep 10; alert
alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'

# Alias definitions.
# You may want to put all your additions into a separate file like
# ~/.bash_aliases, instead of adding them here directly.
# See /usr/share/doc/bash-doc/examples in the bash-doc package.

if [ -f ~/.bash_aliases ]; then
    . ~/.bash_aliases
fi

# enable programmable completion features (you don't need to enable
# this, if it's already enabled in /etc/bash.bashrc and /etc/profile
# sources /etc/bash.bashrc).
if ! shopt -oq posix; then
  if [ -f /usr/share/bash-completion/bash_completion ]; then
    . /usr/share/bash-completion/bash_completion
  elif [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
  fi
fi
unset JAVA_TOOL_OPTIONS

function factorial {
    if [ $1 -eq 1 ]
    then
        echo 1
    else
        local tmp=$[ $1 - 1 ]
        local res=`factorial $tmp`
        local ret=$[ $res * $1 ]
        echo $ret  
    fi
}
PATH=.:$PATH

    显然,这个文件只能在交互式启动时才能执行。会初始化历史记录的长度变量HISTSIZE。同时定义了一些命令的别名,如ls、grep会自动显示颜色。ll和l都是命令别名。最后自己定义了一个递归的阶乘函数factorial,以及在PATH前面追加一个当前目录。这个配置文件用于存放用户的一些自定义的脚本。如果在交互式Shell再运行bash,则会再次运行.bashrc。对于脚本,其运行时的Shell在最开头定义#!/bin/bash,这是非交互式Shell,一般不会运行任何配置文件。

1.6 重要的环境变量

环境变量名含义注意事项
USER当前用户名
HOME用户主目录
PATH冒号分割的Shell查找命令的目录列表查找EXE命令,且一般包含的是bin目录
IFSShell用来分割字符串的一系列字符read、awk以及单词分割都会使用这个环境变量。其值一般为’空格\t\n’

二、数组

  • 定义一个数组
array=(hello world you are 212)

    数组元素用空格隔开,而不是用逗号。

  • 访问某一项
echo ${array[1]}

    注意,对于数组要加花括号。第0项有两种输出方法:

echo ${array[0]}
echo $array
  • 输出整个数组
echo "${array[@]}"

    也可以用循环语句逐项输出。

  • 输出数组的长度
echo ${#array[@]}

    注意,未初始化的元素不会参与计数。这与其他语言不同。

  • 追加元素
array+=(d e f)

    注意+=不能出现空格。

  • 数组排序
array_sorted=$(for i in "${array[@]}"; do echo $i; done | sort)

    注意sort排序使用的是本地化设置,小写字母会出现在大写字母前面。

三、结构化命令

    下面的list表示的是命令、test命令、复合命令[[]]、复合命令(()),以及用&&、||等连接上述命令的表达式。

  • 条件语句
if list; then
 cmd1
elif list; then 
 cmd2
else
 cmd3
fi

    注意,使用方法和C语言类似。list命令执行成功则会执行对应的语句。

  • case 语句
case variable in
pattern1 | pttern2) cmd1;;
pattern3 | pattern4) cmd2;;
*) cmd3;;
esac

    注意上面要用;;结束,表示当前项匹配时,不会继续往下匹配。最后要用case反过来写的esac结束。对于每一项,可以使用|来提供相同命令的执行。最后一项是默认。这里的pattern和路径名匹配类似。

  • for语句
for variable in word; do
    cmd
done

    这里的word可以是一个字符串,或者是一个命令的执行结果,即命令替换,或者数组(一般要用引号包围),但是用$IFS分割。cmd使用variable时必须加$,因为它是一个变量。另外这个命令还可以直接输出或者重定向,即在done后加>或者|,将for的输出重定向,或者通过管道输出。

for (( expr1; expr2; expr3 )); do 
    cmd
done

    这是一种C语言风格的for语句,用法跟C语言类似。但是expr必须是算术表达式,不用加(()),可以加任意空格。

  • while语句
while list; do
    cmd
done

    跟C语言类似。只要list成立,则不断循环。

  • until语句
until list; do
    cmd
done

    这个语句表示当list成立时,退出循环。与while的语义正好相反。while和until语句都可以使用break和continue这两个控制命令,与C语言用法类似。

四、条件表达式

4.1 test命令

    test命令的格式如下:

test expression

    但是我们更常用它的等价命令:

[ expression ]

    注意这个命令的两边需要有空格,不然会报错。expression可以分为三类,分别是数值比较、字符串比较、文件比较。其中数值比较可以使用算术表达式(())替代。而test命令也有更高级更新的形式[[]]复合命令,它还支持字符串模式匹配。

  • 数值比较
arg1 OP arg2

    这里arg是数值,而OP是-eq, -ge, -gt, le, -lt, -ne。

  • 字符串比较
#字符串长度非空
-n string
#字符串长度为空
-z string
#字符串相等,[[]]还可以用==,同时它还支持模式匹配
string1 = string2
string1 != string2
#使用ASCII排序。对于test命令必须用\对<和>转义,[[]]不用。
string1 < string2
string1 > string2
  • 文件比较
#文件是否存在
-e file
#文件存在且是目录
-d file
#文件存在且是普通文件
-f file
#文件存在且可读
-r file
#文件存在且可写
-w file
#文件存在且可执行
-x file
#文件更新,file2可以不存在
file1 -nt file2
#文件更老,file1可以不存在
file1 -ot file2

4.2 算术表达式(())

    格式是

$((expression))

    其中$可以省略。算术表达式专门用于数值运算。expression如下,优先级相同的放在一组,同时优先级从上到下递减。

       id++ id--
       ++id --id
       - +    #正负号
       ! ~    #逻辑取反和位取反
       **     #幂运算
       * / %  #乘除取余
       + -    #加减
       << >>  #左右位移
       <= >= < > #比较
       == !=  #相等和不等
       &      #按位与
       ^      #按位异或
       |      #按位或
       &&     #逻辑与
       ||     #逻辑或
       expr?expr:expr
       = *= /= %= += -= <<= >>= &= ^= |=
       expr1 , expr2 #逗号表达式

     (()) 可以去掉,如果变量名没有定义将用0代替。null也会视为0。可以使用<、>、==比较数值大小。这里的=是赋值,与C语言类似。注意test中字符串相等是=,而[[]]可以是==。

五、字符串

#字符串长度
${#parameter}

#从offset开始的length个字符
${parameter:offset:length}

#从头开始去除最短匹配
${parameter#pattern}
#从开头开始去除最长匹配
${parameter##pattern}
#从尾开始去除最短匹配
${paramter%pattern}
#从尾开始去除最长匹配
${parameter%%pattern}

#从头替换第一个字符串
${paramter/pattern/string}
${paramter/#pattern/string}
#从尾替换第一个字符串
${paramter/%pattern/string}
#替换全部匹配的字符串
${parameter//pattern/string}

    注意,当没有给出string时,将删除pattern。

六、输入

6.1 read命令

    read的格式如下:

read -t timeout -p prompt -s var1 var2 var3...

    read将使用$IFS对输入的一行进行分割,以换行符作为读入结束的标志。其中,-t表示等待多少秒,超时没有输入则返回。-p输出提示字符。-s表示输入不回显,一般在输入密码时使用。后面的var1表示接收的变量名。read将分割的第一项给var1,第二项给var2,第三项给var3。对于多出的项全部给最后一项,对于少的置空。如果没有给出任何参数,则默认给变量$REPLY。
    由于这个命令是一行一行处理,所以可以用于处理文件。不过它不支持从管道读取。

6.2 here文档

    格式如下:

cat <<- _EOF_
str
_EOF_

    <<-后面的-表示忽略开始的tab,这样_EOF_就不用顶格写。_EOF_用于表示输入的起始和结束。

6.3 here字符串

cat <<< word

七、输出

7.1 回显echo

    默认情况下,echo会输出一个换行符号。如果不要输出换行符号,则必须加-n选项:

echo -n ${variable}

    如果要对转移字符进行解释,如\n,则必须加-e选项:

echo -e ${variable}

7.2 格式化输出printf

    这是格式化输出语句,默认不会有换行,和C语言用法基本一致。

printf format var1 var2 ...

    注意,后面的变量要加$。

八、函数

    函数的格式:

function func_name(){
}

    这里funciton关键字和括号可以省略其中一个。因为这里的函数的参数不会通过括号里给出,而且传递的参数的个数是任意的,所以括号没什么必要。那么在函数内部这些传递的参数是怎么获取到的呢?这是通过位置参数来获取的。
    位置参数的格式是$1, …, $9, ${10}, ${11}…。注意大于9的位置参数要加花括号,而且参数是从1开始算起的。另外,$0表示的脚本文件的名字,$#表示位置参数的个数。不仅可以给函数传递参数,还可以给脚本传递参数,而且获取的都是这些位置参数。获取所有参数的变量是”$@”。
    另外shift可以将位置参数整体往前移动一位,所以在有shift的while循环中,每次只需判断和利用\$1就可以了。

九、信号

    脚本中可以设置信号句柄,格式是:

trap cmd[func_handler] signal_name...

    其中,第一个参数命令后者函数句柄,第二个参数是信号的名字或者信号号码。如SIGINT,SIGTERM。

十、运行脚本

10.1 手动运行

    对于首行没有#!/bin/bash的且没有可执行权限的文件,可以在当前Shell运行:

source filename
. filename

    对于一般的文件,则要在首行添加#!/bin/bash,并修改可执行权限。

chmod u+x filename

    运行时脚本的目录必须在PATH目录下,或者当前目录.在PATH目录下,这样可以直接用文件名运行。

filename

    最后一种方法,可以直接用完整路径名运行。

#脚本在当前工作目录中
./filename
#任意目录
full_direcory/filename

10.2 自动运行

    主要有两种方式,一种是在将自己的脚本写在/etc/rc.local里面,这样开机时init会创建该进程。另一种是在/etc/profile.d下放置自己的sh脚本,这样用户登录时会执行。或者在~/.bashrc中定义自己的脚本,每次打开一个交互式终端,都会执行。

10.3 在非控制台下运行

    当控制终端会话退出时,会收到一个SIGHUP信号,这时所有在这个终端上的进程都会被终止。如果要使进程能够继续运行,则必须屏蔽该信号,即

nohup script &

    但该脚本的输出将导出到脚本所在目录下的nohup.out中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值