Shell学习笔记

嘛,作为习惯使用Mac作为开发系统的开发者,Shell算是必须学习的内容之一。CI的脚本之类的也需要用Shell来写,总之学习一下没有坏处。本文主要以基础的Bourne Shell为学习对象,记录学习过程中的一些知识要点,和其他笔记一样,基本上是写给自己看的,不喜欢的不要拍砖。

1.关于 #!

'#'表示注释,而'#!'则告诉系统当前文件应该用什么来执行。
例如:

#!/bin/sh
echo Hello World

/bin/sh是Bourne shell在Unix下的标准路径,如果是Linux下,它则是一个指向bash(最新版是dash)的软连接。

2.关于chmod

对用户创建的文件来说,默认只有读写权限,没有执行权限。为了让脚本可以被执行,就需要通过chmod命令修改权限。

r 表示读权限 用数字表示为4
w 表示写权限 用数字表示为2
x 表示执行权限 用数字表示为1
- 表示无权限 用数字表示为0

chmod 777 test.sh

3.变量赋值

变量在赋值的时候没有空格,否则系统会以为这是一条执行命令语句,例如:

#!/bin/sh
MY_MESSAGE="Hello World"
echo $MY_MESSAGE

如果=左右有空格的话,系统还以为MY_MESSAGE是一个命令,而=和“Hello world”是命令的两个参数。
如果变量没有被赋值,那么变量将被视为一个空字符串。

4.使用read命令

read命令可以从标准输入中读取一行并将其赋值给你提供的变量。

#!/bin/sh
echo "What is your name?"
read MY_NAME
echo "Your name is $MY_NAME"

注意到read命令能够自动给你的输入加一对双引号,这意味着你可以安全的使用空格。

What is your name?
Grand Kindom
Your name is Grand Kindom

5.变量的作用域

一般来说,变量的作用域只限于当前的shell环境,也就是#!bin/sh之下的环境中。
例如,我们有如下的shell脚本:

#!/bin/sh
echo "MY_VAR is $MY_VAR"
MY_VAR="Hello"
echo "MY_VAR is $MY_VAR"

现在我们执行脚本:

$ MY_VAR=hello
$ ./test.sh
MY_VAR is
MY_VAR is Hello

可以发现,我们在交互式shell(interactive shell)下赋值的变量,对新生成的用来运行脚本的子shell环境没有任何影响。
为了能让子shell可以使用我们的变量,需要使用export命令允许子shell继承变量。

$ MY_VAR=World
$ export MY_VAR
$ ./test.sh
MY_VAR is World
MY_VAR is Hello

脚本中的第三行,我们更改了MY_VAR变量的值,我们尝试在交互式shell下输出:

$ echo $MY_VAR
World

发现值并没有发生任何变化,也就是说子shell对父shell的变量没有影响。
为了做到这点,我们需要将脚本在当前的交互式shell中运行,而不是新生成一个shell来运行。这一过程叫做source。通过.命令,我们可以source一个脚本:

$ . ./test.sh
MY_VAR is World
MY_VAR is Hello
$ echo $MY_VAR
Hello

MY_VAR变量被成功改变了!这也是.profile.bashrc的工作原理。

6.变量的边界

考虑以下脚本

#!/bin/sh
echo "Please tell your name"
read MY_NAME
echo "Hello $MY_NAME"
echo "Now I will create a file called $MY_NAME_file"
touch "$MY_NAME_file"

文件能够创建成功吗?不行。因为shell不知道变量的起始位置,它还以为有一个变量的名字叫做'MY_NAME_file',而事实上并没有这样的一个变量,因此文件创建失败了。
使用花括号可以界定变量的边界,他告诉shell:花括号内的是一个变量,需要单独对待。

echo "Please tell your name"
read MY_NAME
echo "Hello $MY_NAME"
echo "Now I will create a file called ${MY_NAME}_file"
touch "${MY_NAME}_file"

再次运行上述修改后的脚本,我们会发现文件被成功创建。

为什么touch命令的参数要用双引号圈起来呢?这是为了防止你输入的名字有空格。比如说你输入的名字是Grand Kindom,那么最后的touch命令就会变成touch Grand Kingdom_file。结果会生成两个文件,一个叫做Grand,另一个叫做Kindom_file

7.循环

1)for循环

for i in hello 1 * 2 world
do
  echo "i is set to $i in the loop now"
done

2)while循环

INPUT_STRING=hello
while [ "$INPUT_STRING" != "bye" ]
do
  echo "Please type something in (bye to quit)"
  read INPUT_STRING
  echo "You typed: $INPUT_STRING"
done

当while后跟着一个:时,条件表达式永远为true,这在某些情况下很有用。

while :
do
  echo "Please type something int (^C to quit)"
  read INPUT_STRING
  echo "You typed: $INPUT_STRING"
done

另一个比较常见的使用方式是while read f循环

while read f
  do
    case $f in
      hello)        echo English    ;;
        howdy)      echo American   ;;
        gday)       echo Australian ;;
        bonjour)    echo French ;;
        "guten tag")    echo German ;;
        *)      echo Unknown Language: $f
        ;;
    esac
  done < myfile

8.测试

在shell中,[代表测试,它本身是一个程序,就像lschmod一样,而不是一个符号。所以[后面必须跟空格,否则shell会把[和后面的字符拼在一起解释,导致问题。在Unix系统中,[是一个指向test程序的软连接,也就是说:

if [$foo = "bar" ]

会被解释为if test$foo = "bar" ],这也就是问题的所在了。可以看到,在shell中,使用=比较字符串,而对于整数则使用-eq

1) if

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

注意then必须另起一行。也可以加上;使其和if在同一行。在shell中;表示后面的语句是另一行语句,不过出于某种目的(比如说节约空间)所以写在同一行。与其对应的符号是\,表示下一行的语句应该和本行语句视为同一行。下面的第二个shell运用了逻辑运算符的短路性质。

if [ ... ]; then
  ...
fi
[ "$X" -nt "/etc/passwd" ] && \
      echo "X is a file which is newer than /etc/passwd"

也可以使用elif

if  [ something ]; then
 echo "Something"
 elif [ something_else ]; then
   echo "Something else"
 else
   echo "None of the above"
fi

9.预设变量

1) $0 $1 ... $9$#

  • $0表示当前程序(脚本)的名称
  • $1 .. $9表示调用脚本时候传入的前9个参数
  • 变量$@表示所有参数 $*也有类似的作用但是它不保留空格和引号(通常情况下避免使用$*)
  • $#表示脚本调用时候传入的参数个数
#!/bin/sh
echo "I was called with $# parameters"
echo "My name is $0"
echo "My first parameter is $1"
echo "My second parameter is $2"
echo "All parameters are $@"
$ ./var3.sh hello world earth
I was called with 3 parameters
My name is ./var3.sh
My first parameter is hello
My second parameter is world
All parameters are hello world earth

2) $?

这个变量包含上一个命令的退出值。在shell中,返回值0表示正常退出,所以可以通过test来判断上一个命令执行的时候有没有执行成功,增加程序的鲁棒性。

#!/bin/sh
/usr/local/bin/my-command
if [ "$?" -ne "0" ]; then
  echo "Sorry, we had a problem there!"
fi

3)$$$!

  • $$变量表示当前的进程标识符,如果你的脚本允许存在多个实例同时运行的话,在创建文件的时候就可以在文件名上带上这个进程标识符避免冲突:/temp/my_file.$$
  • $!变量表示最后运行的后台进程标识符

10.花括号的高级用法

花括号除了用来保证变量的边界外,还可以用来处理字符串undefined或者为null的情况(这两者在shell里基本没有区别),为其添加一个默认值,方法是在变量名后面加上:-

#!/bin/sh
echo "What is your name [ `whoami` ] \c"
read MY_NAME
echo "Hello ${MY_NAME:-`whoami`}"

上述脚本中的\c告诉echo命令不要换行。反引号中的内容whoami被视为一个命令,命令在执行之后的标准输出会被替换为反引号所在的内容。而whoami命令输出的是当前登录的用户名称,所以上述命令的作用是让用户键入自己的名称,并且会显示当前登录的用户名称作为默认值,如果用户直接点了回车没有输入内容的话,则会输出用户的登录名。

$ ./test.sh
What is your name [ melot ] abc
Hello abc

你可能感兴趣的:(Shell学习笔记)