0%

日常解惑-Linux中的各类引号

前情提要

在Java中,我们使用单引号来表示一个字符,用双引号来表示一个字符串。而在Python中,单引号和双引号对于字符串来说,毫无区别。那么在shell中单引号和双引号有区别吗?除了单双引号,还有反引号,那么反引号又是用来做什么的?

我使用Python来验证我的想法,argv.py的代码如下:

1
2
3
#!/usr/local/bin/python3
import sys
print('argv.length = ', len(sys.argv), 'argv = ', sys.argv)

直接上结果吧:

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
# 1. 只输入脚本名字
# ./args.py
argv.length = 1 argv = ['./args.py']

# 2. 用空格作为分隔符
./args.py 123 456 789
argv.length = 4 argv = ['./args.py', '123', '456', '789']

# 3. 加上双引号
./args.py "123 456 789"
argv.length = 2 argv = ['./args.py', '123 456 789']

# 4. 加上单引号
# ./args.py '123 456 789'
argv.length = 2 argv = ['./args.py', '123 456 789']

# 5. 使用反引号
# ./args.py `ls`
argv.length = 6 argv = ['./args.py', 'a.py', 'args.py', 'b.py', 'rf.py', 'rnn.py']

# 6. 用双引号包住反引号
# ./args.py "`ls`"
argv.length = 2 argv = ['./args.py', 'a.py\nargs.py\nb.py\nrf.py\nrnn.py']

# 7. 用单引号包住反引号
# ./args.py '`ls`'
argv.length = 2 argv = ['./args.py', '`ls`']

从上面的结果中我们不难发现:

  • 反引号里面的内容会被当成命令先执行,再把结果放到里面执行。
  • 在shell中如果只是单纯引用字符串来说,那么单引号和双引号没有任何区别,这点同Python
  • 如果不是单纯引用字符串,比如有了反引号,或者$,那么在单引号里面全部失效,即反引号还是它自己,美元符还是它自己。但是在双引号里面这些值还是自己。
  • 不论是在Python还是在shell里面,都推荐使用单引号来把字符串括起来。
  • 在windows的cmd里面,只能使用双引号来把名字中带空格的文件夹引起来。

拓展argv

C语言中,最为“标准”的main函数写法是int main(int argc, char *argv[]),而在java中是public static void main(String[] args),因为java里面的数组是有长度的,所以不需要argc

那么第一个问题,为什么用python xxx.py和直接使用./xxx.py执行程序,这个argv是一样的呢?

image-20210118114317821

我们之前学过,argv的第一个参数(也就是数组中的argv[0])应该是程序名,那么照理来说,上面的第一个程序应该是python3,但是仍然是 ./argv,那这是为什么呢?

可以通过strace命令来帮助查看对应的调用:

1
2
3
4
5
execve("/usr/local/bin/python3", ["python3", "args.py"], [/* 25 vars */]) = 0
execve("./args.py", ["./args.py"], [/* 25 vars */]) = 0

// execve的函数声明是:
int execve(const char *filename, char *const argv[], char *const envp[]);

而如果我们再加入java进行对比的话:

1
execve("/usr/bin/java", ["java", "ArgsTest", "123"], [/* 25 vars */]) = 0

java中的argv[0]就是123,会发现java和Python的处理方法是不一样的,即java会忽略掉字节码文件的名字,而是把参数当成是第一个参数;而在Python中,会把执行的文件名字作为第一个参数。