Shell从入门到看开 ₁

shell简介

#!/bin/bash 

shell脚本通常是这样起始的文本文件,其中#!位于解释器路径之前,/bin/bash是Bash解释器命令路径。

有两种运行脚本的方式。一种是将脚本作为bash的命令行参数,另一种是授予脚本执行权
限,将其变为可执行文件。

方式一:
    $ bash script.sh #假设脚本位于当前目录下
    $ bash /home/path/script.sh #使用script.sh的完整路径
    
方式二:
    $ chmod a+x script.sh #授予脚本执行权限
    $ ./script.sh #./表示当前目录
    $ /home/path/script.sh #使用脚本的完整路径

在Bash中,每个命令或通过使用分号或换行符来分隔。比如:$ cmd1 ; cmd2它等同于:
    $ cmd1 
    $ cmd2

注释以#为起始,一直延续到行尾。注释行通常用于为代码提供注释信息,   

终端打印

echo是用于终端打印的基本命令,在默认情况下,echo在每次调用后会添加一个换行符。
$ echo "Welcome to Bash"
Welcome to Bash

另一个可用于终端打印的命令是printf。printf使用的参数和C语言中的printf函数一样。
$ printf "Hello world"

注意使用echo和printf的命令选项时,要确保选项应该出现在命令行内所有字符串之前,否则 Bash会将其视为另外一个字符串。

变量

变量的定义
var=value(注意中间不能有空格!!!)
var:是变量名,value:是变量的值

把var=value写成var = value是Java工程师一个常犯的错误,Java工程师注意一下啦。前者是赋值操作,后者是相等操作。

#!/bin/bash 
fruit=apple
count=5
echo "We have $count ${fruit}(s)"
输出如下:
We have 5 apple(s)

export命令用来设置环境变量。

补充内容
1. 获得字符串长度 可以用下面的方法获得变量值的长度:

$ var=12345678901234567890$
length=${#var}
echo ${#var}
20

2. 识别当前所使用的shell 可以用下面的方法获知当前使用的是哪种shell:
$ echo $SHELL
    

文件描述符

(1)将输出文本重定向或保存到一个文件中:

$ echo "This is a sample text 1" > temp.txt

这种方法通过截断文件的方式,将输出文本存储到文件temp.txt中,也就是说在把 echo命令的输出写入文件之前,temp.txt中的内容首先会被清空。

(2) 将文本追加到目标文件中
$ echo "This is sample text 2" >> temp.txt

(3) 查看文件内容:
$ cat temp.txt
This is sample text 1
This is sample text 2

(4) 将结果输出到文本
$ cmd&>output.txt

数组

(1) 定义数组的方法有很多种。可以在单行中使用一列值来定义一个数组:
array_int=(1 2 3 4 5 6) #这些值将会存储在以0为起始索引的连续位置上
另外,还可以将数组定义成一组“索引-值”:
           array_str[0]="test1"
           array_str[1]="test2"
           array_str[2]="test3"
           array_str[3]="test4"
           array_str[4]="test5"
           array_str[5]="test6"
(2) 打印出特定索引的数组元素内容:
           echo ${array_str[0]}
           test1
           index=5
           echo ${array_str[$index]}
           test6
(3) 以清单形式打印出数组中的所有值: $ echo ${array_str[*]}
           test1 test2 test3 test4 test5 test6
也可以这样使用:
           $ echo ${array_str[@]}
           test1 test2 test3 test4 test5 test6
(4) 打印数组长度(即数组中元素的个数):
             $ echo ${#array_var[*]}
             6
             
- 关联数组

关联数组从Bash 4.0版本开始被引入

(1)定义关联数组

在关联数组中,我们可以用任意的文本作为数组索引。首先,需要使用声明语句将一个变量 名声明为关联数组。声明之后,可以用两种方法将元素添加到关联数组中。
  $ declare -A ass_array
1. 利用内嵌“索引-值”列表的方法,提供一个“索引-值”列表:
   $ ass_array=([index1]=val1 [index2]=val2)
2. 使用独立的“索引-值”进行赋值:
   $ ass_array[index1]=val1
   $ ass_array'index2]=val2
   
举个例子,试想如何用关联数组为水果制定价格:
$ declare -A fruits_value
$ fruits_value=([apple]='100dollars' [orange]='150 dollars')
用下面的方法显示数组内容:
$ echo "Apple costs ${fruits_value[apple]}"
Apple costs 100 dollars

(2)列出数组索引

 每一个数组元素都有一个索引用于查找。普通数组和关联数组具有不同的索引类型。我们可以用下面的方法获取数组的索引列表:
    $ echo ${!array_var[*]}
也可以使用:
    $ echo ${!array_var[@] 
以先前提到的fruits_value数组为例,运行如下命令:
    $ echo ${!fruits_value[*]}
    orange apple

使用别名

别名就是一种便捷方式,以省去用户输入一长串命令序列的麻烦。
创建别名时,如果已经有同名的别名存在,那么原有的别名设置将被新的设置取代。

(1) 为安装命令apt-get install创建别名:
$ alias install='sudo apt-get install'
这样一来,我们就可以用install代替sudo apt-get install pidgin了。

(2) alias命令的作用只是暂时的。一旦关闭当前终端,所有设置过的别名就失效了。 为了使别名设置一直保持作用:
    Linux可以将它写入~/.bashrc文件中。因为每当一个新的shell进程生成时,都会执行 ~/.bashrc中的命令。
$ echo 'alias cmd="command seq"' >> ~/.bashrc
    Mac可以将它写入~/.bashrc文件中。
$ echo 'alias cmd="ls"' >> ~/.bash_profile 

(3) 如果需要删除别名,只用将其对应的语句(如果有的话)从 ~/.bashrc中删除, 或者使用unalias命令。或者使用alias example=,这会取消名为example 的别名。

(4) 我们可以创建一个别名rm,它能够删除原始文件,同时在backup目录中保留副本: alias rm='cp $@ ~/backup && rm $@'

调试脚本

调试功能是每一种编程语言都应该实现的重要特性之一,当出现一些始料未及的情况时,用 它来生成脚本运行信息。

(1) 使用选项–x,启用shell脚本的跟踪调试功能,打印出所执行的每一行命令以及当前状态 
    $ bash -x script.sh

(2) 使用set -x和set +x对脚本进行部分调试。例如:
    #!/bin/bash 
    #文件名: debug.sh 
    for i in {1..6}; do
       set -x
       echo $i
       set +x
       done
    echo "Script executed"
在上面的脚本中,只会打印出echo $i的调试信息,因为使用了-x和+x对调试区域
进行了限制。

(3) 把shebang从#!/bin/bash改成 #!/bin/bash -xv,这样一来,不用任何其他选项就可以启用调试功能了。

函数和参数

(1) 定义函数:
   function fname()
   {
       statements;
   }
或者:
    fname() {
       statements;
   }

(2) 只需要使用函数名就可以调用某个函数: 
    $ fname ; #执行函数
(3) 参数可以传递给函数,并由脚本进行访问:
    #传递参数 以下是函数fname的定义。在函数fname中,包含了各种访问函数参数的方法。
    $ fname arg1 arg2 ; 
    
    fname() {
    echo $1, $2; #访问参数1和参数2
    echo "$@";#以列表的方式一次性打印所有参数 
    echo "$*"; #类似于$@,但是参数被作为单个实体 
    return 0; #返回值
}

 $1是第一个参数。
 $2是第二个参数。
 $n是第n个参数。
 "$@" 被扩展成 "$1" "$2" "$3"等。
 "$*" 被扩展成 "$1c$2c$3",其中c是IFS的第一个字符。
 "$@" 要比"$*"用得多。由于 "$*"将所有的参数当做单个字符串,因此它很少被
使用。

将命令序列的输出读入变量

shell脚本最棒的特性之一就是可以轻松地将多个命令或工具组合起来生成输出。一个命令的 输出可以作为另一个命令的输入,而这个命令的输出又会传递至另一个命令,依次类推。

这些命令被称为过滤器(filter)。我们使用管道(pipe)连接每个过滤器。管道操作符是|。
    $ cmd1 | cmd2 | cmd3
这里我们组合了3个命令。cmd1的输出传递给cmd2,而cmd2的输出传递给cmd3,最终的输
出(来自cmd3)将会被打印或导入某个文件。

例如:
    $ ls | cat -n > out.txt
    
ls的输出(当前目录内容的列表)被传给cat -n,后者将通过stdin所接收到输入 内容加上行号,然后将输出重定向到文件out.txt。

不使用回车键来读取 n 个字符

read是一个重要的Bash命令,它用于从键盘或标准输入中读取文本。我们可以使用read以 交互的形式读取来自用户的输入,不过read能做的可远不止这些。

(1) 下面的语句从输入中读取n个字符并存入变量var:
    $ read -n 2 var
    $ echo $var

(2) 用无回显的方式读取密码: 
    $ read -s var

(3) 显示提示信息: 
    $ read -p "Enter input:" var

(4) 在特定时限内读取输入:
    $ read -t 2 var #在2秒内将键入的字符串读入变量var
    
(5) 用特定的定界符作为输入行的结束:
    #输入hello:
    #var 被设置为 hello
    $ read -d ":" var
            

运行命令直至执行成功

我们创建了函数repeat,它包含了一个无限while循环,该循环执行以参数形式(通过$@
访问)传入函数的命令。如果命令执行成功,则返回,进而退出循环。

1. repeat函数
repeat() { while :; do $@ && return; done }

2. 增加延时 假设你要用repeat()从Internet上下载一个暂时不可用的文件,这个文件需要等一会
才下载。方法如下:

repeat wget -c http://www.example.com/software-0.1.tar.gz
    
若采用这种形式,需要向www.example.com发送很多数据,有可能会对服务器产生影响。 
(甚至也会给你造成麻烦,如果服务器认为你是在发送垃圾信息,就可能把你列入黑名单。)
为了解决这个问题,我们修改函数,加入一段短暂的延时:

repeat() { while :; do $@ && return; sleep 30; done }#使得命令每30秒运行一次

循环语句

for循环
    $ echo {a..z}
    $ for i in {a..z}; do echo ${i};done;
    i在{a..z}中打印出i,应输出a b c d...z。

while循环
    $ x=0
    $ while [ $x -lt 20 ];  do let x=$x+1; echo ${x}; done;
    x小于20时打印出i+1,应输出1 2 3...20。

until循环
    $ y=0;
    $ until [ $y -eq 9 ];do let y++; echo ${y};done;

分支语句

if单分支
if [ "13" -lt "12" ]  
then
        echo "yes 13 is less then 12"
else
        echo "NO"
fi

------------------------------------------------------

if多分支
if 条件1
then 
     命令1
elif 条件2
then 
     命令2
else
     命令3
fi

------------------------------------------------------

case语句
echo -n "enter a number from 1 to 3:"
read num
case $num in
1)
        echo "you select 1"
        ;;
2)
        echo "you select 2"
        ;;
3)
        echo "you select 3"
        ;;
y|Y)
        echo “you select y”
        ;;
*)
        echo "`basename $0`:this is not between 1 and 3">&2
        exit;
        ;;
esac

case取值后面必须为单词in;
每一模式必须以右括号结束。
取值可以为变量或常数。
匹配发现取值符合某一模式后,其后的所有命令开始执行,直到;;
模式匹配*表示任意字符; ?表示任意单字符; [..](注意:只有两个点)表示类或范围中任意字符

字符串比较

注意在 = 前后各有一个空格。如果忘记加空格,那就不是比 较关系了,而变成了赋值语句。

使用字符串比较时,最好用双中括号,因为有时候采用单个中括号会产生错误,所以最 好避开它们。
    [[ $str1 = $str2 ]]:当str1等于str2时,返回真。也就是说,str1和str2包含的文本是一模一样。
    [[ $str1 == $str2 ]]:这是检查字符串是否相等的另一种写法。

补充

算术比较 

条件通常被放置在封闭的中括号内。一定要注意在[或]与操作数之间有一个空格。如果忘记了这个空格,脚本就会报错。例如:

    [$var -eq 0 ] or [ $var -eq 0]  
    
对变量或值进行算术条件判断:

    [ $var -eq 0 ] #当$var等于0时,返回真 
    [ $var -ne 0 ] #当 $var 为非0时,返回真
    
其他重要的操作符如下所示。
 
 -gt:大于。
 -lt:小于。
 -ge:大于或等于。 
 -le:小于或等于。

可以按照下面的方法结合多个条件进行测试:
 
    [ $var1 -ne 0 -a $var2 -gt 2 ] #使用逻辑与-a 
    [ $var1 -ne 0 -o var2 -gt 2 ] #逻辑或 -o
    
文件系统相关测试 

我们可以使用不同的条件标志测试不同的文件系统相关的属性。

    [ -f $file_var ]:如果给定的变量包含正常的文件路径或文件名,则返回真。 
        [ -x $var ]:如果给定的变量包含的文件可执行,则返回真。
    [ -d $var ]:如果给定的变量包含的是目录,则返回真。
    [ -e $var ]:如果给定的变量包含的文件存在,则返回真。
    [ -c $var ]:如果给定的变量包含的是一个字符设备文件的路径,则返回真。 
        [ -b $var ]:如果给定的变量包含的是一个块设备文件的路径,则返回真。
    [ -w $var ]:如果给定的变量包含的文件可写,则返回真。
    [ -r $var ]:如果给定的变量包含的文件可读,则返回真。
    [ -L $var ]:如果给定的变量包含的是一个符号链接,则返回真。

使用方法如下:
fpath="/etc/passwd"
if [ -e $fpath ]; then
    echo File exists;
else
    echo Does not exist;
fi

第一时间获取更新请戳这里

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,252评论 6 516
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,886评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,814评论 0 361
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,869评论 1 299
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,888评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,475评论 1 312
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,010评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,924评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,469评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,552评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,680评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,362评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,037评论 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,519评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,621评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,099评论 3 378
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,691评论 2 361

推荐阅读更多精彩内容

  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,407评论 0 5
  • linux资料总章2.1 1.0写的不好抱歉 但是2.0已经改了很多 但是错误还是无法避免 以后资料会慢慢更新 大...
    数据革命阅读 12,175评论 2 33
  • .bat脚本基本命令语法 目录 批处理的常见命令(未列举的命令还比较多,请查阅帮助信息) 1、REM 和 :: 2...
    庆庆庆庆庆阅读 8,109评论 1 19
  • 首先,我们先说明一下为什么要封装网络请求的单例类:用instruments测试项目内存的时候突然发现了大量的内存泄...
    绛紫哟阅读 1,382评论 2 3
  • 上午八点去学校听黄老师的公开课,收拾妥当后,跟儿子一起出发了。 平时很少有机会走进课堂听老师给孩子们讲...
    ykmm阅读 157评论 0 0