bash语法

来源:互联网 发布:可云的孩子是谁的 知乎 编辑:程序博客网 时间:2024/04/29 10:26

一、变量

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  fifi# 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 ifiexport JAVA_HOME=/usr/lib/jvm/jdk1.8.0_45export CLASSPATH=.:$JAVA_HOME/libexport MYSQL_HOME=/usr/local/mysqlexport CATALINA_BASE=/opt/apache-tomcat-8.0.28export CATALINA_HOME=/opt/apache-tomcat-8.0.28export TOMCAT_HOME=/opt/apache-tomcat-8.0.28export SQLITE_HOME=/home/gzx/sqlite3export M2_HOME=/home/gzx/apache-maven-3.3.9export 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 bashif [ -n "$BASH_VERSION" ]; then    # include .bashrc if it exists    if [ -f "$HOME/.bashrc" ]; then    . "$HOME/.bashrc"    fifi# set PATH so it includes user's private bin if it existsif [ -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 hintif [ ! -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    esacfi# if the command-not-found package is installed, use itif [ -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 anythingcase $- in    *i*) ;;      *) return;;esac# don't put duplicate lines or lines starting with space in the history.# See bash(1) for more optionsHISTCONTROL=ignoreboth# append to the history file, don't overwrite itshopt -s histappend# for setting history length see HISTSIZE and HISTFILESIZE in bash(1)HISTSIZE=1000HISTFILESIZE=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=yesif [ -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=    fifiif [ "$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\$ 'fiunset color_prompt force_color_prompt# If this is an xterm set the title to user@host:dircase "$TERM" inxterm*|rxvt*)    PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"    ;;*)    ;;esac# enable color support of ls and also add handy aliasesif [ -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 aliasesalias ll='ls -alF'alias la='ls -A'alias l='ls -CF'# Add an "alert" alias for long running commands.  Use like so:#   sleep 10; alertalias 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_aliasesfi# 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  fifiunset JAVA_TOOL_OPTIONSfunction 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目录 IFS Shell用来分割字符串的一系列字符 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 cmd1elif list; then  cmd2else cmd3fi

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

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

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

  • for语句
for variable in word; do    cmddone

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

for (( expr1; expr2; expr3 )); do     cmddone

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

  • while语句
while list; do    cmddone

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

  • until语句
until list; do    cmddone

    这个语句表示当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 = string2string1 != string2#使用ASCII排序。对于test命令必须用\对<和>转义,[[]]不用。string1 < string2string1 > 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中。

1 0
原创粉丝点击