Yifei Kong

Jul 16, 2017

shell 编程教程

变量和值

variables are referenced by $var or ${var}. Global variables are visible to all sub bash sessions, and are often called env variables, local variables are only visible to local session, not subsessions.

Global variables can be viewed as env, and can be created by export.

TEST=testing; export $TEST # or
export TEST=testing # NOTE: no $

Useful variables

HOME    Same to ~
IFS 
PATH    Search path
EUID    User id
GROUPS  Groups for current user
HOSTNAME    Hostname
LANG    
LC_ALL  
OLDPWD  
PWD 

定义和使用变量

定义变量,注意因为 shell 中的语法使用空格作为命令分割的限制,等于号前后不能加空格。

FOO=bar

使用变量,需要添加上 $ 符号。

echo $FOO

字符串在双引号中可以直接插入,这时候要加上大括号来指示变量名的起始位置。

echo "${FOO}xxx"

变量默认实在当前的回话中可见的,而不会作为环境变量传递给调用的命令。可以使用 export 导出变量,或者在命令前加上指定的环境变量。

-> % cat env.py
import os
print('FOO env variable is: ', os.environ.get('FOO'))

-> % python3 env.py
FOO env variable is:  None

-> % FOO=bar python3 env.py
FOO env variable is:  bar

使用 export

-> % export FOO=bar
-> % python3 env.py
FOO env variable is:  bar

一些有用的内置变量

$HOME     家目录,比如 /home/kongyifei
$IFS      默认的分隔符,和 for 循环紧密相关
$PATH     搜索路径,当你执行 ls 的时候,shell 会在这个变量中查找 ls 命令
$EUID     当前有效用户 ID
$LANG     
$LC_ALL   
$OLDPWD   上一个工作目录
$PWD      当前工作目录

数组

使用小括号来定义一个数组,关于 for 循环随后会讲

A=(1 2 3)

for el in ${A[@]}; do
    echo $el
done

字符串操作

大括号里面的字符串会被展开成独立的字符串

% echo {1,2,3,4}
1 2 3 4
% mkdir -p test/{a,b,c,d}{1,2,3,4}
% ls test/
a1  a2  a3  a4  b1  b2  b3  b4  c1  c2  c3  c4  d1  d2  d3  d4
% mv test/{a,c}.conf  # 这个命令的意思是:mv test/a.conf test/c.conf

切片: ${string:start:length}

默认值 ${var:-default}

设定值 ${var:=default}

长度 ${#var}

字符串 Expansion and slice

[zorro@zorrozou-pc0 bash]$ mkdir -p test/zorro/{a,b,c,d}{1,2,3,4} [zorro@zorrozou-pc0 bash]$ ls test/zorro/ a1  a2  a3  a4  b1  b2  b3  b4  c1  c2  c3  c4  d1  d2  d3  d4

[zorro@zorrozou-pc0 bash]$ mv test/{a,c}.conf 这个命令的意思是:mv test/a.conf test/c.conf

${string:start :length} string slice

default value: ${var:-default} set value: ${var:=default}

${#var} get variable length

Redirection

input: <, output >, append >>

cat > file << EOF this line will be redirected to file EOF

pipe

pipe commands will be run simultaneously, but the second command will wait for the input

Sub shell

use $(expression)

控制语句

条件语句

if 语句成立的条件是 expr 返回值为 0。

if expr; then
    statement;
elif expr; then
    statement;
else
    statement;
fi

test command

虽然可以使用任意的语句作为判断条件,不过我们一般情况下都是用 [ 这个命令来作为判断条件的,需要注意的是 [ 并不是一个语法,而是一个命令。不过由于 [ 这个上古命令实在功能太少,现在一般采用 [[ 来作为判断条件。

if [[ "a" == "b" ]]; then
    echo "wtf"
else
    echo "meh"
fi

[[支持的条件有

1 数值比较, 仅限整数,注意不能使用 > < 等符号。

n1 -eq n2   equal
n1 -ge n2   greater or equal
n1 -gt n2   greater
n1 -le n2       less or equal
n1 -lt n2   less
n1 -ne n2   not equal

2 字符串比较

Note: Variables may contain space, so the best way to comparison is to add quotes: "$var1" = "$var2"

str1 == str2    equal
str1 != str2    not equal
str1 < str2 less
str1 > str2 greater
-z str  zero
-n str  not zero length

3 file comparison

-d  is directory?
-e  exist?
-f  is regular file?
-r  exist and readable?
-s  exist and has content
-w  exist and writable
-x  exist and executealbe
-O  exist and owned
-G  exist and in same group
file -nt file2  newer than
file1 -ot file2     older than

case

case var in parttern | pattern2) commands;; pattern3) commands2; *) default commnads;; esac

Loops

foreach 语句

for var in list; do echo $var done

其中 list 可以是一个数组,也可以是一个被 $IFS 分割的字符串。默认情况下,$IFS 是 " \n\t"。其中包含了空格。

如果要覆盖 IFS,一般这样使用:

OLDIFS=$IFS IFS="\n" # new seperator

do things

IFS=$OLDIFS

while-loop

until/while expr; do # commands done

pipe

the result of a for loop is pipe-able to other command

for city in beijing shanghai; do
    echo $city is big
done > cities.txt
# will save the result in cities.txt

输入输出

命令行参数

parameters to a script can be obtained as $1, $2, $3...。 $0 is the script name, remember to check whether the parameter is empty. $# is the number of parameters(without script name).

$0  script name / function name
$1...$x command line arguments / parameters
$#  number of arguments(without $0)
$*  all parameters as a string
$@  all parameters as a string list

shift

processing parameters using shift,

while [ -n "$1" ]; do case "$1" in -a) echo "option -a" ;; --) shift break;; *) echo "$1" is not a option ;; esac shift done

read

read OPTIONS VARNAME read input to variables

  • read -p Prompt
  • read -t timeout
  • read -s hide input

we can use read to read file or stdin

redirection

2> redirect STDERR m>&n redirect fd m to fd n's associated file

Note: you have to use command >> command.log 2>&1 (put 2>&1 at the end), since this means redirect 2 to 1's in a scirpt exec 2> filename # reopen stdout to filename

Signal

trap commnad signal is used to handle signals in shell

Functions

有两种定义函数的方式

function name {
    # function body
}

foo() {
    # function body
}

要调用上面这个函数,直接就输入

foo

就好了

return

shell functions behave like a small script, and it does NOT return a computed value...It retures a exit code, which is between 0 and 255. if no return is specified, the exit code of last command will be returned

You can read the return value by $? like any normal commands

the right way to to return a value from function, you will have to echo out the value, and put the function is subshell

function foo {
    # do some compute
    echo $result;
}

retval=$(foo)

Note: any thing that echos in the function body will be captured, so please keep that from happen

parameters

like a shell script, $0 holds the function name, $1 ... $9 holds the parameters, $# is the num of parameters

local variables

use local to declare local variables

alias

alias new_name='command string'
$ \command  # bypass alias

debugging

DEBUG macro

multiprocess

PID_ARRAY=() for file in filelist; do md5sum file &; PID_ARRAY+=("$!") done wait ${PID_ARRAY[@]}