在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):wzb56/13_questions_of_shell开源软件地址(OpenSource Url):https://github.com/wzb56/13_questions_of_shell开源编程语言(OpenSource Language):开源软件介绍(OpenSource Introduction):13_questions_of_shellshell十三问--shell教程(markdown 版本) ##shell十三问之1: 何为shell?
我们知道计算机的运作不能离开硬件,但使用者却无法直接操作硬件,
硬件的驱动只能通过一种称为“ 然而,从使用者的角度来说,使用者没有办法直接操作一个 从技术的角度来说,
每次当我们完成 若从 如果是执行 这里, 我们必须知道:
在 /etc/shells 不同的
大部分的Linux操作系统的预设shell都是
##shell十三问之2:shell prompt(PS1)与Carriage Return(CR)关系 当你成功登陆一个shell终端的文字界面之后,大部分的情形下,
你会在屏幕上看到一个不断闪烁的方块或者底线(视不同的版本而别),
我们称之为 假如你刚完成登陆,还没有输入任何按键之前,
你所看到的
事实上,shell prompt的意思很简单: 告诉shell使用者,您现在可以输入命令行了。 我们可以说,使用者只有在得到shell prompt才能打命令行,
而 (question:为何我们这里坚持使用 不同的命令可以接受的命令的格式各有不同, 一般情况下,一个标准的命令行格式为如下所列: command-name options argument 若从技术的细节上来看,
shell会依据 (注意:请务必理解以上两句的意思,我们日后的学习中常回到这里思考。) 其中
系统可以接受的命令的名称(command-name)可以从如下途径获得:
每一个命令行均必须包含命令的名称,这是不能缺少的。 ##shell十三问之3:别人echo、你也echo,是问echo知多少?承接上一章介绍的
$echo argument
为了更好理解,不如先让我们先跑一下 $echo
$ 你会发现只有一个空白行,然后又回到了 $echo -n
$ 不妨让我们回到 要想看看 $echo first line
first line
$echo -n first line
first line $ 以上两个 事实上,
关于
或许,我们可以通过实例来了解 例一: $ echo -e "a\tb\tc\n\d\te\tf"
a b c
d e f
$ 上例中,用\t来分割abc还有def,及用\n将def换至下一行。 例二: $echo -e "\141\011\142\011\143\012\144\011\145\011\146"
a b c
d e f 与例一中结果一样,只是使用ASCII八进制编码。 例三: $echo -e "\x61\x09\x62\x09\x63\x0a\x64\x09\x65\x09\x66"
a b c
d e f 与例二差不多,只是这次换用ASCII的十六进制编码。 例四: $echo -ne "a\tb\tc\nd\te\bf\a"
a b c
d f $ 因为e字母后面是退格键(\b),因此输出结果就没有e了。
在结束的时听到一声铃响,是\a的杰作。
由于同时使用了-n选项,因此 事实上,在日后的 $ A=B
$ echo $A
B
$ echo $?
0
好了,更多的关于 ##shell十三问之4:""(双引号)与''(单引号)差在哪?还是回到我们的 经过前面两章的学习,应该很清楚当你在 简单而言,(我不敢说精确的定义,注1),
除了常用的
假如我们需要在 在
下面的例子将有助于我们对quoting的了解: $ A=B C #空白符未被关闭,作为IFS处理
$ C:command not found.
$ echo $A
$ A="B C" #空白符已被关掉,仅作为空白符
$ echo $A
B C 在第一个给A变量赋值时,由于空白符没有被关闭,
$ A=`B
> C
> '
$ echo "$A"
B
C 在上例中,由于 上例的 $ A="B
> C
> "
$ echo $A
B C 然而,由于 同样的,用escape亦可关闭CR字符: $ A=B\
> C\
>
$ echo $A
BC 上例中的,第一个 你或许发现光是一个
至于,什么时候解释为什么字符,这个我就没法去挖掘了, 或者留给读者君自行慢慢摸索了...^-^ 至于soft quote跟hard quote的不同,主要是对于某些meta的关闭与否,以$来做说明: $ A=B\ C
$ echo "$A"
B C
$ echo '$A'
$A 在第一个 在第二个
$ A=B\ C
$ echo '"$A"' #最外面的是单引号
"$A"
$ echo "'$A'" #最外面的是双引号
'B C' 在CU的shell版里,我发现很多初学者的问题, 都与quoting的理解有关。 比方说,若我们在awk或sed的命令参数中, 调用之前设定的一些变量时,常会问及为何不能的问题。 要解决这些问题,关键点就是:区分出 shell meta 与 command meta 前面我们提到的那些meta,都是在command line中有特殊用途的, 比方说{}就是将一系列的command line置于不具名的函数中执行(可简单视为command block), 但是,awk却需要用{}来区分出awk的命令区段(BEGIN,MAIN,END). 若你在command line中如此输入: $ awk {print $0} 1.txt 由于{}在shell中并没有关闭,那shell就将{print $0}视为command block,
但同时没有 要解决之,可用hard quote: awk '{print $0}' 上面的hard quote应好理解,就是将原来的 {、、$、}这几个shell meta关闭, 避免掉在shell中遭到处理,而完整的成为awk的参数中command meta。
要是理解了hard quote的功能,在来理解soft quote与escape就不难: awk "{print \$0}" 1.txt
awk \{print \$0\} 1.txt 然而,若要你改变awk的$0的0值是从另一个shell变量中读进呢?
比方说:已有变量$A的值是0, 那如何在 $ awk '{print $$A}' 1.txt 那是因为$A的$在hard quote中是不能替换变量的。 聪明的读者(如你!),经过本章的学习,我想,你应该可以理解为 为何我们可以使用如下操作了吧: A=0
awk "{print \$$A}" 1.txt
awk \{print\ \$$A\} 1.txt
awk '{print $'$A'}' 1.txt
awk '{print $'"$A"'}' 1.txt 或许,你能给出更多方案... ^_^ 更多练习:
read a #输入: abc
echo "$a" #只输出abc 原因: 变量a的值,从终端输入的值是以IFS开头,而这些IFS将被shell解释器忽略(trim)。 应该与shell解释器分词的规则有关; read a #输入:\ \ \ abc
echo "$a" #只输出abc 需要将空格字符转义
解决思路:
若你还是不理解,那来验证一下下面这个例子: $ A=" abc"
$ echo $A
abc
$ echo "$A" #note1
abc
$ old_IFS=$IFS
$ IFS=;
$ echo $A
abc
$ IFS=$old_IFS
$ echo $A
abc
问题二:为什么多做了几个分号,我想知道为什么会出现空格呢? $ a=";;;test"
$ IFS=";"
$ echo $a
test
$ a=" test"
$ echo $a
test
$ IFS=" "
$ echo $a
test 解答: 这个问题,出在 要不是试试下面这个代码片段: $ old_IFS=$IFS
$ read A
;a;b;c
$ echo $A
;a;b;c
$ IFS=";" #Note2
$ echo $A
a b c
思考问题二:文本处理:读文件时,如何保证原汁原味。 cat file | while read i
do
echo $i
done 文件file的行中包含若干空,经过read只保留不重复的空格。 如何才能所见即所得。 cat file | while read i
do
echo "X${i}X"
done 从上面的输出,可以看出read,读入是按整行读入的; 不能原汁原味的原因:
cat file | while read i
do
echo "$i"
done 以上代码可以解决原因2中的,command line的分词和重组导致meta字符丢失; 但仍然解决不了原因1中,read读取行时,忽略行起始的IFS meta字符。 回过头来看上面这个问题:为何要原汁原味呢? cat命令就是原汁原味的,只是shell的read、echo导致了某些shell的meta字符丢失; 如果只是IFS meta的丢失,可以采用如下方式:
将IFS设置为null,即 因此上述的解决方案是: old_IFS=$IFS
IFS=; #将IFS设置为null
cat file | while read i
do
echo "$i"
done
IFS=old_IFS #恢复IFS的原始值 现在,回过头来看这个问题,为什么会有这个问题呢; 其本源的问题应该是没有找到解决原始问题的最合适的方法, 而是采取了一个迂回的方式来解决了问题; 因此,我们应该回到问题的本源,重新审视一下,问题的本质。 如果要精准的获取文件的内容,应该使用od或者hexdump会更好些。 ##shell十三问之5:问var=value 在export前后的差在哪?这次让我们暂时丢开 所谓的变量,就是利用一个固定的"名称"(name), 来存取一段可以变化的"值"(value)。 ###1. 变量设定(set) 在bash中, 你可以用"="来设定或者重新定义变量的内容: name=value 在设定变量的时候,得遵守如下规则:
如下是一些变量设定时常见的错误: A= B #=号前后不能有IFS
1A=B #变量名称不能以数字开头
$A=B #变量的名称里有$
a=B #这跟a=b是不同的,(这不是错误,提醒windows用户) 如下则是可以接受的设定: A=" B" #IFS被关闭,参考前面的quoting章节
A1=B #并非以数字开头
A=$B #$可用在变量的值内
This_Is_A_Long_Name=b #可用_连接较长的名称或值,且有大小区别; ###2. 变量替换(substitution) shell 之所以强大,其中的一个因素是它可以在命令行中对变量作 替换(substitution)处理。 在命令行中使用者可以使用$符号加上变量名称(除了用=定义变量名称之外), 将变量值给替换出来,然后再重新组建命令行。 比方: $ A=ls
$ B=la
$ C=/tmp
$ $A -$B $C 以上命令行的第一个 ls -la /tmp 还记得第二章,我请大家"务必理解"的那两句吗? 若你忘了,我这里重贴一遍:
这里的 $ echo $A -$B $C 我们已学过, ls -al /tmp 这是由于 A=B
B=$A 这样,B的变量值就可继承A变量"当时"的变量值了。 不过,不要以"数学逻辑"来套用变量的设定,比方说: A=B
B=C 这样,并不会让A的变量值变成C。再如: A=B
B=$A
A=C 同样也不会让B的值变成C。 上面是单纯定义了两个不同名称的变量: A 与 B, 它们的取值分别是C与B。 若变量被重复定义的话,则原有值为新值所取代。(这不正是"可变的量"吗?^_^) 当我们在设定变量的时候,请记住这点:用一个名称存储一个数值, 仅此而已。 此外, 我们也可以利用命令行的变量替换能力来"扩充"(append)变量的值: A=B:C:D
A=$A:E
这样, 第一行我们设定A的值为"B:C:D", 然后,第二行再将值扩充为"B:C:D:E"。 上面的扩充的范例,我们使用分隔符号(:)来达到扩充的目的, 要是没有分隔符的话,如下是有问题的: A=BCD
B=$AE 因为第二次是将A的值继承$AE的替换结果,而非$A再加E。 要解决此问题,我们可用更严谨的替换处理: A=BCD
A=${A}E 上例中,我们使用{}将变量名称范围给明确定义出来, 如此一来, 我们就可以将A的变量值从BCD给扩充为BCDE。
###3. export 变量 严格来说,我们在当前shell中所定义的变量,均属于
"本地变量"(local variable), 只有经过 $ A=B
$ export A 或者 $ export A=B 经过 $ A=B
$ B=C
$ export $A 上面的命令并未将A输出为"环境变量",而是将B导出
这是因为在这个命令行中,$A会首先被替换为B,然后在"塞回"
作 要理解这个 ####4. 取消变量(unset)
要取消一个变量,在bash中可使用 unset A 与 $ A=B
$ B=C
$ unset $A 事实上,所取消的是变量B而不是A。 此外,变量一旦经过unset取消之后, 其结果是将整个变量拿掉,而不是取消变量的值。 如下两行其实是很不一样的: $ A=
$ unset A 第一行只是将变量A设定为"空值"(null value), 但第二行则是让变量A不存在。 虽然用眼睛来看, 这两种变量的状态在如下的命令结果中都是一样的: $ A=
$ echo $A
$ unset A
$ echo $A 请学员务必能识别null value 与 unset的本质区别, 这在一些进阶的变量处理上是很严格的。 比方说: $ str= #设为null
$ var=${str=expr} #定义var
$ echo $var
$ echo $str
$ unset str #取消str
$ var=${str=expr} #定义var
$ echo $var
expr
$ echo $str
expr 聪明的读者(yes, you!),稍加思考的话, 应该不难发现为何同样的var=${str=expr} 在str为null与unset之下的不同吧? 若你看不出来,那可能是如下原因之一:
不知,您选哪个呢?...... ^_^. ##shell十三问之6:exec跟source差在哪?这次让我们从CU shell版的一个实例帖子来谈起吧: (论坛改版后,原链接已经失效) 例中的提问原文如下:
意思是:运行shell脚本,并没有移动到/etc/aa/bb/cc目录。 我当时如何回答暂时别去深究,先让我们了解一下进程 (process)的概念好了。 首先,我们所执行的任何程序,都是父进程(parent process)产生的一个
子进程(child process),子进程在结束后,将返回到父进程去。
此现象在Linux中被称为 (为何要称为fork呢? 嗯,画一下图或许比较好理解...^_^) 当子进程被产生的时候,将会从父进程那里获得一定的资源分配、及 (更重要的是)继承父进程的环境。 让我们回到上一章所谈到的"环境变量"吧: 所谓环境变量其实就是那些会传给子进程的变量。 简单而言, "遗传性"就是区分本地变量与环境变量的决定性指标。 然而,从遗传的角度来看,我们不难发现环境变量的另一个重要特征: 环境变量只能从父进程到子进程单向传递。 换句话说:在子进程中环境如何变更,均不会影响父进程的环境。 接下来,在让我们了解一下shell脚本(shell script)的概念.
所谓shell script 讲起来很简单,就是将你平时在shell prompt输入的多行
再结合以上两个概念(process + script),那应该不难理解如下的这句话的意思了: 正常来说,当我们执行一个shell script时,其实是先产生一个sub-shell的子进程, 然后sub-shell再去产生命令行的子进程。 然则,那让我们回到本章开始时,所提到的例子在重新思考:
意思是:运行shell脚本,并没有移动到/etc/aa/bb/cc目录。 我当时的答案是这样的:
能够了解问题的原因及其原理是很好的,但是? 如何解决问题,恐怕是我们更应该感兴趣的是吧? 那好,接下来,再让我们了解一下 所谓 因此, 只要我们原本单独输入的script命令行,变成 比方说,原本我们是如此执行script的: $ ./my_script.sh 现在改成这样既可: $ source ./my_script.sh 或者: $ . ./my_script.sh 说到这里,我想,各位有兴趣看看 若然,日后,你有机会写自己的script, 应也不难专门指定一个设定的文件以供不同的script一起"共用"了... ^_^ okay,到这里,若你搞懂
哦...要了解
嗯,光是从理论去理解,或许没那么好消化, 不如动手"实践+思考"来得印象深刻哦。 下面让我们为两个简单的script,分别命名为1.sh以及2.sh 1.sh 全部评论
专题导读
上一篇:kristapsdz/lowdown: simple markdown translator发布时间:2022-08-18下一篇:gouthambs/Flask-Blogging: A Markdown Based Python Blog Engine as a Flask Extensi ...发布时间:2022-08-18热门推荐
热门话题
阅读排行榜
|
请发表评论