Shell脚本学习笔记-语法

Solaris下Shell脚本学习笔记。

Shell语法
Shell中变量都是全局的,如果需要在函数中使用局部变量,在变量前加上local
'expression'单引号括起的表达式中不允许变量扩展;
"expression"双引号中则允许,可以用来连接变量, fullname="$firstname $lastname"
(expression1;expression2;...)括号中的指令形成指令群,也成为subshell,
    其中定义的变量作用范围限定于指令群本身,也不会对主脚本的状态产生影响.
    例如tar cf - * | ( cd /tmp/; tar -xf -) 执行完毕后当前目录不会改变
((expression)) 和let命令类似用于算术运算
     ((b=10**2)); echo $b; 等同于let b=10**2; **表示乘方
{} 大括号可用来表示逗号隔开的字符串的组合,
    ls ab{c,d}.txt  相当于 ls abc.txt abd.txt;
    mkdir {ab,cd}ef{12,34} 相当于mkdir abef12 abef34 cdef12 cdef34

$? 上次执行的shell返回结果
$$ 代表进程ID;
$!最后一个后台命令的进程ID(可供稍后调用wait使用);
$PPID 父进程ID;

$PS1命令提示符,默认为$;
$PS2 行继续提示符,默认为>;
$PS4 用set -x打开跟踪,执行跟踪的提示符,默认为+

$1, $2...${10}... 读取脚本中的参数, 超过9加大括号; $0为指令本身; $#为变量的总数.
$@("$@"="$1" "$2" "$3" ...)或者$*("$*"= "$1 $2 $3 ...")列出所有的参数,
    在不加双引号的情况下,两者相同

${VAR-DEFAULT} 或者 ${VAR=DEFAULT} 使用一个默认值来代替那些不存在的变量;
    如果变量不存在后者同时会导致VAR被设置为DEFAULT.
${VAR:-DEFAULT} 或者 ${VAR:=DEFAULT} 使用一个默认值来代替那些不存在或者值为空的变量;
    如果变量不存在或者为空后者同时会导致VAR被设置为DEFAULT.
${VAR?MSG} 如果变量不存在显示出错误MSG并中止脚本的执行同时返回退出码1,否则返回VAR;
${VAR:?MSG} 如果变量不存在或者为空显示错误MSG并中止脚本的执行同时返回退出码1,否则返回VAR;

${VAR+MSG} 如果变量不存在就会返回null,否则返回MSG;
${VAR:+MSG} 如果变量不存在或者值为空就返回null,否则返回MSG
${#VAR} 变量的长度
${VAR%word} 如果word匹配变量结尾部分,删除匹配的最短部分,然后返回剩余部分
${VAR%%word} 如果word匹配变量结尾部分,删除匹配的最长部分,然后返回剩余部分
${VAR#word} 如果word匹配变量开始部分,删除匹配的最短部分,然后返回剩余部分
${VAR##word} 如果word匹配变量开始部分,删除匹配的最长部分,然后返回剩余部分

$(command) 或者 `command` 返回当前执行命令的结果
$((...)) 对括号内的表达式求值, 例如 x=2; x=$(($x**3)); echo $x

: 不做任何事情,只作参数展开,但是返回值为0. :>file 等同于 cat /dev/null > file;
. 读取文件并执行文件中的内容,注意是在当前shell进程中执行,变量赋值,函数定义,改变目录等影响都是全局的

使用read读取输入时默认行为是把输入行尾的反斜杠\看作续行符, -r选项可以不把\看作续行符.
使用read读取输入时使用$IFS进行字段切分.例如
    while IFS=: read user password uid gid fullname homedir shell
    do
          echo $user $password $uid $gid $fullname $homedir $shell
    done < /etc/passwd
*在执行某个命令时临时修改某个变量: PATH=.:/usr/bin test.sh
*输入重定向置于循环体结尾,也可以使用管道 cat /etc/passwd | while ...
*读取密码stty -echo;read pass < /dev/tty;stty echo;

eval命令读取它的参数然后把结果作为command给shell执行,
在变量替换或命令输出作为命令执行的情况下,比较有用.
例如, x="ls | more", 分别执行$x 和eval $x, 前者不能得到正确结果。

command在查找待执行的命令时,避开shell函数; -p 查找命令时,使用$PATH的默认值

shell查找命令的顺序:特殊内建命令->shell函数->一般内建命令->$PATH目录下的外部命令
特殊内建命令有: . break continue eval exec exit export readonly return set shift times trap unset
一般内建命令有: alias bg cd command false fc fg getopts jobs kill newgrp pwd read true umask unalias wait
所以可以用shell函数来覆盖一般内建命令,例如
cd () {
command cd "$@"
x=`pwd`
PS1="${x##*/}\$ "
}

cd - 变更工作目录到上一次工作目录
cd ~或者cd 变更工作目录到当前用户目录
cd ~user变更工作目录到user用户目录

test命令
-e file 是否存在; -d file 是否目录; -f file 是否一般文件; -h file | -L file 是否符号链接;
-s file 是否文件为空
-r file; -w file; -x file 是否可读;是否可写;是否可执行文件或者可被查找的目录
-n string 是否string不为null; -z string 是否string为null;
s1 = s2 字符串相同(注意=符号前后的空格); s1 != s2 字符串不相同
n1 -eq n2; n1 -ne n2; n1 -lt n2; n1 -gt n2; n1 -le n2; n1 -ge n2 整数比较
*前置!表示否定结果; -a 逻辑与; -o 逻辑或
*所有的变量展开都加上"",防止变量本身为空引起的问题,例如 test -f "$FILE"
*[] 中括号用于条件判断符号,可理解为指向test命令的一个软链接,用法可完全参照test.
    注意紧挨着括号要有空格,例如 if [ ! -f file ]

if [ ... ]
then
...
elif [ ... ]
then
...
else
...
fi

case $1 in
valueA)
...
;;
valueB | valueC)
...
;;
*)
...
;;
esac

for i [in list]
do
...
done
*in list可选,如果省略等同in "$@", 循环整个参数列表.
*list的写法例: for i in 3 4 5; for i in *.txt; for i in {1..10}; for ((i=1;i<=10;++i))

{ while | until } condition
do
...
done

利用getopts命令处理输入参数.
file= verbose= quiet= long=

while getopts :vqf:l opt
do
  case $opt in
  v) verbose=true
     quiet=
     ;;
  q) quiet=true
     verbose=
     ;;
  f) file=$OPTARG
     ;;
  l) long=true
     ;;
  ?) echo "$0: invalid option -$OPTARG" >&2
     echo "Usage: $0 [-f file] [-vql] [files...]" >&2
     exit 1
     ;;
  esac
done
shift $((OPTIND - 1))
*选项后面的:表示该选项必须提供一个参数.getopts会把参数值放到变量OPTARG中.
*OPTIND被设置为下一个待处理参数的索引值.
*:作为选项字符串的第一个字符,会导致针对非法选项
    1)不输出错误信息;
    2)将变量$opt设置为?并且把非法选项放到变量OPTARG中.
*最后的shift $((OPTIND - 1))删除所有选项.留下参数,可以利用$1,$2...获得.
*注意OPTIND是全局的,如果某个函数也需要调用getopts来解析自己的参数,需要重设OPTIND为1.

<<可以在脚本中提供嵌入输入并可以在嵌入文件中使用变量替换,
cat << EOF
...
$HOME
...
EOF
*如果要避免发生变量替换,可以把定界符用引号括起来,例如 cat << 'EOF'
*使用<<-形式则删除嵌入文本中每一行开始的tab(空格不会删除),这样可以提高shell脚本的可读性

exec命令可以用来进行IO重定向,改变文件描述符,例如
exec 3 read arg1 arg2 < &3
再例如,
exec 99>&2     文件描述符99指向标准错误输出
exec 2>/tmp/$0.log  文件描述符2指向文件
...
exec 2>&99    恢复文件描述符2为标准错误输出
exec 99>&-    关闭文件描述符99

set命令
1)不带参数的set打印所有的shell变量
2)+-分别表示关闭/打开特定的shell选项。($- 表示当前已经打开的shell选项)
-a allexport,export所有后续定义的变量
-b notify,立即显示作业完成的信息,而不是等待下一个提示符。
-C noclobber,打开防止覆盖,>重定向遇到目标文件存在就会失败,这种情况下可以使用>|来做重定向。
-e errexit,命令退出值非零则退出shell。
-f noglob,停用通配符展开
-m monitor,打开作业控制(默认是打开的)
-n noexec,读取命令并且检查语法错误但是不执行。在交互式shell下忽略此选项。
-u nounset,视未定义的变量为错误而不是作为null。
-v verbose,在执行命令前打印命令行,不做变量替换。
-x xtrace,在执行命令前打印命令行,做变量替换。另一种方式 bash -x scriptfile。
- 关闭-v -x选项。 

trap命令用来捕获信号 trap 'command' signal lis
信号可以用数字也可以用名字,可以使用man signal.h查看对应关系.
除了标准信号trap还提供一个特殊信号EXIT或者0,表示脚本正常终止。

Xargs用法详解
之所以能用到这个命令,关键是由于很多命令不支持|管道来传递参数,而日常工作中有有这个必要,所以就有了xargs命令,例如:
find /sbin -perm +700 |ls -l 这个命令是错误的
find /sbin -perm +700 |xargs ls -l 这样才是正确的
xargs 可以读入 stdin 的资料,并且以空白字元或断行字元作为分辨,将 stdin 的资料分隔成为 arguments 。 因为是以空白字元作为分隔,所以,如果有一些档名或者是其他意义的名词内含有空白字元的时候, xargs 可能就会误判了

选项解释
-0 当sdtin含有特殊字元时候,将其当成一般字符,比如 /'空格 等
-a file 从文件中读入作为sdtin
-e flag ,注意有的时候可能会是-E,flag必须是一个以空格分隔的标志,当xargs分析到含有flag这个标志的时候就停止。例如

$ cat 0
1
2
3
$ cat 0 | xargs -E '2' echo
1

-p 当每次执行一个argument的时候询问一次用户。
-n num 后面加次数,表示命令在执行的时候一次用的argument的个数,默认是用所有的。例

$ cat 0 | xargs -E '3' -p -n 1 echo
echo 1?...y
1
echo 2?...y
2
-t 表示先打印命令,然后再执行。
-i ,这得看linux支持了,将xargs的每项名称,一般是一行一行赋值给{},可以用{}代替。例如
$ ls
0  1  2
$ ls | xargs -i mv {} {}.bk
$ ls
0.bk  1.bk  2.bk
-I,-i已经是deprecated参数,-I可以指定替换字符串。例如
$ ls | xargs -I x mv x x.bk
$ ls
0.bk.bk  1.bk.bk  2.bk.bk 


你可能感兴趣的:(Unix/Linux)