# 命令
# 命令格式
一条 shell 命令的基本格式为 <命令名> <option> <argument>
。
- 每个字段之间用空格隔开(多个空格也看作一个空格),区分大小写。
- 在命令的用法说明中,通常用尖括号
<>
表示必填字段,用方括号[]
表示可选字段,用省略号...
表示该字段可以输入多个。如下:ls [OPTION]... ping [-c count] [-i interval] destination
- 命令名可以是某条内建命令的名字、某个已安装软件的名字,或者某个可执行文件的路径(绝对路径或相对路径)。
- option 用于选择该命令的一项功能,有长格式(比如 --option )、短格式(比如 -o )两种写法。如下:
- 有些短格式的命令行选项可能与某个长格式选项等价,比如 -h 与 --help 。但有些命令只有 --help 选项,没有 -h 选项;有些命令有 -h 选项,但不等价于 --help 选项。
- 多个命令行选项可以组合使用,且不要求顺序,比如
ls -a -l
相当于ls -l -a 或 ls -al
。
- argument 可能是传给命令的参数,也可能是传给命令行选项的参数。
- 给命令行选项传入参数时,可以用空格或等号作为分隔符,比如
head -n 3 file
、head -lines=3 file
。
- 给命令行选项传入参数时,可以用空格或等号作为分隔符,比如
# 执行命令
在终端执行一条命令,实际上是执行一个程序来实现某种操作。
- 该程序默认是终端的子进程。
- 输入的命令参数会传给该程序,控制该程序的行为。
- 程序执行结束之后会返回一个退出码。
执行一条命令时,系统会按以下顺序找到一个对应的程序来执行:
- 查找命令名是否为一个有效的文件路径(绝对路径或相对路径),如果是则执行该文件。
- 查找命令名是否为 alias 定义的别名。
- 查找命令名是否为 shell 的内建命令。
- 到 PATH 路径下查找与命令名同名的可执行文件。
- 如果始终没有找到,则报错:command not found
# PATH
- 环境变量
PATH
记录了一些可执行文件的保存路径,每个路径之间用冒号 : 分隔。如下:[root@CentOS ~]# echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
- Linux 系统的 PATH 默认不包含当前目录
.
,因此运行当前目录下的可执行文件时要加上./
前缀。如下:[root@CentOS ~]# 1.sh -bash: 1.sh: command not found [root@CentOS ~]# ./1.sh hello
- 例:添加路径到 PATH 中
[root@CentOS ~]# export PATH=$PATH:/root/bin
# 执行多条命令
一般每条命令独占一行,也可以用分号
;
标明一条命令的结束,从而在同一行内输入多条命令。如下:[root@CentOS ~]# x=1; echo $x 1
也可以用
&&
或||
连接多条命令,这样会当前一条命令执行成功或失败时才执行后一条命令。如下:[root@CentOS ~]# ls /root/f1 && echo True || echo False ls: cannot access /root/f1: No such file or directory False
在行尾输入反斜杠
\
,就可以将一条命令换行输入。如下:[root@CentOS ~]# echo hello \ > world hello world
# 命令的输入、输出
# echo
- echo 命令用于将字符串显示到当前终端。用法:
$ echo <string>... # 让终端回显字符串 -n # 显示之后不自动换行 -e # 解析转义字符。默认不会解析
- 例:
[root@CentOS ~]# echo hello # 显示字符串 hello [root@CentOS ~]# a=1 && echo $a # 提取变量的值再显示 1 [root@CentOS ~]# echo -n hello # 显示之后不自动换行 hello[root@CentOS ~]# [root@CentOS ~]# echo -e "hello\n\x41" # 解析转义字符 hello A
# yes
$ yes [string] # 持续不断地输出一个字符串(默认是 'y' )到 stdin
- 例:
yes hello yes | yum install git
# 重定向
一般的命令都是从 stdin 读取输入,并将输出写到 stdout ,将报错写到 stderr 。
通过重定向 stdin、stdout、stderr 的文件描述符,可以转移终端的输入输出。如下:
echo hello > stdout.log # 用 > 将 stdout 重定向到指定文件,相当于 1> echo hello >> stdout.log # 用 >> 是追加式写入 echo hello 2> stderr.log # 2> 是重定向 stderr echo hello &> output.log # &> 是重定向 stdout 和 stderr echo hello 2>&1 # 将 stderr 重定向到 stdout echo hello 1> stdout.log 2> stderr.log # 分别重定向 stdout、stderr echo hello 3>&1 1>&2 2>&3 # 创建一个文件描述符 3 ,将它重定向到 1 。然后将 1 重定向到 2 ,将 2 重定向到 3 。这样就互换了 stdin 和 stdout cat < stdout.log # 用 < 将指定文件重定向到 stdin ,即读取该文件的内容作为输入,读取到 EOL 为止 cat <& 0 # 加上 & ,将指定 ID 的文件描述符重定向到 stdin cat <<EOF ... EOF # 以 EOF 作为定界符(可以换成其它符号),将一些内容重定向到 stdin
- 使用 > 或 >> 时,
- 如果目标文件不存在,则会自动创建它。
- 如果在命令执行时将目标文件删掉,则不会重新创建该文件,因此不会保存重定向之后的输出。
- 使用 > 时,
- 如果目标文件已存在,则会先将其长度截断为 0 ,相当于覆盖式写入。
- 使用 > 或 >> 时,
例:
[root@CentOS ~]# cat > f1 <<EOF >Hello >World >EOF
同时重定向多个文件描述符时,是按从右往左的优先级:
[root@CentOS ~]# echo1 >f1 2>&1 # 这是先将 stderr 重定向到 stdout ,再将 stdout 重定向到 f1 [root@CentOS ~]# echo1 2>&1 >f1 # 这是先将 stdout 重定向到 f1 ,而 stderr 输出到了终端 -bash: echo1: command not found
# 管道符
使用管道符 |
可以在连续执行两条命令时,将上一条命令的 stdout 转化成下一条命令的 stdin 。
- 例:
cat /etc/*-release | grep Linux
# xargs
有些命令默认会读取 stdin ,因此可以通过管道符传递上一条命令的输出;有些命令默认不会读取 stdin ,此时可使用管道符加 xargs 命令。
- 命令:
$ xargs [command] # 从 stdin 中提取内容,传给子命令去执行(子命令默认为 echo )。每传递一行内容,就执行一次子命令 -d ',' # 提取内容时的字段分隔符。默认为一个或多个连续的空格、Tab -n 3 # 每提取 3 个字段就换行 -L 3 # 每提取 3 行,就合并为一行。默认全部合并为一行 -I {} # 默认会将提取的内容拼接到子命令末尾,而使用 -I 选项会进行字符串替换 -P 1 # 并发进程数,允许同时执行多少个子命令。默认为 1
- 例:
[root@CentOS ~]# echo A B | xargs A B [root@CentOS ~]# echo A B | xargs -n 1 echo A B [root@CentOS ~]# ls /root/ | xargs -n1 -P4 -I{} rsync -aP /root/{} /tmp/ # 并行拷贝
# tee
$ tee <file>... # 将 stdin 的内容同时拷贝到 stdout 和一个文件中
-a # 采用追加模式写入(默认采用覆盖模式写入)
- 例:
[root@CentOS ~]# echo Hello | tee f1 # 类似于 echo hello > f1 ,但是依然会输出 stdout Hello [root@CentOS ~]# echo Hello | tee - | wc -l # tee - 是输出到 /dev/stdout ,因此这里输出两份到 stdout 2 [root@CentOS ~]# echo Hello | tee /dev/tty | wc -l # 这里让 tee 输出到当前终端,不经过 stdout Hello 1
# 关于命令的命令
# alias
$ alias # 显示所有已定义的命令别名
<name> # 显示某个别名的定义语句
cd1="cd ~" # 定义一个别名
- 将 alias 的定义语句写到
/etc/bashrc
中就可以永久生效,如下:alias ll='ls -lh --color=auto' alias ls='ls --color=auto' alias cp='cp -i' alias rm='rm -i'
# unalias
$ unalias <name> # 取消一个别名
# man
$ man <name> # 显示命令的使用手册(manual),这会打开 less 阅读器
# type
$ type <name>... # 显示指定命令名的类型(还会显示该命令名的二进制文件的位置)
# which
$ which <name>... # 显示指定命令的可执行文件的绝对路径(在 PATH 路径下寻找,显示第一个找到的)
-a # 显示指定命令的所有可执行文件
-i # 考虑 alias 别名
# whereis
$ whereis <name>... # 显示指定命令的可执行文件、源代码、manual 文件的绝对路径(在 PATH、MANPATH 路径下寻找)
# history
$ history # 显示所有历史命令
[n] # 只显示最近的 n 条
-c # 清空当前终端的历史命令,因此它们不会保存到 HISTFILE
-r # 读取 HISTFILE 的内容,作为当前终端的历史命令
-a # 将当前终端中新增的历史命令追加写入 HISTFILE
-w # 将当前终端的全部历史命令写入 HISTFILE ,覆盖原内容
用户每次打开一个交互式终端时,shell 会从 HISTFILE 加载历史命令到内存中,并记录当前终端新增的历史命令。
- 当终端关闭时,shell 会自动执行
history -a
,保存新增的历史命令。可执行unset HISTFILE
或history -c
避免保存。 - HISTFILE 文件可能被删除、篡改,可信度不高。
- 当终端关闭时,shell 会自动执行
相关的环境变量:
HISTFILE=~/.bash_history # 保存历史命令的文件 HISTFILESIZE=1000 # 最多记录多少条命令 HISTSIZE=1000 # 执行 history 命令时最多显示多少条命令 HISTTIMEFORMAT="%F %T " # 执行 history 命令时,在每条命令之前显示时间戳 HISTCONTROL=ignoredups # 如果几条连续执行的命令是重复的,则只记录第一条 # ignorespace # 不记录以空格开头的命令 # ignoreboth # 不记录连续重复的命令,或以空格开头的命令 PROMPT_COMMAND="history -a" # 每次显示终端提示符之前,执行一次该命令
例:
[root@CentOS ~]# export HISTTIMEFORMAT="%F %T `whoami` " [root@CentOS ~]# history 3 1031 root 2020-07-08 15:20:05 ls 1032 root 2020-07-08 15:20:16 export HISTTIMEFORMAT="%F %T `whoami` " 1033 root 2020-07-08 15:20:17 history 3
可通过以下快捷方式执行历史命令:
!! # 打印上一条命令的内容,并执行它 sudo !! # 以 root 用户的身份执行上一条命令 !n # 执行历史记录中的第 n 条命令 !$ # 获得上一条命令的最后一个参数