Shell
演示程序
041 | [ -d Desktop -a $VAR = 'abc' ] |
045 | echo "Is it morning? Please answer yes or no." |
047 | if [ "$YES_OR_NO" = "yes" ]; then |
049 | elif [ "$YES_OR_NO" = "no" ]; then |
050 | echo "Good afternoon!" |
052 | echo "Sorry, $YES_OR_NO not recognized. Enter yes or no." |
057 | echo "Is it morning? Please answer yes or no." |
061 | echo "Good Morning!" ;; |
063 | echo "Good Afternoon!" ;; |
065 | echo "Sorry, $YES_OR_NO not recognized. Enter yes or no." |
070 | for FRUIT in apple banana pear; do |
075 | echo "Enter password:" |
077 | while [ "$TRY" != "secret" ]; do |
078 | echo "Sorry, try again" |
083 | while [ "$COUNTER" - lt 10 ]; do |
084 | echo "COUNTER is $COUNTER" |
085 | COUNTER=$(($COUNTER+1)) |
089 | echo "The program $0 is now running" |
091 | echo "The first parameter is $1" |
092 | echo "The second parameter is $2" |
093 | $@ 表示参数列表 "$1" "$2" ...,例如可以用在 for 循环中的 in 后面 |
098 | echo "The parameter list is $@" |
101 | echo "The first parameter is $1" |
102 | echo "The second parameter is $2" |
103 | echo "The parameter list is $@" |
112 | if [ ! -d $DIR_NAME ]; then |
120 | if is_directory "$DIR" |
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" |
sh与bash区别
在我们所使用的系统当中,使用sh调用执行脚本,相当于打开了bash的POSIX标准模式 (等效于bash的 --posix 参数)
一般的,sh是bash的“子集” (不是子集的部分,具体区别见下的“Things sh has that bash does not”)
例子:
01 | [wwy@sf- watch test ]$ cat t2.sh |
03 | diff <( echo xxx) <( echo yyy) |
05 | [wwy@sf- watch test ]$ bash -x ./t2.sh |
06 | + diff /dev/fd/63 /dev/fd/62 |
13 | [wwy@sf- watch test ]$ sh ./t2.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在语法等方面的具体差异:
001 | Things bash has that sh does not: |
003 | long invocation options |
004 | [+-]O 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 |
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 |
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, |
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 |
051 | arithmetic expansion with $((...)) and ` let ' builtin |
052 | the `[[...]]' extended conditional command |
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 , |
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 |
077 | the `+=' assignment operator |
079 | Things 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') |
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 |
088 | Implementation 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 |
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) 载入的信息:
2 | ~/.bash_profile( -> ~/.bashrc -> /etc/bashrc) |
2、非交互式的登录shell (bash –l xxx.sh) 载入的信息:
2 | ~/.bash_profile ( -> ~/.bashrc -> /etc/bashrc) |
3、交互式的非登录shell (bash –i xxx.sh) 载入的信息:
1 | ~/.bashrc ( -> /etc/bashrc) |
4、非交互式的非登录shell (bash xxx.sh) 载入的信息:
1、交互式的登录shell 载入的信息:
2、非交互式的登录shell 载入的信息:
3、交互式的非登录shell 载入的信息:
4、非交互式的非登录shell 载入的信息: nothing
由此可以看出,最主要的区别在于相关配置文件的是否载入, 而这些配置的是否载入,也就导致了很多默认选项的差异 (具体请仔细查看~/.bash_profile 等文件) 如:
1 | [wangweiyu@ComSeOp ~]$ grep ulimit /etc/profile |
2 | ulimit -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 $-' |
02 | wangweiyu@127.0.0.1 's password: |
04 | [wangweiyu@ComSeOp ~]$ echo $- |
06 | [wangweiyu@ComSeOp ~]$ ssh wangweiyu@127.0.0.1 'echo $0' |
07 | wangweiyu@127.0.0.1 's password: |
09 | [wangweiyu@ComSeOp ~]$ echo $0 |
注: “$-” 中含有“i”代表“交互式shell” “$0”的显示结果为“-bash”,bash前面多个“-”,代表“登录shell” 没有“i“和“-”的,是“非交互式的非登录shell”
另外还有一点,虽然ssh远程执行的命令是“非交互式的非登录shell”,但在执行命令之前,ssh的那一次登录本身是“交互式的登录shell”,所以其会先读一下“~/.bash_profile”
如:
01 | [wangweiyu@ComSeOp ~]$ cat .bashrc |
05 | if [ -f /etc/bashrc ]; then |
10 | [wangweiyu@ComSeOp ~]$ ssh wangweiyu@127.0.0.1 'echo $-' |
11 | wangweiyu@127.0.0.1 's password: |
这一点,衍生出一个关于scp的问题,scp在传输数据之前,会先进行一次ssh登录, 而当.bashrc文件有输出的时候,则会导致scp失败!原因是解析返回的数据包出现混乱
如:
01 | [wangweiyu@ComSeOp ~]$ cat .bashrc |
05 | if [ -f /etc/bashrc ]; then |
09 | [wangweiyu@ComSeOp ~]$ scp file wangweiyu@127.0.0.1 :/tmp |
10 | wangweiyu@127.0.0.1 's password: |
12 | [wangweiyu@ComSeOp ~]$ echo $? |
14 | [wangweiyu@ComSeOp ~]$ ls /tmp/ |