Fork me on GitHub

shell基础语法

shell基础语法

if语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
if [[ expression ][
then
Statement(s) to be executed if expression is true
fi

if test expression; then
Statement(s) to be executed if expression is true
fi

if [[ expression 1 ]]
then
Statement(s) to be executed if expression 1 is true
elif [[ expression 2 ]]
then
Statement(s) to be executed if expression 2 is true
elif [[ expression 3 ]]
then
Statement(s) to be executed if expression 3 is true
else
Statement(s) to be executed if no expression is true
fi

for循环

1
2
3
4
SERVICES="80   22   25   110   8000   23   20   21   3306   "
for x in $SERVICES; do
iptables -A INPUT -p tcp --dport $x -m state --state NEW -j ACCEPT
done

数组

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
# 以下是Bash的用法,与zsh的不同
# a=(1 2 3 4 5)
# echo $a
1
# echo ${#a[@]} # 用${#数组名[@或*]} 可以得到数组长度
5
# echo ${a[*]} # 用${数组名[下标]} 下标是从0开始 下标是:*或者@ 得到整个数组内容
1 2 3 4 5

# 直接通过 数组名[下标] 就可以对其进行引用赋值,如果下标不存在,自动添加新一个数组元素到数组末尾
# a[1]=100 && echo ${a[1]}
100
# a[10]=100 && echo ${a[*]} && echo ${a[10]} && echo ${#a[*]}
1 2 3 4 5 100
100
6

# 直接通过:unset 数组[下标] 可以清除相应的元素,不带下标,清除整个数据。
# unset a[1] && echo ${#a[*]} && echo ${a[1]} && echo ${a[*]}
5

1 3 4 5 100

# 直接通过 ${数组名[@或*]:起始位置:长度} 切片原先数组,返回是字符串,中间用“空格”分开,因此如果加上”()”,将得到切片数组,上面例子:c 就是一个新数据。
# a=(1 2 3 4 5) && echo ${a[@]:0:3}
1 2 3
# c=(${a[@]:1:4}) && echo ${#c[@]}
4

# 替换元素调用方法是:${数组名[@或*]/查找字符/替换字符} 该操作不会改变原先数组内容,如果需要修改,可以看上面例子,重新定义数据。
# a=(1 2 3 4 5) && c=(${a[@]/3/100}) && echo ${c[@]}
1 2 100 4 5

# 判断元素是否在数组中
# if [[ ${array[@]} =~ $item ]]; then echo 'yes'; fi

命令行解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# use break to jump out for loop
for opt in $@; do
case $opt in
init) project_init
break;;
start) start_train
break;;
stop) stop_train
break;;
resume) resume_train
break;;
*) show_usage
break;;
esac
done

函数返回值

  • 通过子命令的方式

    函数内部任何标准输出都会返回给接收变量,结果容易受到输出日志的干扰

    1
    2
    3
    4
    5
    6
    function foo {
    echo 1
    echo 2
    }
    a=$(foo)
    echo $a # 1 2
  • 通过return返回整数值,$?获取

    1
    2
    3
    4
    5
    6
    7
    function foo {
    echo 1
    echo 2
    return 0
    }
    foo # 1 2
    echo $? # 0

字符串操作

  • 截取字符串
表达式 含义
${#string} $string的长度
${string:position} 在$string中, 从位置$position开始提取子串
${string:position:length} 在$string中, 从位置$position开始提取长度为$length的子串
${string#substring} 从变量$string的开头, 删除最短匹配$substring的子串
${string##substring} 从变量$string的开头, 删除最长匹配$substring的子串
${string%substring} 从变量$string的结尾, 删除最短匹配$substring的子串
${string%%substring} 从变量$string的结尾, 删除最长匹配$substring的子串
${string/substring/replacement} 使用$replacement, 来代替第一个匹配的$substring
${string//substring/replacement} 使用$replacement, 代替所有匹配的$substring
${string/#substring/replacement} 如果$string的前缀匹配$substring, 那么就用$replacement来代替匹配到的$substring
${string/%substring/replacement} 如果$string的后缀匹配$substring, 那么就用$replacement来代替匹配到的$substring
  • 示例:

    ${str:0:-1} 去掉最后一个字符
    ${str:0: ${#str}} 返回整个字符串。避免写成$#str,$#有特殊含义,是传给函数或脚本的参数个数
    ``` 
  • 定义字符串

表达式 含义
${var} 变量var的值, 与$var相同
${var-DEFAULT} 如果var没有被声明, 那么就以$DEFAULT作为其值,但是不会改变变量的值 *
${var:-DEFAULT} 如果var没有被声明, 或者其值为空, 那么就以$DEFAULT作为其值 *
${var=DEFAULT} 如果var没有被声明, 那么就以$DEFAULT作为其值,跟上面-的区别在于会设置变量的值 *
${var:=DEFAULT} 如果var没有被声明, 或者其值为空, 那么就以$DEFAULT作为其值 *
${var+OTHER} 如果var声明了, 那么其值就是$OTHER, 否则就为null字符串
${var:+OTHER} 如果var被设置了, 那么其值就是$OTHER, 否则就为null字符串
${var?ERR_MSG} 如果var没被声明, 那么就打印$ERR_MSG *
${var:?ERR_MSG} 如果var没被设置, 那么就打印$ERR_MSG *
${!varprefix*} 匹配之前所有以varprefix开头进行声明的变量
${!varprefix@} 匹配之前所有以varprefix开头进行声明的变量

Git Hooks

Git Hooks

git通过hooks来定制行为,所有的hooks存储在hooks目录,客户端是.git/hooks目录。
git init的时候会在.git/hooks目录生成很多示例hook,只要重命名文件就能启用。

hook分为

  • 客户端hook
  • 服务端hook

客户端hook不会被克隆,如果要强制实现某个git策略,需要用服务端hook。

客户端hook

客户端hook在工程目录下运行
客户端hook又可以分为

  • committing-workflow
  • email-workflow
  • 其他

committing-workflow

  • pre-commit

    第一个运行的hook,在创建提交message之前就运行。
    返回非0则终止提交,但是可以用git commit --no-verify来跳过检查。
    可以在这个hook中运行测试,检查代码格式,做代码静态检查等。

  • prepare-commit-msg

    在默认commit message创建之后,打开message编辑器之前运行。
    会传入几个参数:当前消息文件路径、commit类型,如果是amend提交再加上commit的SHA-1。
    一般用来生成自动提交的信息。

  • commit-msg

    编辑完提交消息之后运行。
    传入一个参数:保存用户提交消息的临时文件的路径。
    返回非0终止提交。
    一般用来验证消息格式。

  • post-commit

    提交完成之后运行。
    一般用来做提交通知。

email-workflow hooks

通过git am命令调用,如果你需要通过email来接收patch,可能会用到这个类型的hook。

其他

  • pre-rebase

    rebase之前运行。
    返回非0终止rebase。

  • post-rewrite

    git commit --amendgit rebase会触发这个hook。
    传入参数:触发hook的命令。

  • post-checkout

    checkout成功之后运行。

  • post-merge

    merge成功之后运行。

  • pre-push

    remote ref更新之后,传输对象之前运行。
    返回非0终止push。
    传入参数:远端的名称和地址。

  • pre-auto-gc

    git gc --auto触发。

服务端hook

服务端hook在服务器的工程.git目录下运行

  • pre-receive

    服务端接收push第一个运行的hook,从stdin接收被push过来的references。
    返回非0则终止push。

  • update

    类似pre-receive,但是每个分支运行一次。
    传入三个参数:分支名、前一个commit的SHA-1,当前push的SHA-1
    返回非0则终止push。

  • post-receive

    所有流程完成之后运行。
    跟pre-receive一样从stdin传入references。
    客户端等待这个脚本完成才断开连接。

reference

https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks
https://docs.gitlab.com/ee/administration/custom_hooks.html

Cpp Static Initialization Order Fiasco

Static initialization order fiasco

  • Construct On First Use Idiom

    • If the object does not need to be destructed, uses a static pointer.

      1
      2
      3
      4
      5
      6
      7
      // File x.cpp
      #include "Fred.h"
      Fred& x()
      {
      static Fred* ans = new Fred();
      return *ans;
      }
      1
      2
      3
      4
      5
      6
      7
      8
      // File Barney.cpp
      #include "Barney.h"
      Barney::Barney()
      {
      // ...
      x().goBowling();
      // ...
      }

      This approach is that the Fred object is never destructed.
      The Construct On First Use Idiom uses a pointer and intentionally leaks the object.

    • If you need to destruct the object and do not care the order of static objects deinitialization, uses a static object.

      1
      2
      3
      4
      5
      6
      7
      // File x.cpp
      #include "Fred.h"
      Fred& x()
      {
      static Fred ans; // was static Fred* ans = new Fred();
      return ans; // was return *ans;
      }

      It does not leak the object, but it does not control the order of static deinitialization, so it is (very!) unsafe to use the object during static deinitialization, that is, from a destructor of another statically declared object.

  • Nifty Counter

    It is a more complicated solution that solves problems mentioned above.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // Stream.h
    #ifndef STREAM_H
    #define STREAM_H

    struct Stream {
    Stream ();
    ~Stream ();
    };
    extern Stream& stream; // global stream object

    static struct StreamInitializer {
    StreamInitializer ();
    ~StreamInitializer ();
    } streamInitializer; // static initializer for every translation unit

    #endif // STREAM_H
    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
    // Stream.cpp
    #include "Stream.h"

    #include <new> // placement new
    #include <type_traits> // aligned_storage

    static int nifty_counter; // zero initialized at load time
    static typename std::aligned_storage<sizeof (Stream), alignof (Stream)>::type
    stream_buf; // memory for the stream object
    Stream& stream = reinterpret_cast<Stream&> (stream_buf);

    Stream::Stream ()
    {
    // initialize things
    }
    Stream::~Stream ()
    {
    // clean-up
    }

    StreamInitializer::StreamInitializer ()
    {
    if (nifty_counter++ == 0) new (&stream) Stream (); // placement new
    }
    StreamInitializer::~StreamInitializer ()
    {
    if (--nifty_counter == 0) (&stream)->~Stream ();
    }

    The header file of the Stream class must be included before any member function can be called on the Stream object.
    An instance of the StreamInitializer class is included in each compilation unit. Any use of the Stream object follows the inclusion of the header, which ensures that the constructor of the initializer object is called before the Stream object is used.

go-doc-tips

Go doc tips

  1. 新建doc.go作为文档文件,package pkgname之前的注释作为文档。

  2. 文档注释的格式

    1. 相邻的行位于同一段落,分段需要添加空行
    2. 上下空行的短语会被作为接下来段落的head
    3. 预先格式化的文本需要跟周围的注释保持缩进
    4. URL会被转换成链接,不需要标记
  3. Go同一个目录可以添加{pkg}_test的测试包。

  4. {pkg}_test的文件(_test.go结尾),函数名以Example开头且没有参数的函数作为示例代码。

    1
    2
    3
    4
    5
    6
    func ExampleFoo()     // documents the Foo function or type
    func ExampleBar_Qux() // documents the Qux method of type Bar
    func Example() // documents the package as a whole
    func ExampleReverse() // Reverse first example
    func ExampleReverse_second() // Reverse second example
    func ExampleReverse_third() // Reverse thirdß example

    _test.go结尾的文件,如果只包含一个Example函数,并且有其他包级声明,整个文件会作为示例代码显示。

  5. // Output: olleh预期输出,示例函数可以作为go test的测试用例。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    package stringutil_test

    import (
    "fmt"

    "github.com/golang/example/stringutil"
    )

    func ExampleReverse() {
    fmt.Println(stringutil.Reverse("hello"))
    // Output: olleh
    }

vim-shortcut

vim快捷键

跳转

  1. 行内跳转

    ^ 行开头
    $ 行尾
    w/W 下一个单词开头
    e/E 下一个单词末尾
    b/B 上一个单词开头
    f+{字符} 下一个字符本身 F+{字符} 上一个字符本身
    t+{字符} 下一个字符之后 T+{字符} 上一个字符之后

  1. 跨行跳转

    hjkl
    {数字}+h/j/k/l 跳转n个位置
    {数字}+G 跳转到第n行
    * 向下搜索光标对应单词
    # 向上搜索光标对应单词
    gg 文件开头
    G 文件末尾
    ctrl+b 向上翻页
    ctrl+f 向下翻页
    ctrl+u 向上翻半页
    ctrl+d 向下翻半页
    ctrl+o 跳转到光标上一个位置
    ctrl+i 跳转到光标下一个位置

编辑

  1. 行内编辑

    x 删除一个字符
    {数字}+x 删除n个字符
    dd 删除一行,包括换行
    d+{数字}+d / {数字}+dd 删除n行
    d+w/W / d+e/E 向后删除一个单词
    d+b/B 向前删除一个单词
    D / d+$ 删除光标之后所有字符,不包括换行
    i 光标之前插入
    a 光标之后插入
    r 替换光标处字符
    {数字}+r 替换光标开始n个字符
    o 插入下一行,光标跳转到新行
    O 插入上一行,光标跳转到新行

  2. 复制粘贴

    yy 复制一行
    {数字}+yy / y+{数字}+y 复制n行
    y+{位置}+y 复制光标与某一行直接的内容
    y+w/W / y+e/E 向后复制一个单词
    y+b/B 向前复制一个单词
    p 在光标后粘贴
    P 在光标前粘贴
    {数字}+p 多次粘贴

寄存器

  1. 常规模式

    :h registers 查看man page
    "+{寄存器名} 引用一个寄存器
    "" 未命名寄存器,复制或删除都会替换它的内容
    "0~"9 "0 保存最后一次复制内容 "1~"9 保存最后删除的内容,"1为最近删除
    "+{寄存器名}+y 复制到指定寄存器
    "+{寄存器名}+p 粘贴指定寄存器内容
    :reg {寄存器名} 查看寄存器内容
    ". "% ": "# 只读寄存器

    ".存最后插入的内容;"%存当前文件相对于vim打开时的路径;":存最后执行的命令,使用@:重复执行上一次命令;"#存替换文件名

    "=保存表达式或命令结果,ctrl-r =可以在插入模式下粘贴结果,:%s/<Ctrl-r>/something/g可以直接替换之前搜索的结果
    :let @{寄存器名}='<Ctrl-r {寄存器名}>' 可以修改寄存器内容

  2. 编辑模式

    ctrl+r+{寄存器名} 粘贴寄存器内容

  • © 2015-2024 RivenZoo
  • Powered by Hexo Theme Ayer
  • PV: UV:

请我喝杯咖啡吧~

支付宝
微信