My Blog

Linux_cmd

记录一些不熟练或之前未了解到的 Linux 命令,参考 linuxfun 书籍

网址 : linux-training.be

链接 : 所参考书籍的下载连接

目录 :


由于目录、文件的增删改查等命令经常用,这里就不在记录

1. 查看文本文件内容

# 默认查看指定文件的前十行内容
head /etc/passwd

# 查看文件的前 n 行内容
head -n /etc/passwd

# tail 命令与 head 类似,它从尾开始看
tail -n /etc/passwd

# cat 将标准输入的内容复制到标准输出
cat ~/dev/Demo.c    # look content of file on terminal

# cat 也是 concatenate 的缩写,即将多个输入拼接再输出
echo one>part1
echo two>part2
echo three>part3
cat part1 part2 part3 > all
cat all
out: one
     two
     three

# cat 创建文件,即将终端作为输入,输出到指定文件中
# 终端书写是以 EOF(end of file) 作为书写结束
# 一般默认为 CTRL+d
cat >test.txt
hello world!
[Ctrl+d]

# cat 创建文件并自定义结束符,用 << 来定义
cat > winter.txt <<stop
> It's so cold!
> stop

# cat 复制文件
cat winter.txt > cold.txt

# tac 即 cat 的反写形式,它是以从尾到头的形式查看
tac ~/dev/Demo.c

# more | less 适用于查看超过一屏的文件内容,空格翻页,q 退出
less ~/dev/Demo.c

# strings 打印一个文件中可打印的内容
# 例如可将一些二进制文件, java 字节码文件以可读的形式打印
strings /usr/bin/ls
strings ~/dev/Demo.class

2. shell expansion

2.1. 命令与参数

shell 根据空白符将命令分为一个个参数,第一个参数作为要执行的命令,其他的为命令要用到的参数。通常命令的空白符会被忽略,但加上 ‘ 或 “ 号,会变为字符串输出,而且 echo -e 允许加转义字符。

外部命令(External),有它们自己的二进制文件,大多位与 /bin 或 /sbin 目录下;而内部命令(Builtin) 属于 shell 程序的一部分。可用 type 命令查看命令的类型,如 type cat 。echo 是既是内部的(默认),也是外部的。 which 在 Path 环境变量下查找命令的二进制文件路径,如 which cd 。

2.2. 别名(alias)

创建别名:alias c='clear' , alias rm='rm -i'

查看别名:alias(所有的), alias c rm(指定的)

删除别名:unalias c

2.3. 特殊符号

1. 分号(;) - 分隔同行的多条命令
echo one; echo two

2. 与号(&) - 跟在命令结尾,将该条命令放后台执行
# 即创建子 shell 执行命令,结束后将结果返回给当前 shell
sleep 10 &

3. ($?) - 上条命令的退出码(exit code),0 指上条命令执行成功;非 0 则失败
echo $?

4. 双与(&&) - 逻辑与,只有前条命令成功才会执行后条命令
echo one && echo two

5. (||) - 逻辑非,前条失败后条才执行
zecho one || echo two

6. && || 结合
rm file1 && echo success || echo failure
# 类似三目运算符 (? :)

7. 井号 - 表注释

8. 反斜线(\) - 转移字符
echo escaping \\\?\&
当放在行尾,表示改行未完,下一行继续接着写

9. 刀乐符($) - 获取变量的值(变量名大小写)
echo $PATH != echo $path

2.4. Shell 变量

- 创建变量
var=tom
echo "hello $var"  --> hello tom
echo 'hello $var'  --> hello $var
注意:双引号支持调用变量值,而单引号会原样打印

- 查看(set)与删除变量(unset)
set  --> show all variables
unset var  --> remove var

- $PS1(命令行提示符变量)

- $PATH(寻找执行命令的路径,类似于 WIN 的环境变量)
echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
# 以冒号来分割各个路径

- env(显示导出的变量,即子 shell 也能访问到的变量)

- export(导出变量)
name=tom
export name

- ${}, 为了让变量值与字符串拼接
prefix=Super
echo $prefixman  --> 空
echo ${prefix}man --> Superman

- unbound 变量, 即未定义的变量
echo $MyVar --> 空 # MyVar 未定义
set -u
echo $MyVar --> -bash: my: unbound variable 

2.5. 嵌套(embedding)和选项(option)

使用 $() 来完成 shell 嵌套,在当前的 shell 中临时开启一个子 shell,并将子 shell 的结果返回给当前 shell

echo $var  --> 空
echo $(var=7; echo var)  --> 7

# 利用嵌套查看目录信息而不跳转当前目录
echo $(cd test; ls -a)

用命令 set -o 来显示当前 shell 的选项配置

2.6. shell history

- !!(显示并重复上条命令)
echo hello
!!  --> 
echo hello
hello

- !xx(显示并重复以xx字符开头的命令)
# xx 字符串也可使用正则表达式来进行匹配

- history(查看在此之前所使用过的命令)
- history 10(查看上10条命令)
# 查看历史时,每条命令前还有个命令编号

- !n(显示并执行某条命令;其中 n 指某条命令编号)

- ctrl-r(进入历史命令的搜索模式)

- $HISTSIZE(当前shell 会话能在内存中记录的历史命令条数)
echo $HISTSIZE --> 1000

- $HISTFILE(历史命令所存放的文件)
echo $HISTFILE --> /home/username/.bash_history

- $HISTFILESIZE(离开 shell 会话后, bash_history 能够报存的命令条数)

- 在命令前加个空格,能防止该命令被记录

3. 管道(Pipes)和命令

3.1. I/O 重定向

shell 有三种基本的 I/O 流,分别是 stdin(stream 0)、stdout(stream 1) 和 stderr(stream 2)。

shell-io

其中,stdin 用来接收用户键盘的输入,而 stdout 发动输出结果、stderr 发送错误信息。

输出重定向(output redirection),将输出结果重定向到指定的路径,用大于号 > stdout 来代表重定向

echo hello  # 屏幕显示 hello
echo hello > hello.txt  # 将 hello 导向 hello.txt 文件中
# 注意,当 shell 看到 > 后,会先清空文件中的内容在完成重定向

set -o noclobber # 防止重定向重写已有文件

echo hello >| hello.txt
# 无视 noclobber,强行重定向

echo world >> hello.txt
# 重定向且将结果追加文件内容之后

错误重定向(error redirection),将 shell 的错误信息重定向到指定的路径。用 2> 来完成重定向

echoa hello # 屏幕显示错误信息
echoa 2> err.txt # 错误信息被存放在 err 文件中

2>&1 能够将输出流和错误流都导向同一文件中
ls > dirlist 2>&1 # 结果或错误都会存在 dirlist 中
ls 2>&1 > dirlist # 仅结果存在文件中,错误会显示在屏幕上


- 由于管道只接收输出流,所以当想对错误信息做筛选时
- 就需将错误流转为输出流(2>&1)交给管道再作处理

rm file1 file2 file3 | grep file2
rm: cannot remove 'file1': No such file or directory
rm: cannot remove 'file2': No such file or directory
rm: cannot remove 'file3': No such file or directory

rm file1 file2 file3 2>&1 | grep file2
rm: cannot remove 'file2': No such file or directory

# (&>) 合并输出流和错误流
# 让错误流和输出流都导向同一个流,与 2>&1 类似
# 只不过后者是将错误流转到输出流中,前者可以是其他的流
rm file2 &> out_and_err
echo hello &>> out_and_err

out_and_err:
rm: cannot remove 'file2': No such file or directory
hello

输入重定向(input redirection),将指定的路径作为输入流显示到屏幕上,使用小于号 < (0< 的缩写)来实现

cat < file.txt # 查看文件内容
cat > file.txt << EOF # 用cat创建文件并以 EOF 结束

# 快速清空文件
>filename
>|filename # 当开启 noclobber

3.2. 过滤器

由管道拼接成多个命令而成,更高效、优雅地解决问题。管道符 (|) 的作用是将前一个命令的输出作为后个命令的输入

1. cat 将标准输入作为标准输出打印
tac count.txt | cat | cat | cat 
# tac 将 count 文件的内容反序输出,经管道将其变为 cat 的输入,
# 而 cat 又将输入变为输出,以此下去最后输出到屏幕显示

---

2. tee 分叉,将一个输入传给多个输出,其中有一个是标准输出
tac count.txt | tee temp.txt | tac 
# tac 将 count 文件的内容反序输出并传给 tee
# tee 将输出传给 temp 文件和下个 tac
# 最后 tac 再将输入反序输出

---

3. ★ grep 从文本中筛选出符合所给字符串的行内容
cat tennis.txt | grep Williams
Serena Williams, usa
Venus Williams, USA

grep Williams tennis.txt # 不用 cat 的情形

# grep 的一些选项
grep -i  --> 匹配时忽略大小写
grep -v  --> 匹配不符合字符串的行
grep -vi  --> 匹配不符合的行且忽略大小写
grep -A1  --> 显示符合的行及它们的下一行(A: After)
grep -B1  --> 显示符合的行及它们的上一行(B:Before)
grep -C1  --> 显示符合的行及它们的上下各一行(C:Context)
# A1、B1、C1 的 1 可以也改为其他的数字

---

4. cut 切片,根据所给的选项对文件进行筛选
cut -d: -f1,3 /etc/passwd | tail -4
# -d 为分隔符,这里是以冒号为分隔符对文件划分
# -f 为字段或列,这里选择 1 和 3 列
# tail 显示 cut 的输出的后四行
cut -d" " -f1 tennis.txt
# 以空格作分隔符时,要加双引号
cut -c2-7 /etc/passwd | tail -4
# -c 为字符,这里选择 2 到 7 的字符
# 注意:必须要有 -f|-c|-b 选项,以指明类型

---

5. tr 将输入以给定的规则转换字符
cat tennis.txt | tr 'e' 'E'
# 将 tennis 文件中的 e 转为 E
tr 'e' 'E' < tennis.txt (无管道版)
tr 'a-z' 'A-Z' < tennis.txt  # 小写转大写
tr '\n' ' ' < tennis.txt  # 换行转空格
tr -s ' ' < tennis.txt  
# -s 为 squeeze 挤压,将连续出现的字符压缩为一个
# 如 [x][x][x][x] --> [x]
tr 'a-z' 'nopqrstuvwxyzabcdefghijklm'
tr 'a-z' 'n-za-m'
# 用 tr 完成 rot-13 简单加密,a 对 n 开头,以此下去
tr -d e < tennis.txt  # 删除输入的字母 e

---

6. wc 计算文本中有几行、几个词、几个字符
wc tennis.txt
  6  16 106 tennis.txt
# 第一列指行数、第二列指单词数、第三列指字符数
wc -l tennis.txt
# -l 单显示行数;-w 单词数;-c 字符数

---

7. sort 排序,默认以 ASCII 的顺序
sort tennis.txt
# 以ascii顺序排列
sort -k1 tennis.txt
# 以第一列或第一个单词的顺序为主
sort -n tennis.txt
# -n 指以数字的大小排序
sort -M month.log
# -M 以月份进行排序,可以用其查看一些日志文件内容

---

8. uniq 删除排序后有重复的行
sort count.txt | uniq
sort count.txt | uniq -c
# -c 统计重复行的数量

---

9. comm 比较两已排过序文件的内容
comm list1.txt list2.txt
# 共三列,其中第一列为 list1 独有的
# 第二列为 list2 独有的
# 第三列为 两者共有的
comm -12 list1.txt list2.txt
# 只显示第三列的结果,即忽略 1、2 列

---

10. sed(stream editor)使用正则对流的内容进行编辑
echo level5 | sed 's/5/42/'  --> level42
echo level5 | sed 's/level/jump/' --> jump5
# 对流的内容作替换
echo level1 level2 | sed 's/level/jump/g'
# g 为全局替换,否则只替换第一个
cat tennis.txt | sed '/usa/d'
# 从流中删除 usa

3.3. 基本的 Unix 工具

通过 Unix 的一些工具,能够快速查找文件、定位文件、压缩解压文件等

1. ★ find 查找文件,基于目录树从左到右以此遍历查找符合规定的文件
find /etc > etcfiles.txt
# 在 /etc 路径下,依次遍历所有的文件,并将结果存到 etcfiles 文件中
find /etc /home /user
# 给出多个查找路径,将查询结果合并输出
find . -name "*.conf"
# 在当前目录下,查找文件名以 .conf 做后缀的所有类型的文件
# -iname 查找文件名且忽略大小写
find . -type f -name "*.conf"
# -type 文件类型,f 为一般文件;l 为链接文件;d 目录
find / -name "*.pdf" 2>./err
# 2>./err 能够将查找时的错误信息存到文件中,从而避免造成显示混乱
find . -newer etcfile.txt
# 查找比 etcfile 后创建的文件
find . -name "*.txt" -exec cp {} ./copyfile \;
# 将当前目录下的所有 txt 文件复制到 copyfile 文件夹下
# -exec command {} \; 指对查找后的结果执行命令
# 其中 {} 指查找结果的路径名,\; 为逗号的转义字符
# -ok 与 -exec 类似,只不过它会执行命令前让用户确认

# -exec 是对每条结果都执行一次命令,但当数量过多后效率就不高
# 通过管道和 xargs 程序来处理会更加高效
# xargs 能够将标准输入转为命令行参数,下面是它与 find 的搭配
find . -type f | xargs ls -s
# 将查询结果作为 ls 命令的参数依次执行
find ~ -type f -mtime +365 | xargs grep "Tom"
# 查找 home 目录下文件修改时间超过一年的且其中包含 Tom 的行
find . -type f | xargs -i mv {} ./backup/{}.old
# -i insert 指多次发送参数给命令,{} 为占位符
# 将当前文件移动到 backup 目录下,且每个文件名加上 old 后缀
# 自定义占位符,-iXX 即以 XX 作占位符

# xargs 的一些选项:
# -n1 按一列显示结果
# -t 先打印命令再执行
# -d 分隔符,默认为回车
# -p prompt 每次执行命令前给与提示
# -r 当输入为空时,不执行命令

---

2. locate 定位文件的位置
locate pwd  # 显示文件路径中包含 pwd 
locate /etc/sh  # 显示 etc 目录下以 sh 开头的文件 | -i 能忽略大小写

---

3. date 显示当前日期
date  # 以默认格式显示当前日期
date +'%Y-%m-%d %A'  --> 2022-07-18 Monday
# 自定义日期格式,%Y年 %m月 %d日 %A星期 %H小时 %M分钟 %S秒

---

4. cal 日历
cal #显示当前月的日历
cal -y  # 当年的日历
cal 4 1999  # 1999年4月的日历

---

5. sleep 暂停等待,脚本中会用到
sleep 5  # 等待 5 秒

---

6. time 查看某命令执行所需的时间
time date # date 命令执行时间

---

7. gzip 压缩文件,后缀为 .gz
gzip text.txt  --> text.txt.gz
# 对单个文件进行压缩,以节省空间
gunzip text.txt.gz  # 解压缩

# 值得注意的是,我们理解的压缩过程是将某个文件夹压缩,或多个文件压缩
# 它其实包含两个过程:打包和压缩
# 打包是将多个文件或文件夹打包成一个文件
# 压缩是通过压缩程序以缩减文件的大小

即最常用的压缩命令
tar -zcvf demo.tar.gz ./demo/
# 将 demo 文件夹压缩,-z 使用 gzip 压缩程序
# -c 创建打包文件 -v 显示过程 -f 使用打包文件
tar -zxvf demo.tar.gz
# 解压缩文件

---

8. rename 批量重命名
rename 's/.txt/.jpg/' *
# 将所有文件中,以 txt 后缀的文件改为 jpg 文件
rename 's/.txt//' *.txt
# 将所有的 txt 文件,删除其 txt 后缀
rename 'y/a-z/A-z/' *
# 将所有文件名改为大写
rename -n 's/$/.bar/' *
# 给所有文件添加 bar 后缀,-n 显示重命名的过程但不执行

4. Shell Script

shell 脚本就是命令的集合,执行脚本就是去执行它一行行的命令。

1. hello world 示例
echo echo hello world > hello_world  # 写 hello_world 脚本
chmod +x hello_world  # 将脚本文件改为可执行的,除非该脚本目录被添加到 PATH 中
./hello_world  # 执行脚本

---

2. she-bang 指定脚本的解释器
#!/bin/bash  --> 首行
cat /etc/shells  # 查看可用的 shell

---

3. 变量
var1=2  # 定义变量,等号两边无空格
echo var1 = $var1

# 脚本内的变量默认不能被外部的 shell 使用
# 但通过 source,可将变量导入外部 shell 中
source hello_world
. ./hello_world  # 上面的简写

# 三种运行脚本的方法
chmod +x filename;./filename  # 加权限直接运行
source filename  # 导入变量运行
bash -x filename  # 创建一个子 shell 运行,-x 显示执行命令的过程

---

4. 条件和循环
条件判断 test or []
test 10 -gt 20; echo $?  --> 1
# 判断 10 是否大于 20,1 指前条命令的错误码,非零表示失败,即 false
[ 10 -gt 20 ]; echo $?  # 使用 [] 做判断,内部两边要有空格
[ 10 -lt 20 ] && echo true || echo false
# 将判断结果变为人可读型
[ 10 -gt 20 -a 5 -lt 6 -o 7 -ge 9 ] && echo true || echo false
# -a: and | -o or

# 注意,脚本的数值比较不可用数学符号表示
# -gt(>): great than | -lt(<): less than | -eq(=): equal
# -ge(>=): great equal | -le(<=): less equal | -ne(!=): not equal

# 一些选项
# [ -d foo ]: 是否存在 foo 文件夹
# [ -e bar ]: 是否存在 bar 文件
# [ -f foo ]: foo 是否为常规文件
# [ -r bar ]: bar 是否为可读文件
# [ '/etc' = $PWD ]: PWD 的值是否为 /etc
# [ $1 != 'secret' ]: 第一个参数值不为 secret?
# [ foo -nt bar ]: foo 文件晚于 bar 文件创建?
# 注意,字符串的比较是使用数学符号表示

# if-else 条件判断
if [ -f init.txt ]
then
  echo init.txt is exists!
else
  echo init.txt not found!
fi

# if-else-if 条件判断
if [ condition1 ]
then
  do-something
elif [ condition2 ]
then
  do-something
else
  do-something
fi

# case 分支语句
case $var in
  case1)
    do-something
  ;;
  case2)
    do-something
  ;;
  *)
    do-something
  ;;
esac  

# for 循环
for counter in 1 3 4 7 8 10 
do  # counter 依次为 1 3 4 7 8 10 执行命令
  echo $counter
done
数字依次遍历
for counter in `seq 1 20`
# 使用命令嵌套,从 1 取到 20,seq 打印一系列数字
for counter in {1..20}
# 嵌套命令的简写

# while 循环
counter=1
while [ $counter -le 10 ]
do
  echo now counter is $counter
  let counter++
done
# 死循环:while true 或 while : do ...

# 计算当前目录下以 .txt 结尾的文件
counter=$(find . -type f | grep ".txt$" | wc -l)
for filename in $(find . -type f | grep ".txt$")
do
  echo $filename
done
# 其中,通过命令嵌套可以将命令的结果赋给 shell 变量

---

5. 脚本参数
脚本中使用一些符号来显示相关的参数信息
# $1, $2, $3.. 获取脚本后跟的参数值
# $0: 获取脚本文件名
# $$: 脚本执行的 PID | $#: 脚本的参数的个数
# $?: 脚本执行后的返回码 | $*: 脚本的所有参数

#!/bin/bash                      | ./pars one two three
echo The first argument is $1  --> The first argument is one   
echo The second argument is $2 --> The second argument is two
echo The third argument is $3  --> The third argument is three
echo \$ $$ PID of the script   --> $ 5610 PID of the script
echo \# $# count arguments     --> # 3 count arguments
echo \? $? last return code    --> ? 0 last return code
echo \* $* all the arguments   --> * one two three all the arguments

shift 遍历所有参数
脚本:
if [ "$#" == "0" ]
then
  echo You have to give at least one parameter.
  exit 1
fi
while (( $# ))
do
  echo You gave me $1
  shift
done

使用:
./shift.ksh one two three 1201 "33 42"

输出:
You gave me one
You gave me two
You gave me three
You gave me 1201
You gave me 33 42
# shift 指参数左移,一个一个地被截断
# 如:one two three four five | 参数个数 5
#     two three four five         4
#     three four five             3
#     four five                   2
#     five                        1
#     空                          0

---

6. 获取用户输入
read varname  # 使用read获取用户的输入值

获取某文件的变量值
- myApp.conf
number=5

- myApp.bash
#!/bin/bash
. ./myApp.conf
echo The number is $number
# 通过在脚本内 source 某文件,来获取其中的变量值

获取命令选项参数,使用 getopts "optstring" var
optstring 列出了对应的脚本可以识别的所有选项值。比如:
如果 Shell Script 可以识别-a,-f以及-s选项,则 optstring 就是 afs;
如果对应的选项后面还跟随一个值,则在相应的 optstring 后面加冒号。
比如,a:fs 表示a参数后面会有一个值出现,-a value 的形式。
另外,getopts 执行匹配到a的时候,会把 value 存放在一个叫 OPTARG 的 Shell Variable 当中。
如果 optstring 是以冒号开头的,命令行当中出现了optstring 当中没有的参数将不会提示错误信息。

var 表示的是参数的名称,每次执行 getopts,会从命令行当中获取下一个参数,然后存放到 var 当中。
如果获取到的参数不在 optstring 当中列出,则 var 的值被设置为 ?。
如果获取到的选项在其中,但后面未跟参数,则将 var 的值设置为 :
命令行当中的所有参数都有一个index,第一个参数从1开始,依次类推。
另外有一个名为 OPTIND 的 Shell Variable 存放下一个要处理的参数的 index。

# 脚本
while getopts ":af:z" option;
do
  case $option in
    a)
      echo received -a
    ;;
    f)
      echo received -f with $OPTARG
    ;;
    z)
      echo received -z
    ;;
    :)
      echo "option -$OPTARG needs an argument"
    ;;
    *)
      echo "invalid option -$OPTARG"
    ;;
  esac
done

# 执行
> ./argoptions.ksh -a -f hello -z
received -a
received -f with hello
received -z
> ./argoptions.ksh -zaf 42
received -z
received -a
received -f with 42
> ./argoptions.ksh -zf
received -z
option -f needs an argument

---

7. eval 命令
格式:eval [argument]
作用:先将参数解析,再将其作为输入传给 shell

示例
cmd="cat script"
eval $cmd  # 相当于让 shell 执行 cat script

---

8. (( xxx )) 允许使用数学符号作判断
(( 20 > 34 )) && echo true || echo false
(( 42 == $var42 )) && echo true || echo false
# 支持常规 for 循环形式
for (( i=0;i<10;i++ ))

---

9. let 命令
让 shell 完成算数表达式的计算
> let x="10*2+100/10" ; echo $x  ---> 30
或者作进制转换
let x="0xfff"; echo $x  --> 4095
let x="2#1011"; echo $x  --> 11
let x="8#011"; echo $x  --> 9
let x="16#10"; echo $x  --> 16
# let 会转为十进制再显示;而直接赋值就显示原进制

---

10. 脚本函数,一组逻辑命令
function hello {  # 定义函数
  echo hello, $USER
}

echo We will call a function
hello   # 函数调用
echo The End

output:
We will call a function
hello, tom
The End

给函数传参数,直接再函数名后跟参数即可,跟命令一样
hello tom
函数内部用 $1, $2..来获取参数值

5. 用户管理

1. 查看用户信息的命令
whoami  # 显示你的用户名
who     # 查看登录过该系统的用户
w       # 查看登录过的用户以及他们正在干吗
su username  # 切换到 username 的用户
su or su -   # 没有跟用户名则默认为 root
sudo+cmd     # 以系统管理员的身份执行命令
sudo su -    # 切换到 root 
# 说明:由于 Ubuntu 的 root 没有 passsword set,所以用户是无法通过 su 切换到 root 

---

2. 用户的增删改查,以下命令要以 root 权限执行
useradd -m new_user   # 创建新用户,-m 为其创建家目录,-d 指定家目录,-c 添加描述
passwd new_user       # 给新建的用户设置密码

usermod -l new_user   # 更改用户名,-s 能更改shell
usermod -md /data/new_home username  # 更改用户的家目录为 /data/new_home

userdel username     # 删除用户,-r 会删除其相关目录及文件(慎用)

users   # 查看用户

# 在 ubuntu 下,新用户的 shell 默认使用 sh,可在 /etc/passwd 中查看
# 通过 root 权限,将 /etc/passwd 中用户的 shell 改为 /bin/bash 即可
# 或者通过 "chsh -s /bin/bash" 来修改用户 bash 
# /etc/passwd 文件七个字段:用户名:x:用户id:组id:用户描述:家目录:使用的 shell

---

3. 用户密码
/etc/shadow 文件存放用户加密后的密码,只可被 root 读取
它有 9 个字段,从左到右分别为 用户名:加密密码:密码最近修改时的天数(1970.01.01为第一天)
:密码必须保持不变的天数:密码到期的天数(1970开始):密码到期前多少天警告
:密码到期后多少天后停用该用户:用户被禁用的日期(1970开始)

---

4. openssl passwd 命令
openssl passwd hello  # 将 hello 字符串加密,即生成对应的 hash 值,并返回加密后的密文
# 该命令每次生成的密文都会不同
openssl passwd -salt 12 hello
# -salt 指定 hash 值,这里密文的前两位是 '12' 随后是 hello 的密文
# 由于指定了 salt,那就不会随机生成密文了,即生成的密文变为固定的
useradd -m -p $(openssl passwd hunter2) mohamed
# 创建 mohamed 用户,并 -p 添加密码和嵌套命令生成密文
# 但此用户的密码会暴露在命令中,也能够在 cmd history 中查到,使用安全性要求不高的场所
openssl passwd -salt '123456'
# 创建一个加密密码,执行命令后会让用户输入密码,然后显示密文密码。这里的 123456 并非密码而是密码的长度,共长六位
# 可以将生成的密文密码手动添加到 /ect/shadow 中,即可作为用户的登录密码

---

5. 第三种加密的方法 - 通过自定义的 C 文件使用 crypt 函数
如 MyCrypt.c 的内容如下:
#include <stdio.h>
#define __USE_XOPEN
#include <unistd.h>
int main(int argc, char** argv)
{
  if(argc==3)
  {
    printf("%s\n", crypt(argv[1],argv[2]));
  }
  else
  {
    printf("Usage: MyCrypt $password $salt\n" );
  }
  return 0;
}
接着用 gcc 编译:gcc MyCrypt.c -o MyCrypt -lcrypt
使用如下:
-$ ./MyCrypt hunter2 44 
44O9hdkJcm7IA
-$ ./MyCrypt hunter2 43 
433fTQVL/x3cg

---

6. 密码日期 
grep PASS /etc/login.defs
# 查看 /etc/login.defs 文件中有关 password 的设置
如:*********************
#       PASS_MAX_DAYS   Maximum number of days a password may be used.
#       PASS_MIN_DAYS   Minimum number of days allowed between password changes.
#       PASS_WARN_AGE   Number of days warning given before a password expires.
PASS_MAX_DAYS   99999
PASS_MIN_DAYS   0
PASS_WARN_AGE   7
#PASS_CHANGE_TRIES
#PASS_ALWAYS_WARN
#PASS_MIN_LEN
#PASS_MAX_LEN
# NO_PASSWORD_CONSOLE
*********************************

chage 命令 - 设置用户密码的相关日期
chage -l qiang # 显示用户密码日期项
Last password change                                    : May 19, 2022
Password expires                                        : never
Password inactive                                       : never
Account expires                                         : never
Minimum number of days between password change          : 0
Maximum number of days between password change          : 99999
Number of days of warning before password expires       : 7

chage -d 2022-06-30 qiang   # 设置用户最后一次修改密码的日期为 2022 年 6 月 30 日
chage -m 5 qiang  # 从最近修改密码的日期开始的 5 天内,用户不能再次修改密码
chage -M 8 qiang  # 用户自修改密码的日期开始,修改后的密码将在 8 天后过期
chage -W 8 qiang  # 用户密码过期前 8 天内,系统持续发出警告

---

7. 锁死密码
在 /etc/shadow 文件中,第二个字段为用户的加密密码,若它以 ❗ 开头,
那么就表明用户的密码被锁死,用户就不能使用该密码进行登录
sudo grep qiang /etc/shadow | cut -c1-70 # 查看用户及加密密码
usermod -L qiang  # 锁死用户密码,再查看时就会在加密密码前出现 !
usermod -U qiang  # 解锁用户密码

---

8. 群组(group) - 在 root 权限下执行
groupadd groupname # 创建一个空组
/etc/group  # 存放组信息的文件
groups  # 查看当前用户所在的组
usermod -a -G groupname username # 将用户添加到组中,必须带上 -a 否则该用户之前所添加的组都会被删除
groupmod -n oldname newname # 修改组名
groupdel groupname  # 删除组

gpasswd -A tom sports  # 指定 tom 用户为 sports 组的管理员
gpasswd groupname  # 若当前用户不在组中,可用该命令将其暂时加入到组中
gpasswd jerry sports  # tom 管理员将 jerry 用户拉入组中

6. 文件安全(file security)

1. 文件所有权
~/owners$ ls -lh
total 636K
-rw-r--r--. 1 paul snooker 1.1K Apr 8 18:47 data.odt
-rw-r--r--. 1 paul paul 626K Apr 8 18:46 file1
-rw-r--r--. 1 root tennis 185 Apr 8 18:46 file2
-rw-rw-r--. 1 root root 0 Apr 8 18:47 stuff.txt
# 再列出文件时,就会看到每个文件的用户所有权和组所有权
# 如 file2 文件,root 为用户所有权,tennis 为组所有权

cut -d: -f1 /etc/passwd | column
# 显示所有用户
chgrp username filename  # 修改文件的组所有权
chown username filename  # 修改文件的用户所有权
chown user:group filename # 修改用户和组的所有权

ls -l 结果的第一列的第一个字符表示该文件的类型
-:为普通文件 | d:目录 | l:符号连接 | p:管道 | b:block device | c:字符设备 | s:套字节

---

2. 文件权限 - rwx
ls -l 输出的第一列后九个字符表示该文件的权限
r:read 可读 | 对文件能 cat | 对目录能 ls
w:write 可写 | 文件能 vim | 目录能 touch
x:execute 可执行 | 文件能运行 | 目录能进入
其中九个字符三个为一组,分表代指所属用户权限、所属组所有用户的权限、其他的用户权限

修改用户权限 - chmod
chmod u+x filename  # 给该文件的所属用户添加执行权限
chmod o-r filename  # 删除该文件其他用户的可读权限
chmod a+w filename  # 给三类用户都添加可写权限,可省去 a,默认为全部
chmod u=rw,g=rw,o=r filename  # 指定三类用户具体的权限
另一种用八进制表示权限的方法
chmod 777 filename  # 给三类用户 rwx 权限,7 - 111(八进制)
chmod 654 filename  # 6(110)5(101)4(100) 即 u=rw, g=rx, o=r

umask # 显示八进制形式显示默认创建文件或目录的权限
umask -S  # 以字符显示权限
mkdir -m 777 mydir  # 创建目录时给权限

目录的粘滞位(sticky bit) 即 other 的 x 位,有时也会是 t(有x权限) 或 T(无x权限)
它能够防止目录中的文件被非文件的用户所有者删除或移动,仅 root 用户和文件所属者才能
ls -ld /tmp  # 可以看到 /tmp 文件就有粘滞位
目录的 setgid,即 group 的 x 位,也能为 s 或 S
它能够让目录中文件的组所属继承自目录的组所属
ls -ld /var/local  # 可看到 local 目录有 setgid
文件的 setuid,即 user 的 x 位,也能为 s 或 S
它能够让普通用户执行 root 用户下的文件,虽然这样很危险
ls -l $(which sudo)  # sudo 命令就有 setuid

---

3. inodes
当文件存到 disk 时,不仅仅存的是文件的内容,还有文件名、创建日期、所属者、所属组、文件权限等信息。
除了文件名和文件内容外,其他信息均存到 inode 中,inode 是一种数据结构,包含文件的元数据。
inode table 即存放所有 inode 的表,可以使用 df -i 查看有多少 inode 被使用
inode number 每个文件都有个单独的 number,类似于 id,可用 ls -li 显示文件的 inode
当修改文件内容时,文件的 inode 就会更改
目录是一种特殊的文件,它包含文件名与文件 inode 的映射关系

- 硬链接(hard link)
ln test.txt hardlink_to_test  # 在当前目录下生成 test 文件的硬链接文件
# 硬链接与原文件具有想用的 inode、权限、内容,即两者是相同的
# 可以理解硬链接是原文件的副本,即使删除原文件硬链接仍存在
find / -inum 817270 2> /dev/null  # 通过 inode 搜索所有对应的文件

- 符号链接/软链接(symbolic link)
ln -s test.txt symbolink_to_test  # 在当前目录生成 test 的软链接文件
# 软链接与原文件没有相同的 inode,软链接存的是目标文件的路径
# 软链接就像是 windows 中的快捷方式,仅仅是导向原文件的指针

使用 rm 命令删除链接文件

7. linux 网络

- 监测和检查网络
1. ping www.baidu.com  # 给指定主机发送数据包以检验网络是否正常

2. traceroute 命令(需先安装工具) - 追踪数据包在网络上的传输时的全部路径
traceroute www.baidu.com

3. netstat 查看各种网络设置和统计数据,根据不同选项完成不同功能
netstat -ie  # 类似于 ifconfig,查看网络接口信息

4. ifconfig 命令
ifconfig  # 查看与配置网络状态命令, 与 windows 的 ipconfig 类似
ifconfig eth1  # 查看指定网卡的网络状态

5. ip 命令 - ifconfig 的加强版
ip address show  # 显示 ip 地址
一些简写:ip addr show | ip addr | ip a
ip -4 addr | ip -6 addr # 仅显示 ipv4 或 ipv6 
ip addr show dev eth1  # 显示指定网卡 ip 信息
ip link show  # 显示系统的已安装的网卡

---

- 网络中传输文件
1. ftp 命令 - 基于 ftp 协议让本地主机与远程主机进行文件传输
ftp [远程主机名或 ip]   # 连接到远程主机
# 连接后,可输入 help 查看 ftp 所支持的命令,help cmd 查看某个命令的描述
# 如:ls 显示文件;get 下载文件到本地;put 上传文件到主机;quit/bye 退出 ftp

2. lftp - 升级版 ftp,支持多协议如 http,提供一些便捷功能

3. wget 命令 - 从指定网站下载文件
wget https://wangchujiang.com/linux-command/c/wget.html
# 下载 html 文件到本地

8. 进程 - 即一个正在执行的程序

8.1. 内核管理进程

当进程被创建时,内核会分配一个唯一标识号,即进程 ID(PID),为了管理这些进程内核会有个进程表来跟踪所有进程。 由于进程会共享系统资源(处理器、内存、IO、网络连接等),所以内核提供了一个调度器(scheduler)来管理进程占用资源。调度器每次会选择一个进程,给予短暂的时间片来执行程序,时间一到就会做一次快照保存进程的数据,直到下次该进程运行时再接着执行。

8.2. 进程的系统调用

当进程需要内核提供执行的服务时,就会用系统调用发送请求。 创建和使用进程的系统调用:fork、exec、wait和exit fork 系统调用:创建当前进程的副本,原进程为父进程,副本为子进程 wait 系统调用:强制进程暂停一会,直到另一个进程执行完 exec 系统调用:改变正在运行的程序 exit 系统调用:终止进程 kill 系统调用:想另一个进程发信号

控制文件 IO 的系统调用 open:打开一个用于读/写的文件 read:从文件中读取数据 write:向文件中写入数据 close:关闭文件

之前了解到 shell 中的命令分为内置命令和外部命令,内置命令直接由 shell 解析执行,而外部命令则必须创建新进程执行。下面就是外部命令的执行过程:首先,shell 用 fork 创建新进程;然后子进程用 exec 执行程序变为外部命令的执行程序;接着父进程用 wait 等待子进程执行完外部程序;外部程序结束,子进程用 exit 终止进程。

当进程终止后,其所占用的资源被释放,以便其他进程使用。终止后的进程称僵尸进程(zombie),进程表中仍会存有僵尸进程的条目

8.3. 孤儿进程和废弃进程

孤儿进程:当进程 fork 后,父进程意外死亡,那么该子进就为孤儿进程 废弃进程:fork 后,父进程并未等待子进程死亡,那么子进程就为废弃进程 对于孤儿进程,Unix 中会有一个 init 进程(孤儿所)收养孤儿进程,来清除僵尸进程

8.4. 特殊进程

PID #0 :空闲进程,简单的程序不做任何事,在没有其他进程执行时被执行 PID #1 :初始化进程(init),在空闲进程初始化内核所需数据后从空闲进程 fork 出,设置内核、打开系统控制台、挂载根文件系统、运行必要的 shell 脚本。永不终止,直到系统关闭

8.5. 守护进程

在后台运行的程序,不与终端连接,只提供服务(类似 Windows 任务管理器上的服务) ps 显示中 TTY 列为 ?的就表示该进程为守护进程,如 init 进程

8.6. 作业(Job) - 即一整条命令

与进程类似,它也有 ID 和作业表,但与进程还是有区别的,进程由内核控制;作业由 shell 控制 如 who | cut -c 1-8 | sort | uniq -c 该命令生成 4 个不同的进程,但只生成 1 个作业 再如 date; who; uptime; cal 该命令生成 4 个进程,也生成 4 个作业

作业有三种状态:前台作业、后台作业、暂停挂起 暂停前台作业 – Ctrl+Z 将该作业暂停挂起 恢复挂起作业 – fg 后台作业 – 在命令后加 &

suspend  # 将当前 shell 挂起,用 fg 恢复。可以用其来作 shell 的切换

jobs # 显示所有作业 -l 显示对应作业的 PID
+ 指当前作业;- 指前一个作业

fg 将当前作业移至前台
fg %n 将 #n 作业移至前台
fg %name 将指定命令名的作业移至前台
%+ | %n | %name  简写

bg 将当前作业移至后台
bg %2 %5 %6 将多个作业移至后台

---
1. 监测进程(任务管理器)
top
ps(process status)
ps axo pid,comm,pcpu # 查看进程的PID、名称以及CPU占用率 
ps -l
ps -aux | grep named(依据进程名检索)
pstree  # 查看进程数  -np 显示每个进程的 PID

2. 杀死进程
kill pid
kill -9 pid (-9 指强制杀死)

3. 挂载(挂载必须是一个分区)
sudo fdisk -l  # 查看磁盘分区
sudo fdisk /dev/xx  # 使用 fdisk 操作指定分区
# 然后显示:Command (m for help):  (输入对应的字符进行操作)
sudo mkfs -t ext3 /dev/xx  # 给编辑好的分区创建文件系统,-t 指定文件系统类型为 ext3

sudo mount /dev/xx /mnt/
           分区路径 要挂载的目录
sudo umount /mnt  # 取消挂载

4. df 显示分区及所挂载的目录
df -h # 文件大小人类可读

5. 创建镜像文件
dd if=/dev/xx of=ubuntu.iso  # 将指定分区的数据拷贝到 iso 文件中,
# if 指输入文件 of 指输出文件
genisoimage -o file.iso -R -J ~/test
# 将 test 目录下的数据拷贝到 iso 文件中, -R -J 为标准

9. 包管理系统

用来安装、更新、卸载软件

dpkg - Debian 系
sudo apt install package_name  # 安装软件包
sudo apt update  # 查看所安装软件包最新的更新信息
sudo apt upgrade  # 将软件包更新到最新版本
sudo apt remove package_name  # 卸载软件包

yum - CentOS、RedHat 系
sudo yum install package_name  # 安装
sudo yum update  # 查看更新
sudo yum upgrade # 一键更新
sudo yum remove package_name  # 卸载