一个程序演示所有的shell编程知识

Shell
演示程序

001#!/bin/sh -x
002#由反引号括起来的也是一条命令,Shell先执行该命令,然后将输出结果立刻代换到当前命令行中。例如定义一个变量存放date命令的输出:
003echo `date`
004#命令代换也可以用$()表示:
005echo $(date)
006  
007#如果一个变量叫做VARNAME,用${VARNAME}可以表示它的值,在不引起歧义的情况下也可以用$VARNAME表示它的值。通过以下例子比较这两种表示法的不同:
008echo $SHELL
009echo $SHELLabc
010echo ${SHELL}abc
011  
012#单引号用于保持引号内所有字符的字面值,即使引号内的\和回车也不例外,但是字符串中不能出现单引号
013echo '$SHELL'
014echo "$SHELL"
015echo '$SHELL
016          hello world'
017#双引号用于保持引号内所有字符的字面值(回车也不例外),除以下情况外:
018#$加变量名可以取变量的值
019#反引号仍表示命令替换
020#\$表示$的字面值
021#\`表示`的字面值
022#\"表示"的字面值
023#\\表示\的字面值
024echo "$SHELL
025          hello world"
026echo "\$SHELL
027          hello world"
028  
029#命令test或[可以测试一个条件是否成立,如果测试结果为真,则该命令的Exit Status为0,如果测试结果为假,则命令的Exit Status为1
030VAR=2
031test $VAR -gt 1
032echo $?
033  
034test $VAR -gt 3
035echo $?
036  
037[ $VAR -gt 3 ]
038echo $?
039#存在Desktop目录且VAR等于abc
040VAR=abc
041[ -d Desktop -a $VAR = 'abc' ]
042echo $?
043  
044#在Shell中用if、then、elif、else、fi这几条命令实现分支控制
045echo "Is it morning? Please answer yes or no."
046read YES_OR_NO
047if [ "$YES_OR_NO" = "yes" ]; then
048        echo "Good morning!"
049elif [ "$YES_OR_NO" = "no" ]; then
050        echo "Good afternoon!"
051else
052        echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
053        exit 1
054fi
055  
056#C语言的case只能匹配整型或字符型常量表达式,而Shell脚本的case可以匹配字符串和Wildcard,每个匹配分支可以有若干条命令,末尾为;;
057echo "Is it morning? Please answer yes or no."
058read YES_OR_NO
059case "$YES_OR_NO" in
060        yes|y|Yes|YES)
061        echo "Good Morning!";;
062        [nN]*)
063        echo "Good Afternoon!";;
064        *)
065        echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
066        exit 1;;
067esac
068  
069#FRUIT是一个循环变量,第一次循环$FRUIT的取值是apple,第二次取值是banana,第三次取值是pear
070for FRUIT in apple banana pear; do
071          echo "I like $FRUIT"
072done
073  
074#while的用法和C语言类似
075echo "Enter password:"
076read TRY
077while [ "$TRY" != "secret" ]; do
078        echo "Sorry, try again"
079        read TRY
080done
081  
082COUNTER=1
083while [ "$COUNTER" -lt 10 ]; do
084        echo "COUNTER is $COUNTER"
085        COUNTER=$(($COUNTER+1))
086done
087  
088#$0     相当于C语言main函数的argv[0]
089echo "The program $0 is now running"
090#$1、$2...       这些称为位置参数(Positional Parameter),相当于C语言main函数的argv[1]、argv[2]...
091echo "The first parameter is $1"
092echo "The second parameter is $2"
093$@      表示参数列表"$1" "$2" ...,例如可以用在for循环中的in后面
094for PARAM in $@; do
095        echo "PARAM $PARAM"
096done
097  
098echo "The parameter list is $@"
099#位置参数可以用shift命令左移
100shift
101echo "The first parameter is $1"
102echo "The second parameter is $2"
103echo "The parameter list is $@"
104  
105#数就像是迷你脚本,调用函数时可以传任意个参数,在函数内同样是用$0、$1、$2等变量来提取参数
106#函数中的位置参数相当于函数的局部变量,改变这些变量并不会影响函数外面的$0、$1、$2等变量
107#函数中可以用return命令返回,如果return后面跟一个数字则表示函数的Exit Status
108  
109is_directory()
110{
111        DIR_NAME=$1
112        if [ ! -d $DIR_NAME ]; then
113                return 1
114        else
115                return 0
116        fi
117}
118  
119for DIR in "$@"; do
120        if is_directory "$DIR"
121        then :
122        else
123                echo "$DIR doesn't exist. Creating it now..."
124                mkdir $DIR > /dev/null 2>&1
125                if [ $? -ne 0 ]; then
126                        echo "Cannot create directory $DIR"
127                        exit 1
128                fi
129        fi
130done


sh与bash区别
在我们所使用的系统当中,使用sh调用执行脚本,相当于打开了bash的POSIX标准模式 (等效于bash的 --posix 参数)

一般的,sh是bash的“子集” (不是子集的部分,具体区别见下的“Things sh has that bash does not”)

例子:

01[wwy@sf-watch test]$ cat t2.sh 
02#!/bin/bash
03diff <(echo xxx) <(echo yyy) # 此语法包含bash的特性,不属于sh的POSIX标准
04  
05[wwy@sf-watch test]$ bash -x ./t2.sh # 使用bash 调用,不会出问题
06+ diff /dev/fd/63 /dev/fd/62
07++ echo xxx
08++ echo yyy
091c1
10< xxx
11---
12> yyy
13[wwy@sf-watch test]$ sh ./t2.sh # 而用sh调用,报错如下
14./t2.sh: line 3: syntax error near unexpected token `('
15./t2.sh: line 3: `diff <(echo xxx) <(echo yyy)'
16[wwy@sf-watch test]$ echo $?

2

但是,在我们的linux系统中,sh是bash的一个软链接:

1[wangweiyu@ComSeOp mon]$ which sh /bin/sh [wangweiyu@ComSeOp mon]$ ls -l /bin/sh lrwxrwxrwx 1 root root 4 Mar 21 2007 /bin/sh -> bash

那为什么上面的例子中还会出现问题呢?原因在于: bash程序执行,当“$0”是“sh”的时候,则要求下面的代码遵循一定的规范,当不符合规范的语法存在时,则会报错, 所以可以这样理解, “sh”并不是一个程序,而是一种标准(POSIX),这种标准,在一定程度上保证了脚本的跨系统性(跨UNIX系统)

下面的内容详细的说明了bash与sh在语法等方面的具体差异:


001Things bash has that sh does not:
002  
003   long invocation options
004   [+-]O invocation option
005   -l invocation option
006   `!' reserved word to invert pipeline return value
007   `time' reserved word to time pipelines and shell builtins
008   the `function' reserved word
009   the `select' compound command and reserved word
010   arithmetic for command: for ((expr1 ; expr2; expr3 )); do list; done
011   new $'...' and $"..." quoting
012   the $(...) form of command substitution
013   the $(<filename) form of command substitution, equivalent to
014      $(cat filename)
015   the ${#param} parameter value length operator
016   the ${!param} indirect parameter expansion operator
017   the ${!param*} prefix expansion operator
018   the ${param:offset[:length]} parameter substring operator
019   the ${param/pat[/string]} parameter pattern substitution operator
020   expansions to perform substring removal (${p%[%]w}, ${p#[#]w})
021   expansion of positional parameters beyond $9 with ${num}
022   variables: BASH, BASH_VERSION, BASH_VERSINFO, UID, EUID, REPLY,
023         TIMEFORMAT, PPID, PWD, OLDPWD, SHLVL, RANDOM, SECONDS,
024         LINENO, HISTCMD, HOSTTYPE, OSTYPE, MACHTYPE, HOSTNAME,
025         ENV, PS3, PS4, DIRSTACK, PIPESTATUS, HISTSIZE, HISTFILE,
026         HISTFILESIZE, HISTCONTROL, HISTIGNORE, GLOBIGNORE, GROUPS,
027         PROMPT_COMMAND, FCEDIT, FIGNORE, IGNOREEOF, INPUTRC,
028         SHELLOPTS, OPTERR, HOSTFILE, TMOUT, FUNCNAME, histchars,
029         auto_resume
030   DEBUG trap
031   ERR trap
032   variable arrays with new compound assignment syntax
033   redirections: <>, &>, >|, <<<, [n]<&word-, [n]>&word-
034   prompt string special char translation and variable expansion
035   auto-export of variables in initial environment
036   command search finds functions before builtins
037   bash return builtin will exit a file sourced with `.'
038   builtins: cd -/-L/-P, exec -l/-c/-a, echo -e/-E, hash -d/-l/-p/-t.
039        export -n/-f/-p/name=value, pwd -L/-P,
040        read -e/-p/-a/-t/-n/-d/-s/-u,
041        readonly -a/-f/name=value, trap -l, set +o,
042        set -b/-m/-o option/-h/-p/-B/-C/-H/-P,
043        unset -f/-v, ulimit -i/-m/-p/-q/-u/-x,
044        type -a/-p/-t/-f/-P, suspend -f, kill -n,
045        test -o optname/s1 == s2/s1 < s2/s1 > s2/-nt/-ot/-ef/-O/-G/-S
046   bash reads ~/.bashrc for interactive shells, $ENV for non-interactive
047   bash restricted shell mode is more extensive
048   bash allows functions and variables with the same name
049   brace expansion
050   tilde expansion
051   arithmetic expansion with $((...)) and `let' builtin
052   the `[[...]]' extended conditional command
053   process substitution
054   aliases and alias/unalias builtins
055   local variables in functions and `local' builtin
056   readline and command-line editing with programmable completion
057   command history and history/fc builtins
058   csh-like history expansion
059   other new bash builtins: bind, command, compgen, complete, builtin,
060             declare/typeset, dirs, enable, fc, help,
061             history, logout, popd, pushd, disown, shopt,
062             printf
063   exported functions
064   filename generation when using output redirection (command >a*)
065   POSIX.2-style globbing character classes
066   POSIX.2-style globbing equivalence classes
067   POSIX.2-style globbing collating symbols
068   egrep-like extended pattern matching operators
069   case-insensitive pattern matching and globbing
070   variable assignments preceding commands affect only that command,
071      even for builtins and functions
072   posix mode and strict posix conformance
073   redirection to /dev/fd/N, /dev/stdin, /dev/stdout, /dev/stderr,
074      /dev/tcp/host/port, /dev/udp/host/port
075   debugger support, including `caller' builtin and new variables
076   RETURN trap
077   the `+=' assignment operator
078  
079Things sh has that bash does not:
080   uses variable SHACCT to do shell accounting
081   includes `stop' builtin (bash can use alias stop='kill -s STOP')
082   `newgrp' builtin
083   turns on job control if called as `jsh'
084   $TIMEOUT (like bash $TMOUT)
085   `^' is a synonym for `|'
086   new SVR4.2 sh builtins: mldmode, priv
087  
088Implementation differences:
089   redirection to/from compound commands causes sh to create a subshell
090   bash does not allow unbalanced quotes; sh silently inserts them at EOF
091   bash does not mess with signal 11
092   sh sets (euid, egid) to (uid, gid) if -p not supplied and uid < 100
093   bash splits only the results of expansions on IFS, using POSIX.2
094      field splitting rules; sh splits all words on IFS
095   sh does not allow MAILCHECK to be unset (?)
096   sh does not allow traps on SIGALRM or SIGCHLD
097   bash allows multiple option arguments when invoked (e.g. -x -v);
098      sh allows only a single option argument (`sh -x -v' attempts
099      to open a file named `-v', and, on SunOS 4.1.4, dumps core.
100      On Solaris 2.4 and earlier versions, sh goes into an infinite
101      loop.)
102   sh exits a script if any builtin fails; bash exits only if one of
103      the POSIX.2 `special' builtins fails



调用相关:

在脚本的调用方面(interactive、login相关),bash与sh也是存在差异 以下是详细说明(假如被调用执行的脚本名字叫xxx.sh)

BASH: 1、 交互式的登录shell (bash –il xxx.sh) 载入的信息:

1/etc/profile
2~/.bash_profile( -> ~/.bashrc -> /etc/bashrc)
3~/.bash_login
4~/.profile

2、非交互式的登录shell (bash –l xxx.sh) 载入的信息:
1/etc/profile
2~/.bash_profile ( -> ~/.bashrc -> /etc/bashrc)
3~/.bash_login
4~/.profile
5$BASH_ENV

3、交互式的非登录shell (bash –i xxx.sh) 载入的信息:
1~/.bashrc ( -> /etc/bashrc)

4、非交互式的非登录shell (bash xxx.sh) 载入的信息:
1$BASH_ENV
2  
3SH:

1、交互式的登录shell 载入的信息:

1/etc/profile
2~/.profile

2、非交互式的登录shell 载入的信息:
1/etc/profile
2~/.profile

3、交互式的非登录shell 载入的信息:
4、非交互式的非登录shell 载入的信息: nothing

由此可以看出,最主要的区别在于相关配置文件的是否载入, 而这些配置的是否载入,也就导致了很多默认选项的差异 (具体请仔细查看~/.bash_profile 等文件) 如:

1[wangweiyu@ComSeOp ~]$ grep ulimit /etc/profile 
2ulimit -S -c unlimited > /dev/null 2>&1

即,如果/etc/profile没有被载入,则不会产生core dump

值得一提的是,使用ssh远程执行命令, 远端sshd进程通过“bash –c”的方式来执行命令(即“非交互式的非登录shell”) 所以这一点,和登录之后再在本地执行执行命令,就存在了一定的差异

如:

01[wangweiyu@ComSeOp ~]$ ssh wangweiyu@127.0.0.1 'echo $-'
02wangweiyu@127.0.0.1 's password: 
03hBc
04[wangweiyu@ComSeOp ~]$ echo $-
05himBH
06[wangweiyu@ComSeOp ~]$ ssh wangweiyu@127.0.0.1 'echo $0'
07wangweiyu@127.0.0.1 's password: 
08bash
09[wangweiyu@ComSeOp ~]$ echo $0
10-bash

注: “$-” 中含有“i”代表“交互式shell” “$0”的显示结果为“-bash”,bash前面多个“-”,代表“登录shell” 没有“i“和“-”的,是“非交互式的非登录shell”

另外还有一点,虽然ssh远程执行的命令是“非交互式的非登录shell”,但在执行命令之前,ssh的那一次登录本身是“交互式的登录shell”,所以其会先读一下“~/.bash_profile”

如:

01[wangweiyu@ComSeOp ~]$ cat .bashrc 
02# .bashrc
03# User specific aliases and functions
04# Source global definitions
05if [ -f /etc/bashrc ]; then
06. /etc/bashrc
07fi
08echo 'xxx'
09  
10[wangweiyu@ComSeOp ~]$ ssh wangweiyu@127.0.0.1 'echo $-'
11wangweiyu@127.0.0.1 's password: 
12xxx
13hBc

这一点,衍生出一个关于scp的问题,scp在传输数据之前,会先进行一次ssh登录, 而当.bashrc文件有输出的时候,则会导致scp失败!原因是解析返回的数据包出现混乱

如:

01[wangweiyu@ComSeOp ~]$ cat .bashrc 
02# .bashrc
03# User specific aliases and functions
04# Source global definitions
05if [ -f /etc/bashrc ]; then
06. /etc/bashrc
07fi
08echo 'xxx'
09[wangweiyu@ComSeOp ~]$ scp file wangweiyu@127.0.0.1 :/tmp
10wangweiyu@127.0.0.1 's password: 
11xxx
12[wangweiyu@ComSeOp ~]$ echo $?
131
14[wangweiyu@ComSeOp ~]$ ls /tmp/
15[wangweiyu@ComSeOp ~]$

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值