知识点总结

一些面试取经后自己梳理的知识点总结

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/以下。

例子:

  1. tail -f filename 说明:监视filename文件的尾部内容(默认10行,相当于增加参数 -n 10),刷新显示在屏幕上。退出,按下CTRL+C。

  2. tail -n 20 filename 说明:显示filename最后20行。

  3. tail -r -n 10 filename 说明:逆序显示filename最后10行。

  4. tail -f test.log 说明:循环读取test.log的内容,只要test.log有新内容写入,将立即读取到标准输出


cat

cat [op] file

说明:

op 为命令参数,常用的有-b、-n、-s

  • -b:对非空输出行编号
  • -n:对输出的所有行编号,由1开始对所有输出的行数编号
  • -s:有连续两行以上的空白行,就代换为一行的空白行

作用:

  1. (显示文件内容)一次显示整个文件:cat filename

  2. (创建文件)从键盘创建一个文件:cat > filename 只能创建新文件,不能编辑已有文件.

  3. (合并文件)将几个文件合并为一个文件: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)
    }
}