Shell学习
Shell学习
教材为Shell脚本学习指南(O'REILY)
入门
#!的作用
#2021/5/14
# #!的作用
#例:假设一个脚本/home/learning/shell/nusers
#第一行#! /bin/bash -,内核解释这行命令后便会以如下方式引用bash:
#/bin/bash /home/learning/shell/nusers
#-表示没有shell选项,基于安全上的考虑,可避免一定程度的欺骗性攻击
合并参数选项
#不需要参数的选项可以合并例如:
#rm -fr
变量
#! /bin/bash -
# myvar
# 变量测试
myvar=this_is_a_long_string_that_not_mean_much
first=isaac middle=bashevis last=singer
fullname="isaac bashevis singer"
oldname=$fullname
echo $oldname
fullname="$first $middle $last"
echo $fullname
echo $myvar
[root@localhost shell]# ./myvar
isaac bashevis singer
isaac bashevis singer
this_is_a_long_string_that_not_mean_much
echo和printf
略(p29-32)
基本的I/O重定向
软件设计原则里的重要概念:程序应该有数据的来源端、数据的目的端以及报告问题的地方,它们分别被称为标准输入、标准输出以及标准错误输出。
读取标准输入、写入标准输出,并将错误信息传递到标准错误输出的一类程序常叫做过滤器(filter)
默认的标准输入、标准输出都是终端,这点可通过cat程序得知
[root@localhost shell]# cat
now is the time
now is the time
for all good men
for all good men
to come to the aid of thier country
to come to the aid of thier country
^D Ctrl-D,文件结尾
是谁替执行中的程序初始化标准输入、输出及错误输出的呢?
答案就是在你登录时,UNIX便将默认的标准输入、输出及错误输出安排成你的终端。
I/O重定向就是你通过与终端交互,或是在Shell脚本里设置,重新安排从哪里输入或输出到哪里
cat
[root@localhost shell]# cat --help
用法:cat [选项]... [文件]...
连接所有指定文件并将结果写到标准输出。
如果没有指定文件,或者文件为"-",则从标准输入读取。
-A, --show-all 等效于 -vET
-b, --number-nonblank 对非空输出行编号,同时取消 -n 选项效果
-e 等效于 -vE
-E, --show-ends 在每行结束处显示"$"
-n, --number 对输出的所有行编号
-s, --squeeze-blank 不输出多行空行
-t 与 -vT 等效
-T, --show-tabs 将跳格字符显示为^I
-u (被忽略)
-v, --show-nonprinting 使用^ 和M- 引用,除了LFD和 TAB 之外
--help 显示此帮助信息并退出
--version 显示版本信息并退出
示例:
cat f - g 先输出f 的内容,然后输出标准输入的内容,最后输出g 的内容。
cat 将标准输入的内容复制到标准输出。
GNU coreutils 在线帮助:<https://www.gnu.org/software/coreutils/>
Report any translation bugs to <https://translationproject.org/team/>
Full documentation <https://www.gnu.org/software/coreutils/cat>
或者在本地使用:info '(coreutils) cat invocation'
重定向与管道
使用UNIX工具程序时,不妨将数据想象成水管里的水。未经处理的水,将流向净水厂,经过各类过滤器的处理,最后产生适合人类饮用的水。 ——《Shell脚本学习指南》:35
- program < file 可将program的标准输入修改为file
- program > file 可将program的标准输出修改为file
- 重定向符>在目的文件不存在时,会新建一个;如果目的文件存在,它就会被覆盖
- program >> file 可将program的标准输出附加到file的结尾处
- program1 | program2 可将program1的标准输出修改为program2的标准输入
- < 与 > 将输入与输入连接到文件
- 管道|可以把两个以上执行中的程序衔接在一起,第一个文件的标准输出可以变成第二个文件的标准输入
- 管道可以使得执行速度比使用临时文件的程序快上十倍
#dos-file.txt
Hello!
How are you?
## 重定向符>和<的测试
[root@localhost tr_test]# vim dos-file.txt
##读dos-file.txt输入,并删除所有'\n'
[root@localhost tr_test]# tr -d '\n' < dos-file.txt
##读dos-file.txt输入,删除所有'\n'后,输出到UNIX-file.txt
Hello!How are you? [root@localhost tr_test]# tr -d '\n' < dos-file.txt > UNIX-file.txt
[root@localhost tr_test]# cat UNIX-file.txt
Hello!How are you? [root@localhost tr_test]#
## 管道测试
[root@localhost tr_test]# tr -d '\n' < dos-file.txt | sort > UNIX-file.txt
[root@localhost tr_test]# cat UNIX-file.txt
Hello!How are you?My friend?
tr是一个读标准输入,对其进行转换字符(大写转小写、删除、浓缩等),然后写到标准输出的程序,用法如下:
[root@localhost tr_test]# tr --help
用法:tr [选项]... SET1 [SET2]
Translate, squeeze, and/or delete characters from standard input,
writing to standard output.
-c, -C, --complement use the complement of SET1
-d, --delete delete characters in SET1, do not translate
-s, --squeeze-repeats replace each sequence of a repeated character
that is listed in the last specified SET,
with a single occurrence of that character
-t, --truncate-set1 first truncate SET1 to length of SET2
--help 显示此帮助信息并退出
--version 显示版本信息并退出
SET 是一组字符串,一般都可按照字面含义理解。解析序列如下:
\NNN 八进制值为NNN 的字符(1 至3 个数位)
\\ 反斜杠
\a 终端鸣响
\b 退格
\f 换页
\n 换行
\r 回车
\t 水平制表符
\v 垂直制表符
字符1-字符2 从字符1 到字符2 的升序递增过程中经历的所有字符
[字符*] 在SET2 中适用,指定字符会被连续复制直到吻合设置1 的长度
[字符*次数] 对字符执行指定次数的复制,若次数以 0 开头则被视为八进制数
[:alnum:] 所有的字母和数字
[:alpha:] 所有的字母
[:blank:] 所有呈水平排列的空白字符
[:cntrl:] 所有的控制字符
[:digit:] 所有的数字
[:graph:] 所有的可打印字符,不包括空格
[:lower:] 所有的小写字母
[:print:] 所有的可打印字符,包括空格
[:punct:] 所有的标点字符
[:space:] 所有呈水平或垂直排列的空白字符
[:upper:] 所有的大写字母
[:xdigit:] 所有的十六进制数
[=字符=] 所有和指定字符相等的字符
Translation occurs if -d is not given and both SET1 and SET2 appear.
-t may be used only when translating. SET2 is extended to length of
SET1 by repeating its last character as necessary. Excess characters
of SET2 are ignored. Only [:lower:] and [:upper:] are guaranteed to
expand in ascending order; used in SET2 while translating, they may
only be used in pairs to specify case conversion. -s uses the last
specified SET, and occurs after translation or deletion.
特殊文件:/dev/null与/dev/tty
- /dev/null:又称bit bucket或者黑洞。传到此文件的数据都会被系统丢掉
- /dev/tty:当程序打开此文件时,UNIX会自动将它重定向到一个终端再与程序结合。这在程序必须读取人工输入时(例如密码)特别有用。此外,用它来产生错误信息也很方便,只是比较少人这么做。
#! /bin/bash -
## 测试/dev/tty
printf "Enter new password: "
stty -echo
read pass < /dev/tty
printf "\n"
printf "Enter again: "
read pass2 < /dev/tty
printf "\n"
stty echo
[root@localhost shell]# ./tty-test
Enter new password:
Enter again:
基本命令查找
Shell会沿着查找路径$PATH来寻找命令。$PATH是一个以冒号分割的目录列表,你可以在列表所指定的目录下找到所要执行的命令。
[root@localhost ~]# echo $PATH
/home/learning/shell/bin:/home/jdk/jdk1.8.0_291/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
如果你要编写自己的脚本,最好准备自己的bin目录来存放它们,并且让Shell能够自动找到它们。只要建立自己的bin目录,并且将它加入$PATH中的列表即可。
注意
- $PATH中的空项目(empty component)表示当前目录。
- 空项目位于路径值中间时,可以用两个连续的冒号来表示。
- 如果将冒号直接置于最前端或者尾端,可以分别表示查找时最先查找或最后查找当前目录。
- 更好的做法是在$PATH中使用点号.(dot);这可以提高程序的可读性。
- 一般来说,不应该在查找路径中放进当前目录,因为这会有安全上的问题。
实践
#创建bin目录(/home/learning/shell/bin)并将脚本文件mv进去
cd /home/learning/shell
mkdir bin
mv 脚本文件... ./bin
##修改/etc/profile
##将自己的bin目录添加到$PATH
MY_SHELL_HOME=/home/learning/shell
PATH=$MY_SHELL_HOME/bin:$PATH
export PATH MY_SHELL_HOME
之前修改了~.bash_profile,以使脚本afterLogin在登录后运行,当时使用的是真实路径
sh /home/learning/shell/afterLogin
现在路径发生了变化,在登录后提示
sh: afterLogin: 没有那个文件或目录
修改~.bash_profile
sh afterLogin #已将/home/learning/shell/bin添加到$PATH
这样,这个脚本就能够正常地在登录后运行了。
访问Shell脚本的参数
所谓位置参数(positional parameters)指的也就是Shell脚本的命令行参数。
各参数由整数来命名。基于历史原因,当它超过9,就应该用大括号把数字框起来
echo first arg is $1
echo tenth arg is ${10}
带参数的脚本:
#! /bin/bash -
# finduser --- if first arg (username) is online, find it.
who | grep $1
[root@localhost bin]# finduser root
root pts/0 2021-05-16 14:50 (10.0.2.2)
如果不指定参数,则会发生:
[root@localhost bin]# finduser
用法: grep [选项]... 模式 [文件]...
尝试使用 'grep --help' 来获得更多信息。
这是这个程序不完美的地方。
简单的执行跟踪
如果你想知道程序正在做什么,可以把Shell执行跟踪的功能打开。这会使得Shell显示每个被执行到的命令,并在前面加上“+”:一个加号后面跟着一个空格。
有两种执行跟踪的方式:
-
你的Shell程序 -x 你的脚本
[root@localhost test]# bash -x finduser root + grep root + who root pts/0 2021-05-16 14:50 (10.0.2.2) #实际的输出可以把选项-x放在脚本的#!行
#! /bin/bash -x # finduser --- if first arg (username) is online, find it. who | grep $1[root@localhost test]# finduser root + grep root + who root pts/0 2021-05-16 14:50 (10.0.2.2) -
set -x和set +x
这个功能对复杂的脚本比较有用
#! /bin/bash set -x echo 1st echo set +x echo 2nd echo[root@localhost test]# ./trace1 + echo 1st echo 1st echo + set +x 2nd echo执行时,set -x不会被跟踪,因为跟踪功能是在这条命令执行完才被打开的。同理,set +x会被跟踪,因为...关闭的。最后的echo命令不会被跟踪,因为此时跟踪功能已经关闭。

浙公网安备 33010602011771号