Mosbyllc


  • 首页

  • 归档

  • 分类

  • 标签

  • 关于

  • 搜索

python编程进阶(6):装饰器

发表于 2018-06-29 | 分类于 python编程进阶
字数统计: 2.7k | 阅读时长 ≈ 11

一切皆对象

首先我们来理解下Python中的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def hi(name="yasoob"):
return "hi " + name

print(hi())
# output: 'hi yasoob'

# 我们甚至可以将一个函数赋值给一个变量,比如
greet = hi
# 我们这里没有在使用小括号,因为我们并不是在调用hi函数
# 而是在将它放在greet变量里头。我们尝试运行下这个

print(greet())
# output: 'hi yasoob'

# 如果我们删掉旧的hi函数,看看会发生什么!
del hi
print(hi())
#outputs: NameError

print(greet()) # 这里可不像数组那样具有对象可变性噢
#outputs: 'hi yasoob'

在函数中定义函数

刚才那些就是函数的基本知识了。我们来让你的知识更进一步。在Python中我们可以在一个函数中定义另一个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def hi(name="yasoob"):
print("now you are inside the hi() function")

def greet():
return "now you are in the greet() function"

def welcome():
return "now you are in the welcome() function"

print(greet())
print(welcome())
print("now you are back in the hi() function")

hi()
#output:now you are inside the hi() function
# now you are in the greet() function
# now you are in the welcome() function
# now you are back in the hi() function

# 上面展示了无论何时你调用hi(), greet()和welcome()将会同时被调用。
# 然后greet()和welcome()函数在hi()函数之外是不能访问的,比如:

greet()
#outputs: NameError: name 'greet' is not defined

那现在我们知道了可以在函数中定义另外的函数。也就是说:我们可以创建嵌套的函数。现在你需要再多学一点,就是函数也能返回函数。

从函数中返回函数

其实并不需要在一个函数里去执行另一个函数,我们也可以将其作为输出返回出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def hi(name="yasoob"):
def greet():
return "now you are in the greet() function"

def welcome():
return "now you are in the welcome() function"

if name == "yasoob":
return greet
else:
return welcome

a = hi() # 执行hi()函数,得到return greet(注意没有括号),将函数赋给a
print(a)
#outputs: <function greet at 0x7f2143c01500>

#上面清晰地展示了`a`现在指向到hi()函数中的greet()函数
#现在试试这个

print(a())
#outputs: now you are in the greet() function

再次看看这个代码。在if/else语句中我们返回greet和welcome,而不是greet()和welcome()。为什么那样?这是因为当你把一对小括号放在后面,这个函数就会执行;然而如果你不放括号在它后面,那它可以被到处传递,并且可以赋值给别的变量而不去执行它。

你明白了吗?让我再稍微多解释点细节。

当我们写下a = hi(),hi()会被执行,而由于name参数默认是yasoob,所以函数greet被返回了。如果我们把语句改为a = hi(name = "ali"),那么welcome函数将被返回。我们还可以打印出hi()(),这会输出now you are in the greet() function。连续两个括号的函数执行也可以,只要第一个hi()返回一个函数即可.

将函数作为参数传给另一个函数

1
2
3
4
5
6
7
8
9
10
11
def hi():
return "hi yasoob!"

def doSomethingBeforeHi(func):
print("I am doing some boring work before executing hi()")
print(func())

doSomethingBeforeHi(hi)
#outputs:I am doing some boring work before executing hi()
# hi yasoob!

现在你已经具备所有必需知识,来进一步学习装饰器真正是什么了。装饰器让你在一个函数的前后去执行代码。

你的第一个装饰器

在上一个例子里,其实我们已经创建了一个装饰器!现在我们修改下上一个装饰器,并编写一个稍微更有用点的程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def a_new_decorator(a_func):

def wrapTheFunction():
print("I am doing some boring work before executing a_func()")

a_func()

print("I am doing some boring work after executing a_func()")

return wrapTheFunction

def a_function_requiring_decoration():
print("I am the function which needs some decoration to remove my foul smell")

a_function_requiring_decoration()
#outputs: "I am the function which needs some decoration to remove my foul smell"

a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
# 将带参数a_new_decorator函数传递给a_function_requiring_decoration
#now a_function_requiring_decoration is wrapped by wrapTheFunction()

a_function_requiring_decoration()
# 执行a_function_requiring_decoration,也就是执行带参数的a_new_decorator函数
#outputs:I am doing some boring work before executing a_func()
# I am the function which needs some decoration to remove my foul smell
# I am doing some boring work after executing a_func()

你看明白了吗?我们刚刚应用了之前学习到的原理。这正是python中装饰器做的事情!它们封装一个函数,并且用这样或者那样的方式来修改它的行为。现在你也许疑惑,我们在代码里并没有使用@符号?那只是一个简短的方式来生成一个被装饰的函数。这里是我们如何使用@来运行之前的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@a_new_decorator #将@下面的函数作为参数传入a_new_decorator函数,并合成@下面的同名函数。
def a_function_requiring_decoration():
"""Hey you! Decorate me!"""
print("I am the function which needs some decoration to "
"remove my foul smell")

a_function_requiring_decoration() # 执行同名函数,也即执行带参的修饰器函数
#outputs: I am doing some boring work before executing a_func()
# I am the function which needs some decoration to remove my foul smell
# I am doing some boring work after executing a_func()

#the @a_new_decorator is just a short way of saying:
a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)

希望你现在对Python装饰器的工作原理有一个基本的理解。如果我们运行如下代码会存在一个问题:

1
2
3
print(a_function_requiring_decoration.__name__)
# Output: wrapTheFunction

这并不是我们想要的!Ouput输出应该是“a_function_requiring_decoration”。这里的函数被warpTheFunction替代了。它重写了我们函数的名字和注释文档(docstring)。幸运的是Python提供给我们一个简单的函数来解决这个问题,那就是functools.wraps。我们修改上一个例子来使用functools.wraps:在装饰器函数内加入@wraps(a_func)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from functools import wraps

def a_new_decorator(a_func):
@wraps(a_func)
def wrapTheFunction():
print("I am doing some boring work before executing a_func()")
a_func()
print("I am doing some boring work after executing a_func()")
return wrapTheFunction

@a_new_decorator
def a_function_requiring_decoration():
"""Hey yo! Decorate me!"""
print("I am the function which needs some decoration to "
"remove my foul smell")

print(a_function_requiring_decoration.__name__)
# Output: a_function_requiring_decoration

现在好多了。我们接下来学习装饰器的一些常用场景。

蓝本规范:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from functools import wraps
def decorator_name(f):
@wraps(f)
def decorated(*args, **kwargs):
if not can_run:
return "Function will not run"
return f(*args, **kwargs)
return decorated

@decorator_name
def func():
return("Function is running")

can_run = True # 全局变量
print(func()) # 由于func函数有装饰器,执行func即执行func为参的修饰器函数
# Output: Function is running

can_run = False
print(func())
# Output: Function will not run

注意:@wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性。

使用场景:授权(Authorization)

装饰器能有助于检查某个人是否被授权去使用一个web应用的端点(endpoint)。它们被大量使用于Flask和Django web框架中。这里是一个例子来使用基于装饰器的授权:

1
2
3
4
5
6
7
8
9
10
from functools import wraps

def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
authenticate()
return f(*args, **kwargs)
return decorated

使用场景:日志(Logging)

日志是装饰器运用的另一个亮点。这是个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from functools import wraps

def logit(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called") # 打印调用函数信息并执行相关函数
return func(*args, **kwargs)
return with_logging

@logit
def addition_func(x):
"""Do some math."""
return x + x

result = addition_func(4)
# Output: addition_func was called

我敢肯定你已经在思考装饰器的一个其他聪明用法了。

带参数的装饰器

来想想这个问题,难道@wraps不也是个装饰器吗?但是,它接收一个参数,就像任何普通的函数能做的那样。那么,为什么我们不也那样做呢?

这是因为,当你使用@my_decorator语法时,你是在应用一个以单个函数作为参数的一个包裹函数。记住,Python里每个东西都是一个对象,而且这包括函数!记住了这些,我们可以编写一下能返回一个包裹函数的函数。

在函数中嵌入装饰器

我们回到日志的例子,并创建一个包裹函数,能让我们指定一个用于输出的日志文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from functools import wraps

def logit(logfile='out.log'):
def logging_decorator(func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
# 打开logfile,并写入内容
with open(logfile, 'a') as opened_file:
# 现在将日志打到指定的logfile
opened_file.write(log_string + '\n')
return func(*args, **kwargs)
return wrapped_function
return logging_decorator # return函数就是执行函数

@logit()
def myfunc1():
pass

myfunc1()
# Output: myfunc1 was called
# 现在一个叫做 out.log 的文件出现了,里面的内容就是上面的字符串

@logit(logfile='func2.log')
def myfunc2():
pass

myfunc2()
# Output: myfunc2 was called
# 现在一个叫做 func2.log 的文件出现了,里面的内容就是上面的字符串

装饰器类

现在我们有了能用于正式环境的logit装饰器,但当我们的应用的某些部分还比较脆弱时,异常也许是需要更紧急关注的事情。比方说有时你只想打日志到一个文件。而有时你想把引起你注意的问题发送到一个email,同时也保留日志,留个记录。这是一个使用继承的场景,但目前为止我们只看到过用来构建装饰器的函数。

幸运的是,类也可以用来构建装饰器。那我们现在以一个类而不是一个函数的方式,来重新构建logit。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from functools import wraps

class logit(object):
def __init__(self, logfile='out.log'):
self.logfile = logfile

def __call__(self, func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
# 打开logfile并写入
with open(self.logfile, 'a') as opened_file:
# 现在将日志打到指定的文件
opened_file.write(log_string + '\n')
# 现在,发送一个通知
self.notify()
return func(*args, **kwargs) # logit装饰器顺序:写入日志,发送通知,调用相关函数
return wrapped_function

def notify(self):
# logit只打日志,不做别的
pass

这个实现有一个附加优势,在于比嵌套函数的方式更加整洁,而且包裹一个函数还是使用跟以前一样的语法:

1
2
3
4
@logit()	# 类作为装饰器
def myfunc1():
pass

现在,我们给logit创建子类,来添加email的功能(虽然email这个话题不会在这里展开)。

1
2
3
4
5
6
7
8
9
10
11
12
13
class email_logit(logit):	
'''
一个logit的实现版本,可以在函数调用时发送email给管理员
'''
def __init__(self, email='admin@myproject.com', *args, **kwargs):
self.email = email
super(email_logit, self).__init__(*args, **kwargs)

def notify(self):
# 发送一封email到self.email
# 这里就不做实现了
pass

从现在起,@email_logit将会和@logit产生同样的效果,但是在打日志的基础上,还会多发送一封邮件给管理员。

python编程进阶(5):set&三元运算符

发表于 2018-06-28 | 分类于 python编程进阶
字数统计: 569 | 阅读时长 ≈ 2

set(集合)数据结构

set(集合)是一个非常有用的数据结构。它与列表(list)的行为类似,区别在于set不能包含重复的值。
这在很多情况下非常有用。例如你可能想检查列表中是否包含重复的元素,你有两个选择,第一个需要使用for循环,就像这样:

1
2
3
4
5
6
7
8
9
10
11
some_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n']

duplicates = []
for value in some_list:
if some_list.count(value) > 1:
if value not in duplicates:
duplicates.append(value)

print(duplicates)
### 输出: ['b', 'n']

但还有一种更简单更优雅的解决方案,那就是使用集合(sets),你直接这样做:

1
2
3
4
5
some_list = ['a', 'b', 'c', 'b', 'd', 'm', 'n', 'n']
duplicates = set([x for x in some_list if some_list.count(x) > 1])
print(duplicates)
### 输出: set(['b', 'n'])

集合还有一些其它方法,下面我们介绍其中一部分。

交集

你可以对比两个集合的交集(两个集合中都有的数据),如下:

1
2
3
4
5
valid = set(['yellow', 'red', 'blue', 'green', 'black'])
input_set = set(['red', 'brown'])
print(input_set.intersection(valid))
### 输出: set(['red'])

差集

你可以用差集(difference)找出无效的数据,相当于用一个集合减去另一个集合的数据,例如:

1
2
3
4
5
valid = set(['yellow', 'red', 'blue', 'green', 'black'])
input_set = set(['red', 'brown'])
print(input_set.difference(valid))
### 输出: set(['brown'])

你也可以用{}符号来创建集合,如:

1
2
3
a_set = {'red', 'blue', 'green'}
print(type(a_set))
### 输出: <type 'set'>

三元运算符

三元运算符通常在Python里被称为条件表达式,这些表达式基于真(true)/假(false)的条件判断,在Python 2.4以上才有了三元操作。

下面是一个伪代码和例子:

伪代码:

1
2
3
#如果条件为真,返回真 否则返回假
condition_is_true if condition else condition_is_false

例子:

1
2
3
is_fat = True
state = "fat" if is_fat else "not fat"

它允许用简单的一行快速判断,而不是使用复杂的多行if语句。 这在大多数时候非常有用,而且可以使代码简单可维护。

python编程进阶(4):函数式编程

发表于 2018-06-28 | 分类于 python编程进阶
字数统计: 822 | 阅读时长 ≈ 3

Map,Filter 和 Reduce 三个函数能为函数式编程提供便利。我们会通过实例一个一个讨论并理解它们。

Map

Map会将一个函数映射到一个输入列表的所有元素上。这是它的规范:

规范

1
map(function_to_apply, list_of_inputs)

大多数时候,我们要把列表中所有元素一个个地传递给一个函数,并收集输出。比方说:

1
2
3
4
items = [1, 2, 3, 4, 5]
squared = []
for i in items:
squared.append(i**2)

Map可以让我们用一种简单而漂亮得多的方式来实现。就是这样(x为逗号右边的输入元素items):

1
2
items = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, items))

大多数时候,我们使用匿名函数(lambdas)来配合map, 所以我在上面也是这么做的。 不仅用于一列表的输入, 我们甚至可以用于一列表的函数!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def multiply(x):
return (x*x)
def add(x):
return (x+x)

funcs = [multiply, add]
for i in range(5):
value = map(lambda x: x(i), funcs) # 每次执行i时,map将funcs内元素都执行lambda函数,即执行multiply(i)和add(i)
print(list(value))
# 译者注:上面print时,加了list转换,是为了python2/3的兼容性
# 在python2中map直接返回列表,但在python3中返回迭代器
# 因此为了兼容python3, 需要list转换一下

# Output:
# [0, 0]
# [1, 2]
# [4, 4]
# [9, 6]
# [16, 8]

Filter

顾名思义,filter过滤列表中的元素,并且返回一个由所有符合要求的元素所构成的列表,符合要求即函数映射到该元素时返回值为True. 这里是一个简短的例子:

1
2
3
4
5
6
7
8
9
number_list = range(-5, 5)
less_than_zero = filter(lambda x: x < 0, number_list)
print(list(less_than_zero))
# 译者注:上面print时,加了list转换,是为了python2/3的兼容性
# 在python2中filter直接返回列表,但在python3中返回迭代器
# 因此为了兼容python3, 需要list转换一下

# Output: [-5, -4, -3, -2, -1]

这个filter类似于一个for循环,但它是一个内置函数,并且更快。

注意:如果map和filter对你来说看起来并不优雅的话,那么你可以看看另外一章:列表/字典/元组推导式。

译者注:大部分情况下推导式的可读性更好

Reduce

当需要对一个列表进行一些计算并返回结果时,Reduce 是个非常有用的函数。举个例子,当你需要计算一个整数列表的乘积时。Reduce() 函数会对参数序列中元素进行累积,函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。

语法:reduce(function, iterable[, initializer])

参数:

  • function — 函数,有两个参数
  • iterable — 可迭代对象
  • initializer — 可选,初始参数

现在我们来试试 reduce:

1
2
3
4
from functools import reduce
product = reduce( (lambda x, y: x * y), [1, 2, 3, 4] )

# Output: 24

在 Python3 中,reduce() 函数已经被从全局名字空间里移除了,它现在被放置在 fucntools 模块里,如果想要使用它,则需要通过引入 functools 模块来调用 reduce() 函数:

1
from functools import reduce

python编程进阶(3):生成器

发表于 2018-06-28 | 分类于 python编程进阶
字数统计: 1.4k | 阅读时长 ≈ 5

首先我们要理解迭代器(iterators)。根据维基百科,迭代器是一个让程序员可以遍历一个容器(特别是列表)的对象。然而,一个迭代器在遍历并读取一个容器的数据元素时,并不会执行一个迭代。这里有三个部分:

  • 可迭代对象(Iterable)
  • 迭代器(Iterator)
  • 迭代(Iteration)

上面这些部分互相联系。我们会先各个击破来讨论他们,然后再讨论生成器(generators).

可迭代对象(Iterable)

Python中任意的对象,只要它定义了可以返回一个迭代器的__iter__方法,或者定义了可以支持下标索引的__getitem__方法(这些双下划线方法会在其他章节中全面解释),那么它就是一个可迭代对象。简单说,可迭代对象就是能提供迭代器的任意对象。那迭代器又是什么呢?

迭代器(Iterator)

任意对象,只要定义了next(Python2) 或者__next__方法,它就是一个迭代器。就这么简单。现在我们来理解迭代(iteration)

迭代(Iteration)

用简单的话讲,它就是从某个地方(比如一个列表)取出一个元素的过程。当我们使用一个循环来遍历某个东西时,这个过程本身就叫迭代。现在既然我们有了这些术语的基本理解,那我们开始理解生成器吧。

关系图

关系结论:

  • 可迭代对象包含迭代器
  • 如果一个对象拥有__iter__方法,其是可迭代对象;如果一个对象拥有next方法,其是迭代器。
  • 定义可迭代对象,必须实现_iter 方法;定义迭代器,必须实现 _iter和next方法。

生成器(Generators)

生成器也是一种迭代器,但是你只能对其迭代一次。这是因为它们并没有把所有的值存在内存中,而是在运行时生成值。你通过遍历来使用它们,要么用一个“for”循环,要么将它们传递给任意可以进行迭代的函数和结构。大多数时候生成器是以函数来实现的。然而,它们并不返回一个值,而是yield(暂且译作“生出”)一个值。这里有个生成器函数的简单例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def generator_function():
for i in range(10):
yield i

for item in generator_function():
print(item)

# Output: 0
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9

这个案例并不是非常实用。生成器最佳应用场景是:你不想同一时间将所有计算出来的大量结果集分配到内存当中,特别是结果集里还包含循环。

译者注:这样做会消耗大量资源

许多Python 2里的标准库函数都会返回列表,而Python 3都修改成了返回生成器,因为生成器占用更少的资源。

下面是一个计算斐波那契数列的生成器:

1
2
3
4
5
6
7
# generator version
def fibon(n):
a = b = 1
for i in range(n):
yield a
a, b = b, a + b

函数使用方法如下:

1
2
3
for x in fibon(1000000):
print(x)

用这种方式,我们可以不用担心它会使用大量资源。然而,之前如果我们这样来实现的话:

1
2
3
4
5
6
7
8
def fibon(n):
a = b = 1
result = []
for i in range(n):
result.append(a)
a, b = b, a + b
return result

这也许会在计算很大的输入参数时,用尽所有的资源。我们已经讨论过生成器使用一次迭代,但我们并没有测试过。在测试前你需要再知道一个Python内置函数:next()。它允许我们获取一个序列的下一个元素。那我们来验证下我们的理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def generator_function():
for i in range(3):
yield i

gen = generator_function()
print(next(gen))
# Output: 0
print(next(gen))
# Output: 1
print(next(gen))
# Output: 2
print(next(gen))
# Output: Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# StopIteration

我们可以看到,在yield掉所有的值后,next()触发了一个StopIteration的异常。基本上这个异常告诉我们,所有的值都已经被yield完了。你也许会奇怪,为什么我们在使用for循环时没有这个异常呢?啊哈,答案很简单。for循环会自动捕捉到这个异常并停止调用next()。你知不知道Python中一些内置数据类型也支持迭代哦?我们这就去看看:

1
2
3
4
5
6
my_string = "Yasoob"
next(my_string)
# Output: Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# TypeError: str object is not an iterator

好吧,这不是我们预期的。这个异常说那个str对象不是一个迭代器。对,就是这样!它是一个可迭代对象,而不是一个迭代器。这意味着它支持迭代,但我们不能直接对其进行迭代操作。那我们怎样才能对它实施迭代呢?是时候学习下另一个内置函数,iter。它将根据一个可迭代对象返回一个迭代器对象。这里是我们如何使用它:

1
2
3
4
5
my_string = "Yasoob"
my_iter = iter(my_string)
next(my_iter)
# Output: 'Y'

现在好多啦。我肯定你已经爱上了学习生成器。一定要记住,想要完全掌握这个概念,你只有使用它。确保你按照这个模式,并在生成器对你有意义的任何时候都使用它。你绝对不会失望的!

python编程进阶(2):调试Debugging

发表于 2018-06-27 | 分类于 python编程进阶
字数统计: 495 | 阅读时长 ≈ 1

调试(Debugging)

利用好调试,能大大提高你捕捉代码Bug的。大部分新人忽略了Python debugger(pdb)的重要性。 在这个章节我只会告诉你一些重要的命令,你可以从官方文档中学习到更多。

参考:https://docs.python.org/2/library/pdb.html Or https://docs.python.org/3/library/pdb.html

从命令行运行

你可以在命令行使用Python debugger运行一个脚本, 举个例子:

1
2
$ python -m pdb my_script.py

这会触发debugger在脚本第一行指令处停止执行。这在脚本很短时会很有帮助。你可以通过(Pdb)模式接着查看变量信息,并且逐行调试。

从脚本内部运行

同时,你也可以在脚本内部设置断点,这样就可以在某些特定点查看变量信息和各种执行时信息了。这里将使用pdb.set_trace()方法来实现。举个例子:

1
2
3
4
5
6
7
8
import pdb

def make_bread():
pdb.set_trace()
return "I don't have time"

print(make_bread())

试下保存上面的脚本后运行之。你会在运行时马上进入debugger模式。现在是时候了解下debugger模式下的一些命令了。

命令列表:
  • c: 继续执行
  • w: 显示当前正在执行的代码行的上下文信息
  • a: 打印当前函数的参数列表
  • s: 执行当前代码行,并停在第一个能停的地方(相当于单步进入)
  • n: 继续执行到当前函数的下一行,或者当前行直接返回(单步跳过)

单步跳过(next)和单步进入(step)的区别在于, 单步进入会进入当前行调用的函数内部并停在里面, 而单步跳过会(几乎)全速执行完当前行调用的函数,并停在当前函数的下一行。

pdb真的是一个很方便的功能,上面仅列举少量用法,更多的命令强烈推荐你去看官方文档。

python编程进阶(1):*args和**kwargs

发表于 2018-06-27 | 分类于 python编程进阶
字数统计: 608 | 阅读时长 ≈ 2

args和*kwargs

首先, 其实并不是必须写成*args 和**kwargs。 只有变量前面的 *(星号)才是必须的. 你也可以写成*var 和**vars. 而写成*args 和**kwargs只是一个通俗的命名约定。

*args的用法

*args 和 **kwargs 主要用于函数定义。 你可以将不定数量的参数传递给一个函数。这里的不定的意思是:预先并不知道, 函数使用者会传递多少个参数给你, 所以在这个场景下使用这两个关键字。 *args 是用来发送一个非键值对的可变数量的参数列表给一个函数

看下面这个例子

1
2
3
4
5
6
def test_var_args(f_arg, *argv):
print("first normal arg:", f_arg)
for arg in argv:
print("another arg through *argv:", arg)

test_var_args('yasoob', 'python', 'eggs', 'test')

产生输出:

1
2
3
4
first normal arg: yasoob
another arg through *argv: python
another arg through *argv: eggs
another arg through *argv: test

**kwargs的用法

**kwargs 允许你将不定长度的键值对, 作为参数传递给一个函数。 如果你想要在一个函数里处理带名字的参数, 你应该使用**kwargs

看下面的例子

1
2
3
4
5
6
7
def greet_me(**kwargs):
for key, value in kwargs.items():
print("{0} == {1}".format(key, value))


>>> greet_me(name="yasoob")
name == yasoo

使用args和*kwargs来调用函数

假设,你有这样一个小函数:

1
2
3
4
def test_args_kwargs(arg1, arg2, arg3):
print("arg1:", arg1)
print("arg2:", arg2)
print("arg3:", arg3)

你可以使用*args或**kwargs来给这个小函数传递参数。 下面是怎样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 首先使用 *args
>>> args = ("two", 3, 5)
>>> test_args_kwargs(*args)
arg1: two
arg2: 3
arg3: 5

# 现在使用 **kwargs:
>>> kwargs = {"arg3": 3, "arg2": "two", "arg1": 5}
>>> test_args_kwargs(**kwargs)
arg1: 5
arg2: two
arg3: 3

标准参数与args、*kwargs在使用时的顺序:

1
some_func(fargs, *args, **kwargs)

什么时候使用它们?

这还真的要看你的需求而定。

最常见的用例是在写函数装饰器的时候(会在另一章里讨论)。

此外它也可以用来做猴子补丁(monkey patching)。猴子补丁的意思是在程序运行时(runtime)修改某些代码。 打个比方,你有一个类,里面有个叫get_info的函数会调用一个API并返回相应的数据。如果我们想测试它,可以把API调用替换成一些测试数据。例如:

1
2
3
4
5
6
import someclass

def get_info(self, *args):
return "Test data"

someclass.get_info = get_info

The Linux Command Line学习笔记(四)

发表于 2018-06-16 | 分类于 Linux
字数统计: 6.6k | 阅读时长 ≈ 25

第二十一章:文本处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
这一章会重新拜访一些老朋友,并且会给我们介绍一些新朋友:
cat – 连接文件并且打印到标准输出
sort – 给文本行排序
uniq – 报告或者省略重复行
cut – 从每行中删除文本区域
paste – 合并文件文本行
join – 基于某个共享字段来联合两个文件的文本行
comm – 逐行比较两个有序的文件
diff – 逐行比较文件
tr – 翻译或删除字符
sed – 用于筛选和转换文本的流编辑器

cat
这个 cat 程序具有许多有趣的选项。其中许多选项用来帮助更好的可视化文本内容。一个例子是-A 选项, 其用来在文本中显示所有非打印字符。

[me@linuxbox ~]$ cat -A foo.txt
^IThe quick brown fox jumped over the lazy dog. $
[me@linuxbox ~]$
在输出结果中我们看到,这个 tab 字符在我们的文本中由^I 字符来表示,我们也看到一个$字符出现在文本行真正的结尾处, 表明我们的文本包含末尾的空格。

cat 程序也包含用来修改文本的选项。最著名的两个选项是-n,其给文本行添加行号和-s, 禁止输出多个空白行。

[me@linuxbox ~]$ cat > foo.txt
The quick brown fox


jumped over the lazy dog.
[me@linuxbox ~]$ cat -ns foo.txt #增加行号,删除多余的空白行
1 The quick brown fox
2
3 jumped over the lazy dog.
[me@linuxbox ~]$

sort
我们能够演示如何用 sort 程序来处理标准输入:
[me@linuxbox ~]$ sort > foo.txt

c
b
a
[me@linuxbox ~]$ cat foo.txt
a
b
c

表21-1: 常见的 sort 程序选项
| 选项 | 长选项 | 描述 |
|-------|-------|-------|
| -b | --ignore-leading-blanks | 默认情况下,从每行的第一个字符开始。这个选项从第一个非空白字符开始排序。 |
| -f | --ignore-case | 让排序不区分大小写。 |
| -n | --numeric-sort | 基于字符串的长度来排序
| -r | --reverse | 按相反顺序排序。结果按照降序排列,而不是升序。 |
| -k | --key=field1[,field2] | 对从 field1到 field2之间的字符排序,而不是整个文本行
| -o | --output=file | 把排好序的输出结果发送到文件,而不是标准输出。 |
| -t | --field-separator=char | 定义域分隔字符。

我们通过对 du 命令的输出结果排序来说明这个选项,du 命令可以 确定最大的磁盘空间用户,通常,这个du 命令列出的输出结果按照路径名来序:

[me@linuxbox ~]$ du -s /usr/share/\* | head
252 /usr/share/aclocal
96 /usr/share/acpi-support
8 /usr/share/adduser
...

[me@linuxbox ~]$ du -s /usr/share/* | sort -nr | head #根据长度反序排序
509940 /usr/share/locale-langpack
242660 /usr/share/doc
197560 /usr/share/fonts
这种排序起作用是 因为数值出现在每行的开头。但是如果我们想要基于文件行中的某个数值排序
忽略 ls 程序能按照文件大小对输出结果进行排序,我们也能够使用 sort 程序来完成此任务:
[me@linuxbox ~]$ ls -l /usr/bin | sort -nr -k 5 | head #空格和制表符作为分界符,所以是以第五个字段,文件大小来排序
-rwxr-xr-x 1 root root 8234216 2008-04-0717:42 inkscape
-rwxr-xr-x 1 root root 8222692 2008-04-07 17:42 inkview
...
在上面的例子中,我们指定 n 和 r 选项来执行相反的数值排序,并且指定 -k 5,让 sort 程序使用第五字段作为排序的关键值。

依赖多个关键值排序,例如 文件中的每一行都有三个字段:发行版的名称,版本号,和 MM/DD/YYYY 格式的发行日期:

SUSE 10.2 12/07/2006
Fedora 10 11/25/2008
SUSE 11.04 06/19/2008
Ubuntu 8.04 04/24/2008
...
为了解决这个问题,我们必须依赖多个键值来排序。sort 程序允许多个 -k 选项的实例,所以可以指定多个排序关键值。

[me@linuxbox ~]$ sort --key=1,1 --key=2n distros.txt
Fedora 5 03/20/2006
Fedora 6 10/24/2006
Fedora 7 05/31/2007
...
在第一个 key 选项的实例中, 我们指定了一个字段区域。因为我们只想对第一个字段排序,我们指定了 1,1, 意味着“始于并且结束于第一个字段。”在第二个实例中,我们指定了 2n,意味着第二个字段是排序的键值,选项字母和sort 程序的全局选项一样:b(忽略开头的空格),n(数值序),r(逆向排序),等等。
我们列表中第三个字段包含的日期格式不利于排序。在计算机中,日期通常设置为 YYYY-MM-DD 格式,这样使按时间顺序排序变得容易,但是我们的日期为美国格式 MM/DD/YYYY。那么我们怎样能按照 时间顺序来排列这个列表呢?幸运地是,sort 程序提供了一种方式。这个 key 选项允许在字段中指定偏移量,所以我们能在字段中 定义键值。
[me@linuxbox ~]$ sort -k 3.7nbr -k 3.1nbr -k 3.4nbr distros.txt
Fedora 10 11/25/2008
Ubuntu 8.10 10/30/2008
SUSE 11.0 06/19/2008
...
通过指定 -k 3.7,我们指示 sort 程序使用一个排序键值,其始于第三个字段中的第七个字符,同样地,我们指定 -k 3.1和 -k 3.4来分离日期中的月和日。 我们也添加了 n 和 r 选项来实现一个逆向的数值排序。这个 b 选项用来删除日期字段中开头的空格( 行与行之间的空格数迥异,因此会影响 sort 程序的输出结果)。

一些文件不会使用 tabs 和空格做为字段界定符;例如,这个 /etc/passwd 文件,这个文件的字段之间通过冒号分隔开,所以我们怎样使用一个 key 字段来排序这个文件?sort 程序提供了一个 -t 选项来自定义分隔符。
[me@linuxbox ~]$ sort -t ':' -k 7 /etc/passwd | head # 以 : 为分隔符

uniq
与 sort 程序相比,这个 uniq 程序是个轻量级程序,uniq 会删除任意重复行,并且把结果发送到标准输出。 它常常和 sort 程序一
块使用,来清理重复的输出。
让我们创建一个文本文件,来实验一下:

[me@linuxbox ~]$ cat > foo.txt
a
b
c
a
b
c
住输入 Ctrl-d 来终止标准输入。现在,如果我们对文本文件执行 uniq 命令:

[me@linuxbox ~]$ uniq foo.txt
a
b
c
a
b
c
输出结果与原始文件没有差异;重复行没有被删除。实际上,uniq 程序能完成任务,其输入必须是排好序的数据,

[me@linuxbox ~]$ sort foo.txt | uniq
a
b
c
这是因为 uniq 只会删除相邻的重复行。uniq 程序有几个选项。这里是一些常用选项:
表21-2: 常用的 uniq 选项
| 选项 | 说明 |
|-------|-----|
| -c | 输出所有的重复行,并且每行开头显示重复的次数。 |
| -d | 只输出重复行,而不是特有的文本行。 |
| -i | 在比较文本行的时候忽略大小写。 |
| -u | 只是输出独有的文本行。这是默认的。 |

[me@linuxbox ~]$ sort foo.txt | uniq -c
2 a
2 b
2 c

切片和切块
cut
这个 cut 程序被用来从文本行中抽取文本,并把其输出到标准输出。从文本行中指定要抽取的文本有些麻烦,使用以下选项:
表21-3: cut 程序选择项
| 选项 | 说明 |
|-----|------|
| -c char_list | 从文本行中抽取由 char_list 定义的文本,也就是字符。
| -f field_list | 从文本行中抽取一个或多个由 field_list 定义的字段
| -d delim_char | 当指定-f 选项之后,使用 delim_char 做为字段分隔符
| --complement | 抽取整个文本行,除了那些由-c 和/或-f 选项指定的文本。 |
正如我们所看到的,cut 程序抽取文本的方式相当不灵活。cut 命令最好用来从其它程序产生的文件中 抽取文本,而不是从人们直接输入的文本中抽取。
如果我们使用带有 -A 选项的 cat 命令,我们能查看是否这个文件符号由 tab 字符分离字段的要求。
[me@linuxbox ~]$ cat -A distros.txt
SUSE^I10.2^I12/07/2006$ #看起来不错。字段之间仅仅是单个 tab 字符,没有嵌入空格
Fedora^I10^I11/25/2008$
SUSE^I11.0^I06/19/2008$
...
[me@linuxbox ~]$ cut -f 3 distros.txt #第三个字段
12/07/2006
11/25/2008
06/19/2008
04/24/2008
...
因为我们的 distros 文件是由 tab 分隔开的,最好用 cut 来抽取字段而不是字符。这是因为一个由 tab 分离的文件, 每行不太可能包含相同的字符数,这就使计算每行中字符的位置变得困难或者是不可能。然而, 我们已经抽取了一个字段,幸运地是其包含地日期长度相同,所以通过从每行中抽取年份,我们能展示怎样 来抽取字符:
[me@linuxbox ~]$ cut -f 3 distros.txt | cut -c 7-10
2006
2008
2007
...
通过对我们的列表再次运行 cut 命令,我们能够抽取从位置7到10的字符,其对应于日期字段的年份.
当操作字段的时候,有可能指定不同的字段分隔符,而不是 tab 字符。这里我们将会从/etc/passwd 文件中 抽取第一个字段:
[me@linuxbox ~]$ cut -d ':' -f 1 /etc/passwd | head #使用-d 选项,我们能够指定冒号做为字段分隔符。

paste
这个 paste 命令的功能正好与 cut 相反。它会添加一个或多个文本列到文件中,而不是从文件中抽取文本列
[me@linuxbox ~]$ paste distros-dates.txt distros-versions.txt #第二个文件粘贴在第一个文件后

join
一个 join操作通常与关系型数据库有关联,在关系型数据库中来自多个享有共同关键域的表格的 数据结合起来,得到一个期望的结果。这个 join 程序执行相同的操作
[me@linuxbox ~]$ join distros-key-names.txt distros-key-vernums.txt | head
这两个文件的日期是共同列,可以自动连接。同样,第二个文件链接到第一个文件后面

比较文本
comm
这个 comm 程序会比较两个文本文件,并且会显示每个文件特有的文本行和共有的文本行。通过使用 cat 命令,我们将会创建两个内容几乎相同的文本文件:

[me@linuxbox ~]$ cat > file1.txt
a
b
c
d
[me@linuxbox ~]$ cat > file2.txt
b
c
d
下一步,我们将使用 comm 命令来比较这两个文件:

[me@linuxbox ~]$ comm file1.txt file2.txt
a
b
c
d
e
正如我们所见到的,comm 命令产生了三列输出。第一列包含第一个文件独有的文本行;第二列, 文本行是第二列独有的;第三列包含两个文件共有的文本行。comm 支持 -n 形式的选项,这里 n 代表 1,2或 3。这些选项使用的时候,指定了要隐藏的列。例如,如果我们只想输出两个文件共享的文本行, 我们将隐藏第一列和第二列的输出结果:

[me@linuxbox ~]$ comm -12 file1.txt file2.txt
b
c
d

diff
类似于 comm 程序,diff 程序被用来监测文件之间的差异。软件开发员经常使用 diff 程序来检查不同程序源码 版本之间的更改,diff 能够递归地检查源码目录,经常称之为源码树。diff 程序的一个常见用例是 创建 diff 文件或者补丁,它会被其它程序使用,例如 patch 程序(我们一会儿讨论),来把文件 从一个版本转换为另一个版本。
如果我们使用 diff 程序,来查看我们之前的文件实例:

[me@linuxbox ~]$ diff file1.txt file2.txt
1d0
< a
4a4
> e
在默认格式中(不常用), 每组的更改之前都是一个更改命令,其形式为 range operation range , 用来描述要求更改的位置和类型,从而把第一个文件转变为第二个文件:
表21-4: diff 更改命令
| 改变 | 说明 |
|-----|--------|
| r1ar2 | 把第二个文件中位置 r2 处的文件行添加到第一个文件中的 r1 处。 |
| r1cr2 | 用第二个文件中位置 r2 处的文本行更改(替代)位置 r1 处的文本行。 |
| r1dr2 | 删除第一个文件中位置 r1 处的文本行,这些文本行将会出现在第二个文件中位置 r2 处。 |

常用的是上下文模式和统一模式(记住这个就行了)
当使用上下文模式(带上 -c 选项),我们将看到这些:

[me@linuxbox ~]$ diff -c file1.txt file2.txt
*** file1.txt 2008-12-23 06:40:13.000000000 -0500
--- file2.txt 2008-12-23 06:40:34.000000000 -0500
***************
*** 1,4 ****
- a
b
c
d
--- 1,4 ----
b
c
d
+ e
表21-5: diff 上下文模式更改指示符
| 指示符 | 意思 |
|-------|---------|
| blank | 上下文显示行。它并不表示两个文件之间的差异。 |
| - | 删除行。这一行将会出现在第一个文件中,而不是第二个文件内。 |
| + | 添加行。这一行将会出现在第二个文件内,而不是第一个文件中。 |
| ! | 更改行。将会显示某个文本行的两个版本,每个版本会出现在更改组的各自部分。 |

这个统一模式相似于上下文模式,但是更加简洁。通过 -u 选项来指定它:

[me@linuxbox ~]$ diff -u file1.txt file2.txt
--- file1.txt 2008-12-23 06:40:13.000000000 -0500
+++ file2.txt 2008-12-23 06:40:34.000000000 -0500
@@ -1,4 +1,4 @@
-a
b
c
d
+e
上下文模式和统一模式之间最显著的差异就是重复上下文的消除
表21-6: diff 统一模式更改指示符
| 字符 | 意思 |
|-----|-------|
| 空格 | 两个文件都包含这一行。 |
| - | 在第一个文件中删除这一行。 |
| + | 添加这一行到第一个文件中。 |

patch
Linux 内核是由一个 大型的,组织松散的贡献者团队开发而成,这些贡献者会提交固定的少量更改到源码包中。 这个 Linux 内核由几百万行代码组成,虽然每个贡献者每次所做的修改相当少。对于一个贡献者 来说,每做一个修改就给每个开发者发送整个的内核源码树,这是没有任何意义的。相反, 提交一个 diff 文件。一个 diff 文件包含先前的内核版本与带有贡献者修改的新版本之间的差异。 然后一个接受者使用 patch 程序,把这些更改应用到他自己的源码树中。

运行时编辑
tr
这个 tr 程序被用来更改字符。我们可以把它看作是一种基于字符的查找和替换操作。我们可以通过 tr 命令来执行这样的转换,如下所示:
[me@linuxbox ~]$ echo "lowercase letters" | tr a-z A-Z
LOWERCASE LETTERS
tr 命令接受两个参数:要被转换的字符集以及 相对应的转换后的字符集。
tr 也可以完成另一个技巧。使用-s 选项,tr 命令能“挤压”(删除)重复的字符实例:
[me@linuxbox ~]$ echo "aaabbbccc" | tr -s ab
abccc
这里我们有一个包含重复字符的字符串。通过给 tr 命令指定字符集“ab”,我们能够消除字符集中 字母的重复实例,然而会留下不属于字符集的字符(“c”)无更改。注意重复的字符必须是相邻的, 如果它们不相邻,那么挤压会没有效果,例如:
[me@linuxbox ~]$ echo "abcabcabc" | tr -s ab
abcabcabc

sed
名字 sed 是 stream editor(流编辑器)的简称。它对文本流进行编辑,要不是一系列指定的文件, 要不就是标准输入。这里有一个非常简单的 sed 实例:
[me@linuxbox ~]$ echo "front" | sed 's/front/back/'
back
在这个例子中,我们使用 echo 命令产生了一个单词的文本流,然后把它管道给 sed 命令。sed,依次,对流文本执行指令 s/front/back/,随后输出“back”。这个替换命令由字母 s 来代表,其后跟着查找 和替代字符串,斜杠字符做为分隔符。分隔符的选择是随意的。按照惯例,经常使用斜杠字符。

sed 中的大多数命令之前都会带有一个地址,其指定了输入流中要被编辑的文本行。如果省略了地址, 然后会对输入流的每一行执行编辑命令。最简单的地址形式是一个行号。我们能够添加一个地址 到我们例子中:
[me@linuxbox ~]$ echo "front" | sed '1s/front/back/'
back
给我们的命令添加地址 1,就导致只对仅有一行文本的输入流的第一行执行替换操作。如果我们指定另一个数字:
[me@linuxbox ~]$ echo "front" | sed '2s/front/back/'
front
我们看到没有执行这个编辑命令,因为我们的输入流没有第二行。地址可以用许多方式来表达。这里是 最
常用的:
表21-7: sed 地址表示法
| 地址 | 说明 |
|-----|-------|
| n | 行号,n 是一个正整数。 |
| $ | 最后一行。 |
| /regexp/ | 所有匹配一个 POSIX 基本正则表达式的文本行
| addr1,addr2 | 从 addr1 到 addr2 范围内的文本行,包含地址 addr2 在内。地址可能是上述任意 单独
的地址形式。 |
| first~step | 匹配由数字first代表的文本行,然后随后的每个在 step 间隔处的文本行。例如 5~5 则指第五行和之后每五行位置的文本。行。 |
| addr1,+n | 匹配地址 addr1 和随后的 n 个文本行。 |
| addr! | 匹配所有的文本行,除了 addr 之外
通过使用这一章中早前的 distros.txt 文件,我们将演示不同种类的地址表示法。首先,一系列行号:
[me@linuxbox ~]$ sed -n '1,5p' distros.txt
SUSE 10.2 12/07/2006
Fedora 10 11/25/2008
SUSE 11.0 06/19/2008
Ubuntu 8.04 04/24/2008
Fedora 8 11/08/2007
在这个例子中,我们打印出一系列的文本行,开始于第一行,直到第五行。为此,我们使用 p 命令, 其就是简单地把匹配的文本行打印出来。然而为了高效,我们必须包含选项 -n(不自动打印选项), 让sed 不要默认地打印每一行。

下一步,我们将试用一下正则表达式:
[me@linuxbox ~]$ sed -n '/SUSE/p' distros.txt # 斜杠界定的正则表达式
SUSE 10.2 12/07/2006
SUSE 11.0 06/19/2008
SUSE 10.3 10/04/2007
SUSE 10.1 05/11/2006
最后,我们将试着否定上面的操作,通过给这个地址添加一个感叹号:
[me@linuxbox ~]$ sed -n '/SUSE/!p' distros.txt
Fedora 10 11/25/2008
Ubuntu 8.04 04/24/2008
Fedora 8 11/08/2007
Ubuntu 6.10 10/26/2006
这里我们看到期望的结果:输出了文件中所有的文本行,除了那些匹配这个正则表达式的文本行。
目前为止,我们已经知道了两个 sed 的编辑命令,s 和 p。这里是一个更加全面的基本编辑命令列表:
表21-8: sed 基本编辑命令
| 命令 | 说明 |
|-----|-------|
| = | 输出当前的行号。 |
| a | 在当前行之后追加文本。 |
| d | 删除当前行。 |
| i | 在当前行之前插入文本。 |
| p | 打印当前行。默认情况下,sed 程序打印每一行,并且只是编辑文件中匹配指定地址的文本行。通过指定-n 选项,这个默认的行为能够被略。 |
| q | 退出 sed,不再处理更多的文本行。如果不指定-n 选项,输出当前行。 |
| Q | 退出 sed,不再处理更多的文本行。 |
| s/regexp/replacement/ | 只要找到一个 regexp 匹配项,就替换为 replacement 的内

到目前为止,这个 s 命令是最常使用的编辑命令。
[me@linuxbox ~]$ echo "aaabbbccc" | sed 's/b/B/'
aaa Bbbccc
我们看到虽然执行了替换操作,但是只针对第一个字母 “b” 实例,然而剩余的实例没有更改。通过添加g 标志, 我们能够更改所有的实例:
[me@linuxbox ~]$ echo "aaabbbccc" | sed 's/b/B/g'

第二十二章:格式化输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
在这章中,我们继续着手于文本相关的工具,关注那些用来格式化输出的程序,而不是改变文本自身。    这些工具通常让文本准备就绪打印,这是我们在下一章会提到的。
nl - 添加行号
nl 程序是一个相当神秘的工具,用作一个简单的任务。它添加文件的行数。在它最简单的用途中,它相当于 cat -n:

[me@linuxbox ~]$ nl distros.txt | head

1 Fedora 5 2006-03-20
2 Fedora 6 2006-10-24
3 Fedora 7 2007-05-31

fold - 限制文件行宽

[me@linuxbox ~]$ echo "The quick brown fox jumped over the lazy dog." | fold -w 12
The quick br
own fox jump
ed over the
lazy dog.
我们设定了行宽为12个字符。 如果没有字符设置,默认是80。注意到文本行不会因为单词边界而不会被分解。增加的 -s 选项将让 fold 分解到最后可用的空白 字符,即会考虑单词边界。

[me@linuxbox ~]$ echo "The quick brown fox jumped over the lazy dog."
| fold -w 12 -s
The quick
brown fox
jumped over
the lazy
dog.

第二十三章:打印

非目前学习重点

第二十四章:编译程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
用高级语言编写的程序,经过另一个称为编译器的程序的处理,会转换成机器语言。一些编译器把    高级指令翻译成汇编语言,然后使用一个汇编器完成翻译成机器语言的最后阶段。
Python脚本在Linux上怎么运行?
[root@qiansw tmp]# cat test.py #这是一个python示例程序
1
2
3
#!/usr/bin/python
for i in range(0,5):
print i
[root@qiansw tmp]# ll
total 48
-rw-r--r-- 1 root root 48 Oct 30 11:04 test.py
[root@qiansw tmp]# chmod +x test.py #为脚本增加执行权限,另外一种表达[root@qiansw tmp]# chmod 755 test.py #为脚本增加执行权限
[root@qiansw tmp]# ./test.py #这是第一种方法
0
1
2
3
4
[root@qiansw tmp]# python test.py #这是第二种方法,不用增加执行权限
0
1
2
3
4

有两种方式:
1、直接使用python xxxx.py执行。其中python可以写成python的绝对路径。使用which python进行查询。
2、在文件的头部(第一行)写上#!/usr/bin/python2.7,这个地方使用python的绝对路径,就是上面用which python查询来的结果。然后在外面就可以使用./xxx.py执行了。
因为在linux中,python啊shell这些程序都是普通的文本格式,都需要一种程序去解释执行它。要么调用的时候指定,要么在文件头指定。

第二十五章:编写第一个shell脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
最简单的解释,一个    shell    脚本就是一个包含一系列命令的文件。shell    读取这个文件,然后执行    文件中的所有命令,就好像这些命令已经直接被输入到了命令行中一样。

我们把此脚本文件保存为 hello_world。
下一步我们要做的事情是让我们的脚本可执行。使用 chmod 命令,这很容易做到:
[me@linuxbox ~]$ ls -l hello_world
-rw-r--r-- 1 me me 63 2009-03-07 10:10 hello_world
[me@linuxbox ~]$ chmod 755 hello_world
[me@linuxbox ~]$ ls -l hello_world
-rwxr-xr-x 1 me me 63 2009-03-07 10:10 hello_world
对于脚本文件,有两个常见的权限设置;权限为755的脚本,则每个人都能执行,和权限为700的 脚本,
只有文件所有者能够执行。

脚本文件位置
当设置了脚本权限之后,我们就能执行我们的脚本了:
[me@linuxbox ~]$ ./hello_world
Hello World!
为了能够运行此脚本,我们必须指定脚本文件明确的路径。如果我们没有那样做,我们会得到这样的提示:
[me@linuxbox ~]$ hello_world
bash: hello_world: command not found

回到第12章,我们讨论了 PATH 环境变量及其它在系统 查找可执行程序方面的作用。回顾一下,如果没有给出可执行程序的明确路径名,那么系统每次都会 搜索一系列的目录,来查找此可执行程序。这个/bin 目录就是其中一个系统会自动搜索的目录。 这个目录列表被存储在一个名为PATH 的环境变量中。

大多数的 Linux 发行版会配置 PATH 变量,让其包含一个位于用户家目录下的 bin 目录,从而允许用户能够执行他们自己的程序。所以如果我们创建了 一个bin 目录,并把我们的脚本放在这个目录下,那么这个脚本就应该像其它程序一样开始工作了:
[me@linuxbox ~]$ mkdir bin
[me@linuxbox ~]$ mv hello_world bin #移动到用户家目录下的bin目录下
[me@linuxbox ~]$ hello_world
Hello World!

如果这个 PATH 变量不包含这个目录(记住以下两个命令就可以了),我们能够轻松地添加它,通过在我们的.bashrc 文件中包含下面 这一行文本:

export PATH=~/bin:"$PATH"
当做了这个修改之后,它会在每个新的终端会话中生效。为了把这个修改应用到当前的终端会话中, 我们必须让 shell 重新读取这个 .bashrc 文件。这可以通过 “sourcing”.bashrc 文件来完成:

[me@linuxbox ~]$ . .bashrc

脚本文件的好去处
这个 ~/bin 目录是存放为个人所用脚本的好地方。如果我们编写了一个脚本,系统中的每个用户都可以使用它, 那么这个脚本的传统位置是 /usr/local/bin。系统管理员使用的脚本经常放到 /usr/local/sbin 目录下。 大多数情况下,本地支持的软件,不管是脚本还是编译过的程序,都应该放到 /usr/local 目录下,而不是在 /bin 或 /usr/bin 目录下。这些目录都是由 Linux 文件系统层次结构标准指定,只包含由 Linux发行商 所提供和维护的文件。

The Linux Command Line学习笔记(三)

发表于 2018-06-12 | 分类于 Linux
字数统计: 5.4k | 阅读时长 ≈ 20

第十六章:存储媒介

非目前学习重点,暂略

第十七章:网络系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
我们仅仅会关注一些最经常    使用到的命令。我们要研究的命令包括那些被用来监测网络和传输文件的命令。
ping - 发送 ICMP ECHO_REQUEST 软件包到网络主机
traceroute - 打印到一台网络主机的路由数据包
netstat - 打印网络连接,路由表,接口统计数据,伪装连接,和多路广播成员
ftp - 因特网文件传输程序
wget - 非交互式网络下载器
ssh - Open SSH SSH 客户端(远程登录程序)

ping,网络连接验证, 按下组合键 Ctrl-c,中断这个命令之后
[me@linuxbox ~]$ ping www.baidu.com

网络中传输文件
ftp
ftp 命令属于真正的“经典”程序之一,它的名字来源于其所使用的协议,就是文件传输协议。FTP(它的原始形式)并不是安全的,因为它会以明码形式发送帐号的姓名和密码,由于此种原因,几乎因特网中所有 FTP 服务器 都是匿名的

在下面的例子中,我们将展示一个典型的会话,从匿名 FTP 服务器,其名字是 fileserver,的/pub/_images/Ubuntu-8.04的目录下,使用 ftp 程序下载一个 Ubuntu 系统映像文件。

[me@linuxbox ~]$ ftp fileserver
Name (fileserver:me): anonymous
ftp> cd pub/cd\_images/Ubuntu-8.04
ftp> ls
ftp> lcd Desktop
ftp> get ubuntu-8.04-desktop-i386.iso
ftp> bye
|---------|------------|
1、 唤醒 ftp 程序,让它连接到 FTP 服务器,fileserver。 |
2、 登录名。输入登录名后,将出现一个密码提示。一些服务器将会接受空密码, 其它一些则
会要求一个邮件地址形式的密码。如果是这种情况,试着输入 “user@example.com”。 |
3、 跳转到远端系统中,要下载文件所在的目录下,注意在大多数匿名的 FTP 服务器中,支持公共下载的文件都能在目录 pub 下找到 |
4、列出远端系统中的目录。 |
5、 跳转到本地系统中的 ~/Desktop目录下。在实例中,ftp 程序在工作目录 ~ 下被唤醒。这个命令把工作目录改为 ~/Desktop |
6、 告诉远端系统传送文件到本地。因为本地系统的工作目录 已经更改到了 ~/Desktop,所以文件会被下载到此目录。 |

另外还有两个常用传输文件程序:
lftp - 更好的 ftp,包括 多协议支持(包括 HTTP),若下载失败会自动地重新下载
wget - 不只能下载单个文件,多个文件,甚至整个网站都能下载

与远程主机安全通信
明码形式来传输所有的交流信息(包括登录命令和密码)。这使它们完全不 适合使用在因特网时代。
ssh
为了解决这个问题,开发了一款新的协议,叫做 SSH(Secure Shell)。 SSH 首先,它要认证远端主机是否为它 所知道的那台主机(这样就阻止了所谓的“中间人”的攻击),其次,它加密了本地与远程主机之间 所有的通讯信息。SSH 由两部分组成。SSH 服务器运行在远端主机上运行,在端口号22上监听将要到来的连接,而 SSH 客户端用在本地系统中,用来和远端服务器通信。

用来与远端 SSH 服务器相连接的 SSH 客户端程序,顺理成章,叫做 ssh。连接到远端名为 remote-sys主机,我们可以这样使用 ssh 客户端程序:

[me@linuxbox ~]$ ssh remote-sys
The authenticity of host 'remote-sys (192.168.1.4)' can't be
established.
RSA key fingerprint is
41:ed:7a:df:23:19:bf:3c:a5:17:bc:61:b3:7f:d9:bb.
Are you sure you want to continue connecting (yes/no)?
一旦建立了连接,会提示 用户输入他或她的密码:

远端 shell 会话一直存在,直到用户输入 exit 命令后,则关闭了远程连接。这时候,本地的 shell 会话 恢
复,本地 shell 提示符重新出现。


也有可能使用不同的用户名连接到远程系统。例如,如果本地用户“me”,在远端系统中有一个帐号名“bob”,则用户 me 能够用 bob 帐号登录到远端系统,如下所示:

[me@linuxbox ~]$ ssh bob@remote-sys
bob@remote-sys's password:
Last login: Sat Aug 30 13:03:21 2008
[bob@remote-sys ~]$

第十八章:查找文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
在这一章中,我们将察看    两个用来在系统中查找文件的工具。这些工具是:
locate – 通过名字来查找文件
find – 在目录层次结构中搜索文件
我们也将看一个经常与文件搜索命令一起使用的命令,它用来处理搜索到的文件列表:

xargs – 从标准输入生成和执行命令行
另外,我们将介绍两个命令来协助我们探索:

touch – 更改文件时间
stat – 显示文件或文件系统状态

locate - 查找文件的简单方法
这个 locate 程序快速搜索路径名数据库,并且输出每个与给定字符串相匹配的文件名,例如,我们想要找到所有名字以“zip”开头的程序,假定包含 匹配程序的目录以”bin/”结尾。

[me@linuxbox ~]$ locate bin/zip
locate 命令将会搜索它的路径名数据库,输出任一个包含字符串“bin/zip”的路径名:

/usr/bin/zip
/usr/bin/zipcloak
/usr/bin/zipgrep
/usr/bin/zipinfo
/usr/bin/zipnote
/usr/bin/zipsplit
locate 可以结合其它工具,比如说 grep 命令,来设计更加 有趣的搜索:

[me@linuxbox ~]$ locate zip | grep bin
/bin/bunzip2
/bin/bzip2
/bin/bzip2recover
/usr/bin/gpg-zip
/usr/bin/funzip
...

find - 查找文件的复杂方式
locate 程序只能依据文件名来查找文件,而 find 程序能基于各种各样的属性, 搜索一个给定目录(以及它的子目录),来查找文件.

find 命令的最简单使用是,搜索一个或多个目录。例如,输出我们的家目录列表。
[me@linuxbox ~]$ find ~
对于最活跃的用户帐号,这将产生一张很大的列表,让我们使用 wc 程序来计算出文件的数量:
[me@linuxbox ~]$ find ~ | wc -l
47068

比如说我们想要目录列表。我们可以添加以下测试条件:

[me@linuxbox ~]$ find ~ -type d | wc -l
这里添加测试条件-type d 限制了只搜索目录

表18-1: find 文件类型
| 文件类型 | 描述 |
|---------|------------|
| b | 块设备文件 |
| c | 字符设备文件 |
| d | 目录 |
| f | 普通文件 |
| l | 符号链接 |
我们也可以通过加入一些额外的测试条件,根据文件大小和文件名来搜索:让我们查找所有文件名匹配 通
配符模式“*.JPG”和文件大小大于1M 的文件:

[me@linuxbox ~]$ find ~ -type f -name "\*.JPG" -size +1M | wc -l
840

在这个例子里面,我们加入了 -name 测试条件,后面跟通配符模式。注意,我们把它用双引号引起来,从而阻止 shell 展开路径名。紧接着,我们加入 -size 测试条件,后跟字符串“+1M”。开头的加号表明我们正在寻找文件大小大于指定数的文件。若字符串以减号开头,则意味着查找小于指定数的文件。 若没有符号意味着“精确匹配这个数”。结尾字母“M”表明测量单位是兆字节。

find 命令支持大量不同的测试条件。下表是列出了一些常见的测试条件。请注意,在需要数值参数的 情况下,可以应用以上讨论的“+”和”-“符号表示法:
表18-3: find 测试条件
| 测试条件 | 描述 |
|---------|------------|
| -name pattern | 用指定的通配符模式匹配的文件和目录。 |
| -size n | 匹配的文件大小为 n。 |
| -type c | 匹配的文件类型是 c。 |
| -user name | 匹配的文件或目录属于某个用户。这个用户可以通过用户名或用户 ID 来表示。 |


操作符
幸运地是,find 命令提供了 一种方法来结合测试条
件,通过使用逻辑操作符来创建更复杂的逻辑关系。 为了表达上述的测试条件,我们可以这样做(括号转义,不要被自动展开):
[me@linuxbox ~]$ find ~ \( -type f -not -perm 0600 \) -or \( -type d -not -perm 0700 \)
| 操作符 | 描述 |
|---------|------------|
| -and | 如果操作符两边的测试条件都是真,则匹配。可以简写为 -a。 注意若没有使用操作符,则默认使
用 -and。 |
| -or | 若操作符两边的任一个测试条件为真,则匹配。可以简写为 -o。 |
| -not | 若操作符后面的测试条件是真,则匹配。可以简写为一个感叹号(!)。 |
| () | 把测试条件和操作符组合起来形成更大的表达式。这用来控制逻辑计算的优先级


预定义的操作
幸运地是,find 命令允许基于搜索结果来执行操作
表18-6: 几个预定义的 find 命令操作
| 操作 | 描述 |
|---------|------------|
| -delete | 删除当前匹配的文件。 |
| -ls | 对匹配的文件执行等同的 ls -dils 命令。并将结果发送到标准输出。 |
| -print | 把匹配文件的全路径名输送到标准输出。如果没有指定其它操作,这是 默认操作。 |
| -quit | 一旦找到一个匹配,退出。 |

find ~ -type f -name '*.BAK' -delete
在这个例子里面,用户家目录(和它的子目录)下搜索每个以.BAK 结尾的文件名。当找到后,就删除它们。
警告:当使用 -delete 操作时,不用说,你应该格外小心。首先测试一下命令, 用 -print 操作代替 -delete,来确认搜索结果。
记住,在每个测试和操作之间会默认应用 -and 逻辑运算符。 我们也可以这样表达这个命令,使逻辑关系更容易看出:

find ~ -type f -and -name '*.BAK' -and -print

因为测试和行为之间的逻辑关系决定了哪一个会被执行,我们知道测试和行为的顺序很重要

1
2
3
4
5
6
7
8
9
10
11
12
用户定义的行为
除了预定义的行为之外,我们也可以唤醒随意的命令。传统方式是通过 -exec 行为。这个 行为像这样工作:
-exec command {} ;
{}是当前路径名的符号表示,分号是要求的界定符 表明命令结束。这里是一个使用 -exec 行为的例子,其作用如之前讨论的 -delete 行为:

-exec rm '{}' ';'
重述一遍,因为花括号和分号对于 shell 有特殊含义,所以它们必须被引起来或被转义。
通过使用 -ok 行为来代替 -exec,在执行每个指定的命令之前, 起提示用户作用:

touch命令通常被用来设置或更新文件的访问,更改,和修改时间。然而,如果一个文件名参数是一个不存在的文件,则会创建一个空文件。
注意不同于 ls 命令,find 命令的输出结果是无序的。其顺序由存储设备的布局决定
stat,是一款加大马力的 ls 命令版本

第十九章:归档和备份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
它们就是文件压缩程序:

gzip – 压缩或者展开文件
bzip2 – 块排序文件压缩器,另外一种压缩算法,基本和gzip一样
归档程序:

tar – 磁带打包工具
zip – 打包和压缩文件
还有文件同步程序:

rsync – 同步远端文件和目录


gzip
这个 gzip 程序被用来压缩一个或多个文件。当执行 gzip 命令时,则原始文件的压缩版会替代原始文件。
相对应的 gunzip 程序被用来把压缩文件复原为没有被压缩的版本

[me@linuxbox ~]$ gzip foo.txt
[me@linuxbox ~]$ gunzip foo.txt.gz
接下来,我们运行 gzip 命令,它会把原始文件替换为一个叫做 foo.txt.gz 的压缩文件。接下来,我们运行 gunzip 程序来解压缩文件。随后,我们能见到压缩文件已经被原始文件替代了, 同样地保留了相同的权限和时间戳。
如果我们的目标只是为了浏览一下压缩文本文件的内容,我们可以这样做:

[me@linuxbox ~]$ gunzip -c foo.txt | less


归档文件
归档就是收集许多文件,并把它们 捆绑成一个大文件的过程。归档经常作为系统备份的一部分来使用。
我们经常看到扩展名为 .tar 或者 .tgz 的文件,它们各自表示“普通” 的 tar 包和被 gzip程序压缩过的 tar 包。一个 tar 包可以由一组独立的文件,一个或者多个目录,或者 两者混合体组成。命令语法如下:

tar mode[options] pathname... # 语法
表19-2: tar 模式
| 模式 | 说明 |
|---------|------------|
| c | 为文件和/或目录列表创建归档文件。 |
| x | 抽取归档文件,相当于归档后复制到另外一个位置。 |
| r | 追加具体的路径到归档文件的末尾。 |
| t | 列出归档文件的内容。 |


让我们创建整个操练场的 tar 包:

[me@linuxbox ~]$ tar cf playground.tar playground #f选项可能是force
列出归档文件的内容

[me@linuxbox ~]$ tar tf playground.tar
抽取 tar 包 playground 到一个新位置

[me@linuxbox ~]$ cd foo #进到目标目录
[me@linuxbox ~]$ tar xf ../playground.tar #xf为抽取后的文件
有一个警告,然而:除非你是超级用户,要不然从归档文件中抽取的文件和目录的所有权由执行此复原操作的用户所拥有,而不属于原始所有者。
tar 命令另一个有趣的行为是它处理归档文件路径名的方式。默认情况下,路径名是相对的,而不是绝对路径。

假定我们想要复制家目录及其内容到另一个系统中, 并且有一个大容量的 USB 硬盘,可以把它作为传输工具
[me@linuxbox ~]$ sudo tar cf /media/Big Disk/home.tar /home
[me@linuxbox2 /]$ sudo tar xf /media/Big Disk/home.tar
c参数为创建归档,x参数为提取归档内容,f应该是强制。第一个路径为归档后放置的路径,第二个路径是对什么内容进行归档。

GNU 版本的 tar 命令(在 Linux 发行版中最常出现)通过 --wildcards 选项来 支持通配符。这个例子使用了之前 playground.tar 文件:这个命令将只会抽取匹配特定路径名的文件,路径名中包含了通配符 dir-*

[me@linuxbox ~]$ cd foo
[me@linuxbox foo]$ tar xf ../playground2.tar --wildcards 'home/me/playground/dir-\*/file-A'

tar 命令经常结合 find 命令一起来制作归档文件。在这个例子里,我们将会使用 find 命令来 产生一个文件集合,然后这些文件被包含到归档文件中。

[me@linuxbox ~]$ find playground -name 'file-A' -exec tar rf playground.tar '{}' '+'
这里我们使用 find 命令来匹配 playground 目录中所有名为 file-A 的文件,然后使用-exec 行为,来 唤
醒带有追加模式(r)的 tar 命令,把匹配的文件添加到归档文件 playground.tar 里面。

zip
这个 zip 程序既是压缩工具,也是一个打包工具,Windows 用户比较熟悉。然而,在 Linux 中 gzip 是主要的压缩程序,而 bzip2则位居第二。例如,制作一个 playground 的 zip 版本的文件包,这样做:

[me@linuxbox ~]$ zip -r playground.zip playground
除非我们包含-r 选项,要不然只有 playground 目录(没有任何它的内容)被存储

使用 unzip 程序,来直接抽取一个 zip 文件的内容。

[me@linuxbox ~]$ cd foo
[me@linuxbox foo]$ unzip ../playground.zip
使用-l 选项,导致 unzip 命令只是列出文件包中的内容而没有抽取文件

对于 zip 命令(与 tar 命令相反)要注意一点,就是如果指定了一个已经存在的文件包,其被更新 而不是
被替代。这意味着会保留此文件包。

同步文件和目录
通过使用 rsync 远端更新协议,此协议 允许 rsync 快速地检测两个目录的差异,执行最小量的复制来达到目录间的同步。
rsync 被这样唤醒:

rsync options source destination
这里 source 和 destination 是下列选项之一:
一个本地文件或目录
一个远端文件或目录,以[user@]host:path 的形式存在
一个远端 rsync 服务器,由 rsync://[user@]host[:port]/path 指定
注意 source 和 destination 两者之一必须是本地文件。rsync 不支持远端到远端的复制。

下一步,我们将同步 playground 目录在 foo 目录中

[me@linuxbox ~]$ rsync -av playground foo
我们包括了-a 选项(递归和保护文件属性)和-v 选项(冗余输出)

第二十章:正则表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
grep    程序会在文本文件中查找一个指定的正则表达式,并把匹配行输出到标准输出。就像这样:

[me@linuxbox ~]$ ls /usr/bin | grep zip
这个命令会列出,位于目录 /usr/bin 中,文件名中包含子字符串“zip”的所有文件。
这个 grep 程序以这样的方式来接受选项和参数:

grep [options] regex [file...] #这里的 regx 是指一个正则表达式。

这是一个常用的 grep 选项列表:
表20-1: grep 选项
选项 描述
-i 忽略大小写。不会区分大小写字符。也可用--ignore-case 来指定。
-v 不匹配。通常,grep 程序会打印包含匹配项的文本行。这个选项导致 grep 程序 只会不包含匹配项的文本行
-c 打印匹配的数量(或者是不匹配的数目,若指定了-v 选项),而不是文本行本身。
-l 打印包含匹配项的文件名,而不是文本行本身
-L 相似于-l 选项,但是只是打印不包含匹配项的文件名
-h 应用于多文件搜索,只输出匹配项,不输出文件名(因为默认两个都会输出)


[me@linuxbox ~]$ grep bzip dirlist*.txt #搜索字符串bzip
dirlist-bin.txt:bzip2
dirlist-bin.txt:bzip2recover
[me@linuxbox ~]$ grep -l bzip dirlist*.txt # 只输出包含匹配项的文件名
dirlist-bin.txt


元字符和文本
表达式元字符由以下字符组成:^ $ . [ ] { } - ? * + ( ) | \ 然后其它所有字符都被认为是原义字符

圆点字符,其被用来匹配任意字符
[me@linuxbox ~]$ grep -h '.zip' dirlist*.txt
bunzip2
bzip2

插入符号和美元符号被看作是锚点,表示以**开头和以**结尾
[me@linuxbox ~]$ grep -h '^zip' dirlist*.txt
zip
zipcloak
[me@linuxbox ~]$ grep -h 'zip$' dirlist*.txt
gunzip
gzip

通过中括号表达式,我们能够指定 一个字符集合
[me@linuxbox ~]$ grep -h '[bg]zip' dirlist*.txt
bzip2recover
gzip

否定,如果在正则表示式中的第一个字符是一个插入字符,则剩余的字符被看作是不会在给定的字符位置出现的字符集合
[me@linuxbox ~]$ grep -h '[^bg]zip' dirlist*.txt #zip之前的字符一定不是b或者g
bunzip2
gunzip

字符区域
[me@linuxbox ~]$ grep -h '^[A-Z]' dirlist*.txt
MAKEDEV

扩展的正则表达式
正则表达式的实现分成了两类: 基本正则表达式(BRE)和扩展的正则表达式(ERE),因为我们将要讨论的下一个特性是 ERE 的一部分,由 egrep 程序来执行这项操作,但是 GUN 版本的 grep 程序也支持扩展的正则表达式,当使用了-E 选项
之后。

Alternation
我们将要讨论的扩展表达式的第一个特性叫做 alternation(交替),其是一款允许从一系列表达式 之间选择匹配项的实用程序。

[me@linuxbox ~]$ echo "AAA" | grep AAA #左边传过来的和右边匹配
AAA
[me@linuxbox ~]$ echo "BBB" | grep AAA
[me@linuxbox ~]$
一个相当直截了当的例子,我们把 echo 的输出管道给 grep,然后看到输出结果。当出现 一个匹配项时,我们看到它会打印出来;当没有匹配项时,我们看到没有输出结果。现在我们将添加 alternation,以竖杠线元字符为标记:

[me@linuxbox ~]$ echo "AAA" | grep -E 'AAA|BBB'
AAA
[me@linuxbox ~]$ echo "BBB" | grep -E 'AAA|BBB'
BBB
[me@linuxbox ~]$ echo "CCC" | grep -E 'AAA|BBB'
[me@linuxbox ~]$
这里我们看到正则表达式’AAA|BBB’,这意味着“匹配字符串 AAA 或者是字符串 BBB”。注意因为这是 一个扩展的特性,我们给 grep 命令(虽然我们能以 egrep 程序来代替)添加了-E 选项,并且我们 把这个正则表达式用单引号引起来,为的是阻止 shell 把竖杠线元字符解释为一个 pipe 操作符。

限定符
扩展的正则表达式支持几种方法,来指定一个元素被匹配的次数。
? - 匹配零个或一个元素
* - 匹配零个或多个元素
+ - 匹配一个或多个元素
{ } - 匹配特定个数的元素
表20-3: 指定匹配的数目
| 限定符 | 意思 |
|-------|-------|
| {n} | 匹配前面的元素,如果它确切地出现了 n 次。 |
| {n,m} | 匹配前面的元素,如果它至少出现了 n 次,但是不多于 m 次。 |
| {n,} | 匹配前面的元素,如果它出现了 n 次或多于 n 次。 |
| {,m} | 匹配前面的元素,如果它出现的次数不多于 m 次。 |

The Linux Command Line学习笔记(二)

发表于 2018-06-10 | 分类于 Linux
字数统计: 3.8k | 阅读时长 ≈ 14

第十一章:进程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
这一章将介绍以下命令:
这一章将介绍以下命令:
ps – 报告当前进程快照
top – 显示任务
jobs – 列出活跃的任务
bg – 把一个任务放到后台执行
fg – 把一个任务放到前台执行
kill – 给一个进程发送信号
killall – 杀死指定名字的进程
shutdown – 关机或重启系统

系统分配给每个进程一个数字,这个数字叫做进程ID 或 PID。PID 号按升序分配,init 进程的 PID 总是1。
查看进程 ps。ps 程序有许多选项,它最简单地使用形式是这样的:

[me@linuxbox ~]$ ps
PID TTY TIME CMD
5198 pts/1 00:00:00 bash
10129 pts/1 00:00:00 ps
TTY 是“Teletype” 的简写,是指进程的控制终端,TIME 字段表示 进程所消耗的
CPU 时间数量,CMD代表运行的命令

[me@linuxbox ~]$ ps x # 可以得到更多关于系统运行状态的信息:
输出结果中,新添加了一栏,标题为 STAT 。STAT 是 “state” 的简写,它揭示了进程当前状态:
表11-1: 进程状态
| 状态 | 意义 |
| --- | --- |
| R | 运行。这意味着,进程正在运行或准备运行。 |
| S | 正在睡眠。 进程没有运行,而是,正在等待一个事件, 比如说,一个按键或者网络数据包。 |
| D | 不可中断睡眠。进程正在等待 I/O,比方说,一个磁盘驱动器的 I/O。 |
| T | 已停止. 已经指示进程停止运行。稍后介绍更多。 |
| Z | 一个死进程或“僵尸”进程
| < | 一个高优先级进程
| N | 低优先级进程。

另一个流行的选项组合是 “aux”(不带开头的”-“字符)。这会给我们更多信息:
[me@linuxbox ~]$ ps aux

用 top 命令动态查看进程
[me@linuxbox ~]$ top
top 显示结果由两部分组成: 最上面是系统概要,下面是进程列表,以 CPU 的使用率排序。输入q退出top程序。

把一个进程放置到后台(执行)
启动一个程序,让它立即在后台 运行,我们在程序命令之后,加上”&”字符:

[me@linuxbox ~]$ xlogo & #xlogo打开一个试验窗口
[1] 28236 # 工作号为1
[me@linuxbox ~]$
如果我们运行 ps 命令,可以看到我们的进程:

[me@linuxbox ~]$ ps
PID TTY TIME CMD
10603 pts/1 00:00:00 bash
28236 pts/1 00:00:00 xlogo
28239 pts/1 00:00:00 ps
执行 jobs 命令,我们可以看到这个输出列表:
[me@linuxbox ~]$ jobs
[1]+ Running xlogo &

进程返回到前台
一个在后台运行的进程对一切来自键盘的输入都免疫,也不能用 Ctrl-c 来中断它。使用 fg 命令,让一个
进程返回前台执行:

[me@linuxbox ~]$ jobs
[1]+ Running xlogo &
[me@linuxbox ~]$ fg %1
xlogo
fg 命令之后,跟随着一个百分号和工作序号,输入 Ctrl-c 来终止 xlogo 程序

停止一个进程,输入 Ctrl-z,可以停止一个前台进程。
使用 fg 命令,可以恢复程序到前台运行,或者用 bg 命令把程序移到后台。

Signals
kill 命令被用来“杀死”程序。这样我们就可以终止需要杀死的程序。指定我们想要终止的进程 PID。也可以用 jobspec(例如,“%1”)来代替 PID。

[me@linuxbox ~]$ xlogo &
[1] 28401
[me@linuxbox ~]$ kill 28401
[1]+ Terminated xlogo
这个 kill 命令不是确切地“杀死”程序,而是给程序 发送信号。当程序 接到信号之后,则做出响应。
通过 kill 命令给进程发送信号,kill 命令被用来给程序发送信号。它最常见的语法形式看起来像这样(默认TERM终止信号):
kill [-signal] PID...
表 11-4: 常用信号
| 编号 | 名字 | 含义 |
| 1 | HUP | 挂起,这意味着,当发送这个信号到一个守护进程后, 这个进程会重新启动,并且重新读取它的配置文件
| 2 | INT | 中断。实现和 Ctrl-c 一样的功能,由终端发送。通常,它会终止一个程序。 |
| 15 | TERM | 终止。这是 kill 命令发送的默认信号
| 18 | CONT | 继续。在停止一段时间后,进程恢复运行。 |
| 19 | STOP | 停止。这个信号导致进程停止运行,而没有终止
例如:
[me@linuxbox ~]$ kill -1 13546 #挂起 工作号为13546的进程

第十二章:shell环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
在这一章,我们将用到以下命令:
printenv - 打印部分或所有的环境变量
set - 设置 shell 选项
export — 导出环境变量,让随后执行的程序知道。
alias - 创建命令别名

shell 在环境中存储了两种基本类型的数据,虽然对于 bash 来说,很大程度上这些类型是不可 辨别的。
它们是环境变量和 shell 变量。Shell 变量是由 bash 存放的少量数据,而剩下的基本上 都是环境变量

[me@linuxbox ~]$ printenv | less #显示环境变量

非登录 shell 会话也会继承它们父进程的环境设置,通常是一个登录 shell

曾经是否感到迷惑 shell 是怎样知道到哪里找到我们在命令行中输入的命令的?例如,当我们输入 ls 后,
shell 不会查找整个计算机系统,来找到 /bin/ls(ls 命令的绝对路径名),而是,它查找一个目录列表,
这些目录包含在 PATH 变量中。

通过添加字符串 $HOME/bin 到 PATH 变量值的末尾,则目录 $HOME/bin 就添加到了命令搜索目录列表中。这意味着当我们想要在自己的家目录下,创建一个目录来存储我们自己的私人程序时,shell 已经给我们准备好了。我们所要做的事就是 把创建的目录叫做 bin

按照通常的规则,添加目录到你的 PATH 变量或者是定义额外的环境变量,要把这些更改放置到
.bash_profile 文件中. 对于其它的更改,要放到 .bashrc 文件中。(修改通过vi编辑器)

移动 鼠标到文件的最后一行,然后添加以下几行到文件 .bashrc 中:

umask 0002
export HISTCONTROL=ignoredups
export HISTSIZE=1000
alias l.='ls -d .* --color=auto'
alias ll='ls -l --color=auto'

| 文本行 | 含义 |
|-------|---------|
| umask 0002 | 设置掩码来解决共享目录的问题。 |
| export HISTCONTROL=ignoredups | 使得 shell 的历史记录功能忽略一个命令,如果相同的命令已被
记录。 |
| export HISTSIZE=1000 | 增加命令历史的大小,从默认的 500 行扩大到 1000 行。 |
| alias l.='ls -d .* --color=auto' | 创建一个新命令,叫做'l.',这个命令会显示所有以点开头的目录项。 |
| alias ll='ls -l --color=auto' | 创建一个叫做'll'的命令,这个命令会显示长格式目录列表。 |

激活我们的修改
我们对于文件 .bashrc 的修改不会生效,直到我们关闭终端会话,再重新启动一个新的会话, 因为
.bashrc 文件只是在刚开始启动终端会话时读取。然而,我们可以强迫 bash 重新读取修改过的 .bashrc
文件,使用下面的命令:
[me@linuxbox ~]$ source .bashrc

第十三章:VI简介

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
[me@linuxbox    ~]$    vi    #启动vi:q!                          #加感叹号是强制退出
小贴示:如果你在 vi 中“迷失”了,试着按下 Esc 键两次来找到路(回到普通模式)。

编辑模式
再次启动 vi,这次传递给 vi 一个不存在的文件名。这也是用 vi 创建新文件的方法。
[me@linuxbox ~]$ vi foo.txt
当 vi 启动后,进入 的是命令模式。这种模式下,几乎每个按键都是一个命令,所以如果我们打算输入字符,vi 会发疯,弄得一团糟。
插入模式
为了在文件中添加文本,首先我们必须进入插入模式。按下”i”按键进入插入模式,按下 Esc 按键,返回命令模式。

-- INSERT --

保存我们的工作
在命令模式下输入一个 ex 命令(ex操作为命令模式下的操作)。 通过按下”:”键。为了写入我们修改的文件,我们在冒号之后输入”w”字符,然后按下回车键

:w

移动光标
当在 vi 命令模式(不是插入模式)下时,vi 提供了大量的移动命令
| 0 (零按键) | 移动到当前行的行首。 |
| $ | 移动到当前行的末尾。 |
| Ctrl-f or Page Down | 向下翻一页 |
| Ctrl-b or Page Up | 向上翻一页 |
| G | 移动到文件末尾。 |

基本编辑
如果我们按下“u” 按键,当在命令模式下,vi 将会撤销你所做的最后一次修改
如果我们想要在这个句子的末尾添加一些文本,输入”a”,在光标处,vi 进入插入模式。”A”命令,在行尾进入插入模式。另外还有:
| o | 当前行的下方打开一行。 |,进入插入模式
| O | 当前行的上方打开一行。 | ,进入插入模式
记住按下 Esc 按键来退出插入模式。

删除文本
表13-3: 文本删除命令
| 命令 | 删除的文本 |
|-------|---------|
| x | 当前字符 |
| 3x | 当前字符及其后的两个字符。 |
| dd | 当前行。 |
| 5dd | 当前行及随后的四行文本。 |
| d$ | 从光标位置开始到当前行的行尾。 |
| d0 | 从光标位置开始到当前行的行首。 |

剪切,复制和粘贴文本
这个 d 命令不仅删除文本,它还“剪切”文本。每次我们使用 d 命令,删除的部分被复制到一个 粘贴缓
冲区中(看作剪切板)。过后我们执行小 p 命令把剪切板中的文本粘贴到光标位置之后, 或者是大 P 命
令把文本粘贴到光标之前。

y 命令用来“拉”(复制)文本。p命令粘贴。
表13-4: 复制命令
| 命令 | 复制的内容 |
|-------|---------|
| yy | 当前行。 |
| 5yy | 当前行及随后的四行文本。 |
| y$ | 从当前光标位置到当前行的末尾。 |
| y0 | 从当前光标位置到行首。 |

查找和替换
查找一行: f 命令查找一行,移动光标到下一个所指定的字符上。例如,命令 fa 会把光标定位到同一行中 下一个出现的”a”字符上。在一行中执行了字符的查找命令之后,通过输入分号来重复这个查找。

查找整个文件:移动光标到下一个出现的单词或短语上,使用 / 命令。下一步,输入要查找的单词或短语后, 按下回车。光标就会移动到下一个包含所查找字符串的位置。通过 n 命令来重复先前的查找

全局查找和替代vi 使用 ex 命令来执行查找和替代操作(vi 中叫做“替换”)。把整个文件中的单词“Line”更改为“line”, 我们输入以下命令:

1
:%s/Line/line/g

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
需要用户确认的替换命令。通过添加一个”c”字符到这个命令的末尾,来完成    这个替换命令。例如:

:%s/line/Line/gc
这个命令会把我们的文件恢复先前的模样;然而,在执行每个替换命令之前,vi 会停下来, 通过下面的信
息,来要求我们确认这个替换:

replace with Line (y/n/a/q/l/^E/^Y)?
表13-5: 替换确认按键
| 按键 | 行为 |
|-------|---------|
| y | 执行替换操作 |
| n | 跳过这个匹配的实例 |
| a | 对这个及随后所有匹配的字符串执行替换操作。 |
| q or esc | 退出替换操作。 |
| l | 执行这次替换并退出。l 是 “last” 的简写。 |
| Ctrl-e, Ctrl-y | 分别是向下滚动和向上滚动。用于查看建议替换的上下文。 |

文件之间转换
从这个文件转到下一个文件,使用这个 ex 命令:

:n
回到先前的文件使用:

:N
当我们从一个文件移到另一个文件时,如果当前文件没有保存修改,vi 会阻止我们转换文件, 这是 vi 强制执行的政策。在命令之后添加感叹号,可以强迫 vi 放弃修改而转换文件。
我们可以查看正在编辑的文件列表,使用:buffers 命令
:buffers

1 # "foo.txt" line 1
2 %a "ls-output.txt" line 0
Press ENTER or type command to continue
注意:你不同通过:n 或:N 命令在由:e 命令加载的文件之间进行切换。这时要使用:buffer 命令, 其后加上缓冲区号码,来转换文件。

从一个文件复制内容到另一个文件
以打开的两个文件为例,首先转换到缓冲区1(foo.txt) ,输入:

:buffer 1

我们应该得到以下输出:

The quick brown fox jumped over the lazy dog. It was cool.
Line 2
Line 3
Line 4
Line 5
下一步,把光标移到第一行,并且输入 yy 来复制这一行。转换到第二个缓冲区,输入:

:buffer 2
现在屏幕会包含一些文件列表(这里只列出了一部分):

total 343700
-rwxr-xr-x 1 root root 31316 2007-12-05 08:58 [
....
移动光标到第一行,输入 p 命令把我们从前面文件中复制的一行粘贴到这个文件中:

total 343700
The quick brown fox jumped over the lazy dog. It was cool.
-rwxr-xr-x 1 root root 31316 2007-12-05 08:58 [
....

保存工作
像 vi 中的其它操作一样,有几种不同的方法来保存我们所修改的文件。我们已经研究了:w 这个 ex 命令,但还有几种方法,可能我们也觉得有帮助。
在命令模式下,输入 ZZ 就会保存并退出当前文件

第十四章:自定制shell提示符

并不是每个人都会花心思来更改提示符,因为通常默认的提示符就很让人满意

第十五章:软件包管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
软件包管理是指系统中一种安装和维护软件的方法,inux    系统中几乎所有的软件都可以在互联网上找到。其中大多数软件由发行商以    包文件的形式提供,剩下的则以源码形式存在,可以手动安装。在后面章节里,我们将会谈谈怎样    通过编译源码来安装软件。

查找资源库中的软件包
| Red Hat | yum search search_string |
例如:yum search emacs

从资源库中安装一个软件包
| Red Hat | yum install package_name |

通过软件包文件来安装软件
如果从某处而不是从资源库中下载了一个软件包文件,可以使用底层工具来直接(没有经过依赖解析)安装它。
| Red Hat | rpm -i package_file |

卸载软件
| Red Hat | yum erase package_name |

经过资源库来更新软件包
| Red Hat | yum update |

经过软件包文件来升级软件
如果已经从一个非资源库网站下载了一个软件包的最新版本,可以安装这个版本,用它来 替代先前的版本:
| Red Hat | rpm -U package_file |

列出所安装的软件包
| Red Hat | rpm -qa |

确定是否安装了一个软件包
| Red Hat | rpm -q package_name |

查找安装了某个文件的软件包
| Red Hat | rpm -qf file_name |

The Linux Command Line学习笔记(一)

发表于 2018-06-07 | 分类于 Linux
字数统计: 2.9k | 阅读时长 ≈ 11

记录读《The Linux Command Line》的学习笔记,因为之前是记在印象笔记里面的,格式有点对不上,这里内容就全部粘贴到代码框里面。

另外一篇此书的学习笔记博客


文件查看

1
`pwd  —  打印出当前工作目录名`
1
`ls    —    列出目录内容ls -l  # 长模式ls -l  # 修改时间排序ls -a # 显示所用文件,默认不显示隐藏文件ls -S  # 按大小排序me@linuxbox    ~]$    ls    /usr   # 除了当前工作目录以外,也可以指定要列出内容的目录[me@linuxbox    ~]$    ls    ~    /usr #列出多个指定目录的内容 ,家目录(用字符“~”代表)`
1
2
3
4
5
cd    —    更改目录
符号 “.” 指的是工作目录(找下一层使用,一般可省略使用),”..” 指的是工作目录的父目录(找上一层使用)
| cd | 更改工作目录到你的家目录。 |
| cd - | 更改工作目录到先前的工作目录。 |
| cd ~user_name | 更改工作目录到用户家目录。例如, cd ~bob 会更改工作目录到用户“bob”的家目
1
2
3
4
linux一切皆文件,不依赖后缀名判断
使用 file 打印文件描述
me@linuxbox ~]$ file picture.jpg
picture.jpg: JPEG image data, JFIF standard 1.01
1
2
3
4
5
6
less浏览文本文件内容,以下是常用命令
| G | 移动到最后一行 |
| 1G or g | 移动到开头一行 |
| /charaters | 向前查找指定的字符串 |
| q | 退出 less 程序 |
| h | 显示帮助屏幕 |

文件操作

1
2
3
4
5
6
接受文件名作为参数的任何命令,都可以使用通配符,要记得一些简单的规则(类似正则)
cp item1 item2 — 复制文件和目录
mv item1 item2— 移动/重命名文件和目录
mkdir dir1 — 创建目录
rm item1 — 删除文件和目录(一旦删除不可恢复,慎重使用)
ln — 创建硬链接和符号链接
1
2
3
4
5
6
7
8
9
10
这里列举了    cp    命令一些有用的选项(短选项和等效的长选项):其他操作也类同
| -a, --archive | 复制文件和目录,以及它们的属性,包括所有权和权限。
| -i, --interactive | 在重写已存在文件之前,提示用户确认
| -r, --recursive | 递归地复制目录及目录中的内容。当复制目录时, 需要这个选项(或者-a 选项)。 |
| -u, --update | 当把文件从一个目录复制到另一个目录时,仅复制 目标目录中不存在的文件,或者是文
件内容新于目标目录中已经存在的文件。 |
| -v, --verbose | 显示翔实的命令操作信息 |

rm命令参数选项:
| -f, --force | 忽视不存在的文件,不显示提示信息。这选项颠覆了“--interactive”选项。 |
1
`ln    命令即可创建硬链接,也可以创建符号链接。`
1
`重命名:[me@linuxbox    playground]$    mv    oldname  newname`

使用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
type    –    说明怎样解释一个命令名
which – 显示会执行哪个可执行程序
man – 显示命令手册页 ,完整正式又复杂
apropos – 显示一系列适合的命令,基于某个关键字的匹配项搜索
info – 显示命令 info
whatis – 显示一个命令的简洁描述
alias – 创建命令别名

到底什么是命令?命令可以是下面四种形式之一:
1. 是一个可执行程序,比如说shell,perl,python,ruby,等等。
2. 是一个内建于 shell 自身的命令。cd 命令,就是一个 shell 内部命令。
3. 是一个 shell 函数。
4. 是一个命令别名。


[me@linuxbox ~]$ type ls
ls is aliased to `ls --color=tty`
[me@linuxbox ~]$ which ls
/bin/ls
[me@linuxbox ~]$ help cd
cd: cd [-L|-P] [dir] # 方括号,表示可选的项目。一个竖杠字符 表示互斥选项
[me@linuxbox ~]$ mkdir --help # 显示命令所支持的语法和选项说明
Usage: mkdir [OPTION] DIRECTORY...
[me@linuxbox ~]$ man ls # 手册页的组织形式| 章节 | 内容 |,可以指定章号
[me@linuxbox ~]$ apropos floppy #以”floppy”为关键词来搜索参考手册的
create_floppy_devices (8) - udev callout to create all possible #输出结果每行的第一个字段是手册页的名字,第二个字段展示章节。注意,man 命令加上”-k”选项,和 apropos 完成一样的功能。
许多手册页都很难阅读,whatis 程序显示匹配特定关键字的手册页的名字和一行命令说明。
info - GNU 项目提供了一个命令程序手册页的替代物,称为”info”,输入”info”,接着输入程序名称,启动 info。下表中的命令,当显示一个 info 页面时, 用来控制阅读器。
表 6-2: info 命令
| 命令 | 行为 |
| --- | --- |
| ? | 显示命令帮助 |
| Pg Up or Backspace | 显示上一页 |
| Pg Dn or Space | 显示下一页 |
| n | 下一个 - 显示下一个结点 |
| p | 上一个 - 显示上一个结点 |
| u | Up - 显示当前所显示结点的父结点,通常是个菜单 |
| Enter | 激活光标位置下的超级链接 |
| q | 退出 |

[me@linuxbox ~]$ type test # 检测新命名是否与系统重名
test is a shell builtin #重名,需要换一个
[me@linuxbox ~]$ type foo # 检测
bash: type: foo: not found # 不重名,可以使用这个新命名
[me@linuxbox ~]$ alias foo='cd /usr; ls; cd -' #通过 alia 命令 把这一串命令转变为一个命令
注意命令结构:alias name='string'。在命令”alias”之后,输入“name”,紧接着(没有空格)是一个等号,等号之后是 一串用引号引起的字符串,字符串的内容要赋值给 name
删除别名,使用 unalias 命令,像这样:
[me@linuxbox ~]$ unalias foo
[me@linuxbox ~]$ type foo
bash: type: foo: not found

IO重定向

1
`重定向命令的输入输出,命令的输入来自文件,而输出也存到文件。    也可以把多个命令连接起来组成一个强大的命令管道。为了炫耀这个工具,我们将叙述    以下命令:`

第八章:从shell眼中看世界

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
echo    是一个    shell    内部命令,来完成非常简单的任务。    它在标准输出中打印出它的文本参数。

[me@linuxbox ~]$ echo this is a test
this is a test
[me@linuxbox ~]$ echo *
Desktop Documents ls-output.txt Music Pictures Public Templates Videos

为什么 echo 不打印“*”呢,最简单的答案就是在 echo 命令被执行前, shell 把“*”展开成了 另外的东西(在这种情况下,就是在当前工作目录下的文件名字)
类似还有

[me@linuxbox ~]$ echo this is a test
this is a test
[me@linuxbox ~]$ echo The total is $100.00
The total is 00.00 # $1的值替换为一个空字符串,因为 1 是没有定义的变量



路径名展开
这种通配符(即*)工作机制叫做路径名展开(当前工作目录下的文件名字)

[me@linuxbox ~]$ echo D*
Desktop Documents #列出当前工作目录下的D开头的文件名字

波浪线展开
波浪线字符(“~”)有特殊的意思。当它用在 一个单词的开头时,它会展开成指定用户的家目录名,如果没有指定用户名,则是当前用户的家目录:

[me@linuxbox ~]$ echo ~
/home/me

算术表达式展开
shell 允许算术表达式通过展开来执行。这允许我们把 shell 提示当作计算器来使用:

[me@linuxbox ~]$ echo $((2 + 2))
4
| / | 除(取整数除法,所以结果是整数。) |
| % | 取余,只是简单的意味着,“余数” |
| ** | 取幂 |

[me@linuxbox ~]$ echo Five divided by two equals $((5/2))
Five divided by two equals 2
[me@linuxbox ~]$ echo with $((5%2)) left over.
with 1 left over.

花括号展开
可能最奇怪的展开是花括号展开。通过它,你可以从一个包含花括号的模式中 创建多个文本字符串。例子:

[me@linuxbox ~]$ echo Front-{A,B,C}-Back
Front-A-Back Front-B-Back Front-C-Back
[me@linuxbox ~]$ echo {Z..A} #可以一系列整数和字符
Z Y X W V U T S R Q P O N M L K J I H G F E D C B A
[me@linuxbox ~]$ echo a{A{1,2},B{3,4}}b #嵌套
a A1b a A2b a B3b a B4b

命令替换
命令替换允许我们把一个命令的输出作为一个展开模式来使用:

me@linuxbox ~]$ echo $(ls)
Desktop Documents ls-output.txt Music Pictures Public Templates Videos
[me@linuxbox ~]$ ls -l $(which cp) #把 which cp的执行结果作为一个参传递给ls命令
-rwxr-xr-x 1 root root 71516 2007-12-05 08:58 /bin/cp


双引号、单引号
双意味着单词分割,路径名展开, 波浪线展开,和花括号展开都被禁止,然而参数展开,算术展开,和命令替换 仍然执行。
单引号所有都被禁止。
[me@linuxbox ~]$ echo text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER
text /home/me/ls-output.txt a b foo 4 me
[me@linuxbox ~]$ echo "text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER"
text ~/*.txt {a,b} foo 4 me
[me@linuxbox ~]$ echo 'text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER'
text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER


转义字符'\'
| \a | 响铃(”警告”-导致计算机嘟嘟响) |
| \b | 退格符 |
| \n | 新的一行。在类 Unix 系统中,产生换行。 |
| \r | 回车符 |
| \t | 制表符 |

第九章:键盘高级操作技巧

1
`clear    -    清空屏幕`

第十章:权限

1
`在这一章中,我们将看看这一系统安全的本质部分,会介绍以下命令:`

chmod - 更改文件模式chmod

命令支持两种不同的方法来改变文件模式:1、八进制数字表示法,或 2、 符号表示法。这两种方法都有人用。首先我们讨论一下八进制数字表示法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
通过使用3个八进制数字,我们能够设置文件所有者,用户组,和其他人的权限:
[me@linuxbox ~]$ chmod 600 foo.txt
通过传递参数 “600”,我们能够设置文件所有者的权限为读写权限,而删除用户组和其他人的所有 权限

chmod 命令还支持另一种符号表示法,通过字符 “u”,“g”,“o”,和 “a” 的组合来指定 要影响的对象。如果没有指定字符,则假定使用”all”
表10-4: chmod 命令符号表示法
|||
|-----|-----------|
| u | "user"的简写,意思是文件或目录的所有者。 |
| g | 用户组。 |
| o | "others"的简写,意思是其他所有的人。 |
| a | "all"的简写,是"u", "g"和“o”三者的联合。 |
执行的操作可能是一个“+”字符,表示加上一个权限, 一个“-”,表示删掉一个权限,或者是一个“=”,表示只有指定的权限可用,其它所有的权限被删除。
表10-5: chmod 符号表示法实例
|||
|-----|-----------|
| u+x | 为文件所有者添加可执行权限。 |
| u-x | 删除文件所有者的可执行权限。 |
| +x | 为文件所有者,用户组,和其他所有人添加可执行权限。 等价于 a+x。 |
| o-rw | 除了文件所有者和用户组,删除其他人的读权限和写权限。 |
| go=rw | 给群组的主人和任意文件拥有者的人读写权限。如果群组的主人或全局之前已经有了执行的权限,他们将被移除。
1
2
3
4
5
6
7
umask    -    设置默认权限

[me@linuxbox ~]$ rm -f foo.txt
[me@linuxbox ~]$ umask
0002
运行不带参数的 umask 命令, 看一下当前的掩码值。响应的数值是0002(0022是另一个常用值),这个数值是掩码的八进制表示形式,
以下是掩码0002的八进制表达形式

1
2
3
4
5
6
这次自己设置掩码

[me@linuxbox ~]$ rm foo.txt
[me@linuxbox ~]$ umask 0000
大多数情况下,你不必修改掩码值,系统提供的默认掩码值就很好了

1
`更改身份`
<i class="fa fa-angle-left"></i>1…456<i class="fa fa-angle-right"></i>

60 日志
10 分类
73 标签
RSS
GitHub E-Mail
Recommended reading
  • Wepillo
  • Wulc
  • Pinard
  • Donche
  • XFT
  • Seawaylee
© 2018 — 2024 Mosbyllc