一些面试取经后自己梳理的知识点总结
1. Linux关键命令解释
which
which 你的命令
作用:
在PATH变量指定的路径中,搜索某个系统命令的位置,并且返回第一个搜索结果。
也就是说,使用which命令,就可以看到某个系统命令是否存在,以及执行的到底是哪一个位置的命令。
参数解释:
-n 指定文件名长度,指定的长度必须大于或等于所有文件中最长的文件名。
-p 与-n参数相同,但此处的包括了文件的路径。
-w 指定输出时栏位的宽度。
-V 显示版本信息
例子:
#which mysql
/usr/local/bin/mysql
scp
格式:
scp [可选参数] file_source file_target
- -r: 递归复制整个目录。
- -v:详细方式显示输出。scp和ssh(1)会显示出整个过程的调试信息。这些信息用于调试连接,验证和配置问题。
- -c cipher: 以cipher将数据传输进行加密,这个选项将直接传递给ssh。
- -F ssh_config: 指定一个替代的ssh配置文件,此参数直接传递给ssh。
- -i identity_file: 从指定文件中读取传输时使用的密钥文件,此参数直接传递给ssh。
- -l limit: 限定用户所能使用的带宽,以Kbit/s为单位。
- -o ssh_option: 如果习惯于使用ssh_config(5)中的参数传递方式,
- -P port:注意是大写的P, port是指定数据传输用到的端口号
作用:
scp命令用于Linux之间复制文件和目录。
scp是 secure copy的缩写, scp是linux系统下基于ssh登陆进行安全的远程文件拷贝命令。
例子:
从本地复制到远程:
1. scp local_file remote_username@remote_ip:remote_folder
2. scp local_file remote_username@remote_ip:remote_file
3. scp local_file remote_ip:remote_folder
4. scp local_file remote_ip:remote_file
- 第1,2个指定了用户名,命令执行后需要再输入密码,第1个仅指定了远程的目录,文件名字不变,第2个指定了文件名;
- 第3,4个没有指定用户名,命令执行后需要输入用户名和密码,第3个仅指定了远程的目录,文件名字不变,第4个指定了文件名;
mv
格式:
命令格式 | 运行结果 |
---|---|
mv 文件名 文件名 | 将源文件名改为目标文件名 |
mv 文件名 目录名 | 将文件移动到目标目录 |
mv 目录名 目录名 | 目标目录已存在,将源目录移动到目标目录;目标目录不存在则改名 |
mv 目录名 文件名 | 出错 |
作用:
用来为文件或目录改名、或将文件或目录移入其它位置。
例子:
将文件 aaa 更名为 bbb :
mv aaa bbb
将info目录放入logs目录中。注意,如果logs目录不存在,则该命令将info改名为logs。
mv info/ logs
再如将/usr/student下的所有文件和目录移到当前目录下,命令行为:
$ mv /usr/student/* .
rm
格式:
rm [options] name…
- -i 删除前逐一询问确认。
- -f 即使原档案属性设为唯读,亦直接删除,无需逐一确认。
- -r 将目录及以下之档案亦逐一删除。
作用:
用于删除一个文件或者目录。
例子:
删除文件可以直接使用rm命令,若删除目录则必须配合选项”-r”,例如:
# rm test.txt
rm:是否删除 一般文件 "test.txt"? y
# rm homework
rm: 无法删除目录"homework": 是一个目录
# rm -r homework
rm:是否删除 目录 "homework"? y
删除当前目录下的所有文件及目录,命令行为:
rm -r *
删除当前目录下的所有文件及目录,并且是直接删除,无需逐一确认命令行为:(删库跑路大法)
rm -rf *
kill
格式:
kill [-s <信息名称或编号>][程序] 或 kill [-l <信息编号>]
- -l <信息编号> 若不加<信息编号>选项,则-l参数会列出全部的信息名称。
- -s <信息名称或编号> 指定要送出的信息。
- [程序] [程序]可以是程序的PID或是PGID,也可以是工作编号。
作用:
kill命令用于删除执行中的程序或工作。
kill可将指定的信息送至程序。预设的信息为SIGTERM(15),可将指定程序终止。若仍无法终止该程序,可使用SIGKILL(9)信息尝试强制删除程序。程序或工作的编号可利用ps指令或jobs指令查看。
例子:
杀死进程
# kill 12345
强制杀死进程
# kill -KILL 123456
彻底杀死进程
# kill -9 123456
tail
tail [ -f ] [ -c Number | -n Number | -m Number | -b Number | -k Number ] [ File ]
作用:
命令默认在屏幕上显示指定文件的末尾10行。
依照要求将指定的文件的最后部分输出到标准设备,通常是终端,通俗讲来,就是把某个档案文件的最后几行显示到终端上,假设该档案有更新,tail会自己主动刷新,确保你看到最新的档案内容。
参数解释:
-f 该参数用于监视File文件增长。
-c Number 从 Number 字节位置读取指定文件。
-n Number 从 Number 行位置读取指定文件。
-m Number 从 Number 多字节字符位置读取指定文件,比方你的文件假设包括中文字,假设指定-c参数,可能导致截断,但使用-m则会避免该问题。
-b Number 从 Number 表示的512字节块位置读取指定文件。
-k Number 从 Number 表示的1KB块位置读取指定文件。
File 指定操作的目标文件名称 上述命令中,都涉及到number,假设不指定,默认显示10行。
Number前面可使用正负号,表示该偏移从顶部还是从尾部開始计算。
tail可运行文件一般在/usr/bin/以下。
例子:
tail -f filename 说明:监视filename文件的尾部内容(默认10行,相当于增加参数 -n 10),刷新显示在屏幕上。退出,按下CTRL+C。
tail -n 20 filename 说明:显示filename最后20行。
tail -r -n 10 filename 说明:逆序显示filename最后10行。
tail -f test.log 说明:循环读取test.log的内容,只要test.log有新内容写入,将立即读取到标准输出
cat
cat [op] file
说明:
op 为命令参数,常用的有-b、-n、-s
- -b:对非空输出行编号
- -n:对输出的所有行编号,由1开始对所有输出的行数编号
- -s:有连续两行以上的空白行,就代换为一行的空白行
作用:
(显示文件内容)一次显示整个文件:cat filename
(创建文件)从键盘创建一个文件:cat > filename 只能创建新文件,不能编辑已有文件.
(合并文件)将几个文件合并为一个文件:cat file1 file2 > file
chown
- -c或——changes:效果类似“-v”参数,但仅回报更改的部分;
- -f或–quite或——silent:不显示错误信息;
- -h或–no-dereference:只对符号连接的文件作修改,而不更改其他任何相关文件;
- -R或——recursive:递归处理,将指定目录下的所有文件及子目录一并处理;
- -v或——version:显示指令执行过程;
作用:
只有文件主和超级用户才可以便用该命令。
改变某个文件或目录的所有者和所属的组,该命令可以向某个用户授权,使该用户变成指定文件的所有者或者改变文件所属的组。用户可以是用户或者是用户D,用户组可以是组名或组id。文件名可以使由空格分开的文件列表,在文件名中可以包含通配符。
例子:
将目录/usr/meng及其下面的所有文件、子目录的文件主改成 liu:
chown -R liu /usr/meng
chmod
chmod [option] [mode] [file]
作用:
用来改变文件或者目录的权限
- -r 可读、
- -w 可写、
- -x 可执行。
- +表示增加权限、
- -表示取消权限
- =表示唯一设定权限
- u 表示该文件的拥有者、
- g 表示与该文件的拥有者属于同一个群体(group)者、
- o 表示其他以外的人、
- a 表示这三者都是。
例子:
chmod who [mode] 文件名
who:u,g,o,a
mode:r,w,x的组合,可以是数字表示(其中r=4,w=2,x=1)
chmod u+x file //给file的属主增加可执行权限
chmod 777 file //给file所有权限
==tar==
tar [主选项+辅选项] 文件或目录
使用该命令时,主选项必须有,它告诉tar要做什么事情,辅选项是辅助使用的,可以选用。
主选项:【一条命令以下5个参数只能有一个】
- -c: –create 新建一个压缩文档,即打包
- -x: –extract,–get解压文件
- -t: –list,查看压缩文档里的所有内容
- -r:–append 向压缩文档里追加文件
- -u:–update 更新原压缩包中的文件 辅助选项:
- -z:是否同时具有gzip的属性?即是否需要用gzip压缩或解压?一般格式为xxx.tar.gz或xx.tgz
- -j:是否同时具有bzip2的属性?即是否需要用bzip2压缩或解压?一般格式为xx.tar.bz2
- -v:显示操作过程!这个参数很常用
- -f:使用文档名,注意,在f之后要立即接文档名,不要再加其他参数!
- -C:切换到指定目录
- –exclude FILE:在压缩过程中,不要将FILE打包
作用:
Linux中很多压缩程序只能针对一个文件进行压缩,这样当你想要压缩一大堆文件时,你得先将这一大堆文件先打成一个包(tar命令),然后再用压缩程序进行压缩(gzip bzip2命令)。
linux下最常用的打包程序就是tar了,使用tar程序打出来的包我们常称为tar包,tar包文件的命令通常都是以.tar结尾的。生成tar包后,就可以用其它的程序来进行压缩。
例子:
将img1和img2两个文件夹打包成img.tar,仅打包不压缩:
tar -cvf img.tar img1 img2
将img1和img2两个文件夹打包成img.tar.gz,打包后,以gzip压缩:
tar -zcvf img.tar.gz img1 img2
将img1和img2两个文件夹打包成img.tar.bz2,打包后,以bzip2来压缩:
tar -jcvf img.tar.bz2 img1 img2
find
find pathname [op] …
pathname为find命令所查找的目录路径。op为命令选项,常用的有-name、-mtime
find命令在目录结构中搜索文件,并执行指定的操作。
grep
它能使用正则表达式搜索文本,并把匹配的行打印出来
用于过滤/搜索的特定字符。可使用正则表达式能多种命令配合使用
echo
echo string 或 echo $variable
作用:
将string或$variable的值输出到屏幕上
comm
comm命令可以用于两个文件之间的比较,它有一些选项可以用来调整输出,以便执行交集、求差、以及差集操作。
2. golang 基础题
2.1 defer 与 return 的坑
这篇文章总结的还可以,可以进行参考: defer 使用小结与注意要点
func f() (result int) {
defer func() {
result++
}()
return 0
}
func f() (r int) {
t := 5
defer func() {
t = t + 5
}()
return t
}
func f() (r int) {
defer func(r int) {
r = r + 5
}(r)
return 1
}
总结下来就是: - defer是在return之前执行的。这个在 官方文档中是明确说明了的; - return xxx这一条语句并不是一条原子指令! - 函数返回的过程:先给返回值赋值,然后调用defer表达式,最后才是返回到调用函数中。
其实可以拆分下语句规则:
返回值 = xxx
调用defer函数
空的return
接下来就可以改写了:
func f() (result int) {
result = 0 //return语句不是一条原子调用,return xxx其实是赋值+ret指令
func() { //defer被插入到return之前执行,也就是赋返回值和ret指令之间
result++
}()
return
// 返回 1
}
func f() (r int) {
t := 5
r = t //赋值指令
func() { //defer被插入到赋值与返回之间执行,这个例子中返回值r没被修改过
t = t + 5
}
return //空的return指令
// 这个的结果是5。
}
func f() (r int) {
r = 1 //给返回值赋值
func(r int) { //这里改的r是传值传进去的r,不会改变要返回的那个r值
r = r + 5
}(r)
return //空的return
// 这个例子的结果是1。
}
2.2 defer 函数调用与 panic
package main
func main() {
f1()
}
func f1() {
defer println("f1-begin")
f2()
defer println("f1-end")
}
func f2() {
defer println("f2-begin")
f3()
defer println("f2-end")
}
func f3() {
defer println("f3-begin")
panic(0)
defer println("f3-end")
}
上面三个函数嵌套调用相当于就是如下代码:
func main() {
defer println("f1-begin") // defer 先进后出
defer println("f2-begin")
defer println("f3-begin")
panic(0) // panic 之后的代码不再执行
defer println("f3-end")
defer println("f2-end")
defer println("f1-end")
}
所有最终打印如下:
f3-begin
f2-begin
f1-begin
panic: 0
2.3 对结构体内部属性操作记得带上&初始化
type Object interface{}
type Reader interface {
ReadTobuffer()
Buffer() string
}
type read struct {
buffer string
}
func (r read) ReadTobuffer() { // 这里加上 *read
r.buffer = "hello"
}
func (r read) Buffer() string {
return r.buffer
}
func doSometing(obj Object) {
reader, ok := obj.(Reader)
if !ok {
panic("invaild type")
}
reader.ReadTobuffer()
log.Errorf("%+v", reader) // 零值 &{buffer:}
buf := reader.Buffer()
fmt.Println(buf)
}
func main() {
var r = read{} // 这里该加上&read{}
doSometing(r)
}
如上代码打印输出结果:
空值(stirng对应的零值)
2.4 goroutine + channel
var sem = make(chan int, 5)
type Request int
func init() {
for i := 0; i < 5; i++ {
sem <- 1
}
}
func handle(r Request) {
<-sem
process(r)
sem <- 1
}
func process(r Request) {
fmt.Println("process:", r)
}
func Serve(queue chan Request) {
for {
req, ok := <-queue
if ok {
go handle(req)
} else {
log.Error("over") // close(queue) 后则进入此逻辑
break
}
}
time.Sleep(2 * time.Second)
}
func main() {
queue := make(chan Request)
go func() {
for i := 0; i < 5; i++ {
time.Sleep(1 * time.Second)
queue <- Request(i)
}
close(queue) // 如果不关闭通道则会一直等待
}()
Serve(queue)
}
最终打印如下:
process: 0
process: 1
process: 2
process: 3
process: 4
over
2.5 (加分题)*** unsafe.Pointer 与 uintptr
func main() {
s := make([]byte, 200)
ptr := unsafe.Pointer(&s[0])
fmt.Printf("%T\n", s)
fmt.Printf("%T\n", ptr)
s1 := ((*[1 << 10]byte)(ptr))[:200]
fmt.Printf("%T, %d, %d\n", s1, len(s1), cap(s1))
var sl = struct {
add uintptr
len int
cap int
}{uintptr(ptr), 200, 512}
s2 := *(*[]byte)(unsafe.Pointer(&sl)) // unsafe.Pointer 用于不同指正类型转换
fmt.Printf("%T, %d, %d\n", s2, len(s2), cap(s2))
}
打印如下:
[]uint8
unsafe.Pointer
[]uint8, 200, 1024
[]uint8, 200, 512
2.6 取地址值始终取得最后一个问题:
type student struct {
Name string
Age int
}
func pase_student() map[string]*student {
m := make(map[string]*student)
stus := []student{
{Name: "zhou", Age: 24},
{Name: "li", Age: 23},
{Name: "wang", Age: 22},
}
for _, stu := range stus {
m[stu.Name] = &stu
}
// 正确修改如下
// for i := 0; i < len(stus); i++ {
// m[stus[i].Name] = &stus[i]
// }
return m
}
func main() {
students := pase_student()
for k, v := range students {
fmt.Printf("key = %s, value = %v\n", k, v)
}
}
````
打印输出如下:
key = zhou, value = &{wang 22}
key = li, value = &{wang 22}
key = wang, value = &{wang 22}
正确打印结果如下:
key = zhou, value = &{zhou 24}
key = li, value = &{li 23}
key = wang, value = &{wang 22}
---
# 3. 搜索旋转排序数组
## 题目需求:
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
### 你的算法时间复杂度必须是 O(log n) 级别。(其实就是用二分查找法)
## 示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
## 示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
> 这道题的关键,想象自己有一把刀,每次去切数组,一定有一边是有序数组,另外一边无序,如果在有序数组范围内,执行二分查找即可,否则继续切
```go
func search(nums []int, target int) int {
low := 0
hight := len(nums) - 1
for low <= hight {
mid := (low + hight) / 2
if nums[mid] == target {
return mid
}
if nums[low] <= nums[mid] { // 有序数组在左边
if target >= nums[low] && target < nums[mid] {
hight = mid - 1
} else {
low = mid + 1
}
} else { // 有序数组在右边
if target <= nums[hight] && target > nums[mid] {
low = mid + 1
} else {
hight = mid - 1
}
}
}
return -1
}
也可以递归拆分写法调用:
func search(nums []int, target int) int {
return twoSearch(nums, 0, len(nums)-1, target)
}
// 递归写法
func twoSearch(nums []int, left, right, target int) int {
if left > right {
return -1
}
mid := (left + right) / 2
if nums[mid] == target {
return mid
}
if nums[mid] < nums[right] {
if target > nums[mid] && target <= nums[right] {
return twoSearch(nums, mid+1, right, target)
}
return twoSearch(nums, left, mid-1, target)
} else {
if target < nums[mid] && target >= nums[left] {
return twoSearch(nums, left, mid-1, target)
}
return twoSearch(nums, mid+1, right, target)
}
}