wys的个人博客

你有很多事放不下?做人要潇洒一点~

0%

Shell Tools and Scripting

Shell Tools and Scripting

shell可以定义变量

1
2
3
rust@DESKTOP-L4PLVQ4:~$ foo=bar
rust@DESKTOP-L4PLVQ4:~$ echo $foo
bar

用空格分开会被认为是可执行程序

1
2
rust@DESKTOP-L4PLVQ4:~$ foo = bar
Command 'foo' not found, did you mean:

在打印字符串的时候,单引号和双引号相同

1
2
3
4
rust@DESKTOP-L4PLVQ4:~$ echo "Hello"
Hello
rust@DESKTOP-L4PLVQ4:~$ echo 'World'
World

但是在打印变量的时候,单引号不会替换变量

1
2
3
4
rust@DESKTOP-L4PLVQ4:~$ echo "Value is $foo"
Value is bar
rust@DESKTOP-L4PLVQ4:~$ echo 'Value is $foo'
Value is $foo

可以定义shell脚本

1
2
3
4
mcd () {
mkdir -p "$1"
cd "$1"
}

source命令可以将shell脚本加载进shell,然后就可以执行shell脚本

1
2
3
rust@DESKTOP-L4PLVQ4:~/missing$ source mcd.sh
rust@DESKTOP-L4PLVQ4:~/missing$ mcd test
rust@DESKTOP-L4PLVQ4:~/missing/test$

$_ 返回上一个命令的最后一个参数

1
2
3
rust@DESKTOP-L4PLVQ4:~/missing$ mkdir test
rust@DESKTOP-L4PLVQ4:~/missing$ cd $_
rust@DESKTOP-L4PLVQ4:~/missing/test$

!! 可以用以代替上一条命令

1
2
3
4
rust@DESKTOP-L4PLVQ4:~/missing/test$ mkdir /mnt/new
mkdir: cannot create directory ‘/mnt/new’: Permission denied
rust@DESKTOP-L4PLVQ4:~/missing/test$ sudo !!
sudo mkdir /mnt/new

$? 可以返回上一条命令的退出状态

1
2
3
4
5
6
7
rust@DESKTOP-L4PLVQ4:~/missing$ echo "Hello"
Hello
rust@DESKTOP-L4PLVQ4:~/missing$ echo $?
0
rust@DESKTOP-L4PLVQ4:~/missing$ grep foobar mcd.sh
rust@DESKTOP-L4PLVQ4:~/missing$ echo $?
1

0表示正常退出,1在grep中表示error code

1
2
3
4
5
rust@DESKTOP-L4PLVQ4:~/missing$ echo $?
0
rust@DESKTOP-L4PLVQ4:~/missing$ false
rust@DESKTOP-L4PLVQ4:~/missing$ echo $?
1

总结

  • $0 - Name of the script
  • $1 to $9 - Arguments to the script. $1 is the first argument and so on.
  • $@ - All the arguments
  • $# - Number of arguments
  • $? - Return code of the previous command
  • $$$$ - Process identification number (PID) for the current script
  • !! - Entire last command, including arguments. A common pattern is to execute a command only for it to fail due to missing permissions; you can quickly re-execute the command with sudo by doing sudo !!
  • $_ - Last argument from the last command. If you are in an interactive shell, you can also quickly get this value by typing Esc followed by . or Alt+.

逻辑运算符,同c语言一样有短路机制。

1
2
3
4
5
6
7
8
rust@DESKTOP-L4PLVQ4:~/missing$ false || echo "Oops fail"
Oops fail
rust@DESKTOP-L4PLVQ4:~/missing$ true || echo "Will be not be printed"
rust@DESKTOP-L4PLVQ4:~/missing$
rust@DESKTOP-L4PLVQ4:~/missing$ true && echo "Things went well"
Things went well
rust@DESKTOP-L4PLVQ4:~/missing$ false && echo "This will not print"
rust@DESKTOP-L4PLVQ4:~/missing$

在命令两边加上 $() 可以获得命令的输出并给变量赋值,你可以像下述这样使用它:

1
2
3
4
5
rust@DESKTOP-L4PLVQ4:~/missing$ foo=$(pwd)
rust@DESKTOP-L4PLVQ4:~/missing$ echo $foo
/home/rust/missing
rust@DESKTOP-L4PLVQ4:~/missing$ echo "We are in $(pwd)"
We are in /home/rust/missing

注意在执行echo命令的时候要加上双引号。

还有一个不为别人所熟知的特性是 <( CMD )。

这个特性会执行CMD的值,然后将结果放在一个临时文件中,然后用<( )代替文件的名字。

For example, diff <(ls foo) <(ls bar) will show differences between files in dirs foo and bar.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
rust@DESKTOP-L4PLVQ4:~/missing$ tree
.
├── a
│   └── a.txt
├── b
│   └── b.txt
├── mcd.sh
└── test

3 directories, 3 files
rust@DESKTOP-L4PLVQ4:~/missing$ diff <(ls a) <(ls b)
1c1
< a.txt
---
> b.txt

接下来是一段 shell 脚本,

/dev/null 是linux一个特殊的设备,它丢弃一切写入其中的数据。

2> 是标准错误重定向,当发生标准错误时,数据写入到 2> 之后。

When performing comparisons in bash, try to use double brackets [[ ]] in favor of simple brackets [ ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/bin/bash

echo "Starting program at $(date)" # Date will be substituted

echo "Running program $0 with $# arguments with pid $$"

for file in "$@"; do
grep foobar "$file" > /dev/null 2> /dev/null
# When pattern is not found, grep has exit status 1
# We redirect STDOUT and STDERR to a null register since we do not care about them
if [[ $? -ne 0 ]]; then
echo "File $file does not have any foobar, adding one"
echo "# foobar" >> "$file"
fi
done

使用大括号可以将多个参数聚合到一块,例如:

1
2
3
rust@DESKTOP-L4PLVQ4:~/missing$ touch foo{,1,2,10}
rust@DESKTOP-L4PLVQ4:~/missing$ ls
a b foo foo1 foo10 foo2 mcd.sh test

如下命令可以在foo,和 bar 下创建 a-j 的文件

1
2
3
rust@DESKTOP-L4PLVQ4:~/missing$ mkdir foo bar
rust@DESKTOP-L4PLVQ4:~/missing$ touch {foo,bar}/{a..j}
rust@DESKTOP-L4PLVQ4:~/missing$

我们对于如下的python文件

1
2
3
4
#!/usr/bin/python
import sys
for arg in reversed(sys.argv[1:]):
print(arg)

我们可以采用如下方式运行程序

1
2
3
4
5
6
7
8
rust@DESKTOP-L4PLVQ4:~/missing$ ./script.py a b c
c
b
a
rust@DESKTOP-L4PLVQ4:~/missing$ python script.py a b c
c
b
a

需要注意的是使用 ./ 启动需要在文件上方加上#!/usr/bin/python 来找到执行文件的程序。

或者你可以在程序上方加上 #!/usr/bin/env python 这句话的意思是在环境变量中找python所在位置,这种方式可以让该程序在不同机器上运行。 这行代码被称为shebang。

我们可以用一种叫做shellcheck的工具来检查脚本。

有个叫做 tldr 的很好用的工具,可以查看命令的示例用法。

以下是 tar 的用法

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

- [c]reate an archive and write it to a [f]ile:
tar cf {{target.tar}} {{file1}} {{file2}} {{file3}}

- [c]reate a g[z]ipped archive and write it to a [f]ile:
tar czf {{target.tar.gz}} {{file1}} {{file2}} {{file3}}

- [c]reate a g[z]ipped archive from a directory using relative paths:
tar czf {{target.tar.gz}} --directory={{path/to/directory}} .

- E[x]tract a (compressed) archive [f]ile into the current directory [v]erbosely:
tar xvf {{source.tar[.gz|.bz2|.xz]}}

- E[x]tract a (compressed) archive [f]ile into the target directory:
tar xf {{source.tar[.gz|.bz2|.xz]}} --directory={{directory}}

- [c]reate a compressed archive and write it to a [f]ile, using [a]rchive suffix to determine the compression program:
tar caf {{target.tar.xz}} {{file1}} {{file2}} {{file3}}

- Lis[t] the contents of a tar [f]ile [v]erbosely:
tar tvf {{source.tar}}

- E[x]tract files matching a pattern from an archive [f]ile:
tar xf {{source.tar}} --wildcards "{{*.html}}"

locate 是基于索引的查找,比find快,而且是搜索全路径,通过updatedb 来更新整个数据库。

grep可以对整个文件夹进行查找

1
2
3
rust@DESKTOP-L4PLVQ4:~/missing$ echo "foobar" >> foo1
rust@DESKTOP-L4PLVQ4:~/missing$ grep -R foobar .
./foo1:foobar

ctrl+r 可以搜索历史命令