# 文件处理

# 查看文件

# ls

$ ls [path]     # 列出指定目录(默认为当前目录)下的各个文件名(不包括隐藏文件)
      -a        # 显示所有文件(包括隐藏文件)
      -A        # 与 -a 相比,不显示相对目录 . 和 ..
      -l        # 显示文件的详细信息
      -h        # 以人类容易阅读的单位显示文件大小
      -d        # 只显示目录本身,不显示目录下的内容
      -i        # 显示文件指向的 inode 号
      -Z        # 增加显示一列安全上下文,格式为 user:role:type:level
  • 例:
    [root@CentOS ~]# ls -al
    total 64
    dr-xr-x---.  7 root root   4096 Nov 27 19:17 .
    dr-xr-xr-x. 18 root root   4096 Oct 10 02:49 ..
    -rw-------   1 root root   6318 Dec  1 15:44 .bash_history
    -rw-r--r--.  1 root root     18 Dec 29  2013 .bash_logout
    -rw-r--r--.  1 root root    176 Dec 29  2013 .bash_profile
    -rw-r--r--.  1 root root    176 Dec 29  2013 .bashrc
    
    • total 表示已显示的这些文件占用的磁盘 block 总数。
    • 第一列,比如 dr-xr-x---. ,开头的 d 表示文件类型为目录,其后的 r-xr-x--- 表示文件的访问权限,末尾的 . 表示该文件使用了 SELinux 。(如果使用了 ACL 就会变成 + )
    • 第二列的数字表示指向该文件的 inode 的硬链接数。
    • 第三列、第四列表示文件所有者、所有者所属用户组。
    • 第五列表示文件的大小(单位为 Byte)。如果该文件是目录,则不包括目录中各文件的大小。
    • Nov 27 19:17 表示文件的最后修改时间。
    • 最后一列表示文件名。
  • 文件路径中,支持用通配符 ? 匹配单个字符,用通配符 * 匹配任意个字符(包括零个)。如下:
    ls /etc/*-release
    

# du

$ du [dir]...  # 显示指定目录(默认为当前目录)及其子目录下的各个文件占用的磁盘空间
      -a       # 增加显示非目录文件
      -h       # 以人类容易阅读的单位显示文件大小
      -d n     # 最多显示 n 层子目录
      -s       # 只显示该目录占用的总磁盘空间
  • 例:
    du -ahd 1   # 显示当前目录下各个文件的大小,像 Windows 风格
    

# tree

$ tree [目录]...   # 按树形结构显示某个目录(默认为当前目录)下的所有文件
       -a          # 增加显示隐藏文件
       -C          # 采用不同颜色显示不同类型的文件
       -L <n>      # 最多显示到第 n 层子目录
       -D          # 显示每个文件的最后修改时间
       -p          # 显示每个文件的访问权限
       -u          # 显示每个文件的所有者
       -sh         # 以人类容易阅读的单位显示文件大小
  • 安装: yum install tree

# lsof

$ lsof             # 显示目前所有被进程打开的文件
      <filename>   # 显示打开指定文件的所有进程
      -p <pid>     # 显示指定进程打开的所有文件
      -u <uid>     # 显示指定用户打开的所有文件

      -i           # 只显示 socket 文件
      -i :22       # 只显示指定端口的 socket 文件
  • 安装:yum install lsof

# 文件路径

# basename

$ basename <path>     # 获得一个路径的最后一段的名字
           [suffix]   # 去掉后缀
  • 不会检查输入的路径是否实际存在。
  • 例:
    [root@CentOS ~]# basename /root/test.py .py
    test
    

# dirname

$ dirname <path>...   # 获得路径所属的目录
  • 如果输入的路径不包含 / ,则视作位于当前目录。
  • 例:
    [root@CentOS ~]# dirname f1 /tmp
    .
    /
    

# realpath

$ realpath <path>...    # 将路径转换成绝对路径
  • 例:
    [root@CentOS ~]# realpath f1 /tmp
    /root/f1
    /tmp
    

# 查找文件

# find

$ find [path]                   # 显示指定目录(默认是当前目录)下的所有文件(包括子目录)
            -exec echo {} \;    # 对每个找到的文件名(用 {} 表示)执行一条命令,该命令以 \; 结尾

            # 可以添加以下选项作为筛选条件
            -maxdepth 1         # 限制搜索的目录深度(默认为无限深)
            -type f             # 限制文件类型,类型可以是 f、d、l、s、c 等

            -name *.py          # 筛选文件名,采用 shell 风格的匹配
            -iname *.py         # 匹配时不区分大小写(insensitive)
            -path */test/*      # 筛选文件路径
            -ipath */test/*
            -regex .*py         # 筛选文件路径,采用正则匹配
            -iregex .*py

            -user root          # 属于指定用户的文件,可以使用 uid
            -group root         # 属于指定用户组的文件,可以使用 gid
            -perm <mode>        # 文件权限等于 mode ,mode 取值可以为 0644、644、u=rw-,g=rw,o=r 等格式,按二进制位进行匹配
            -perm -<mode>       # 文件权限大于 mode

            -size -10k          # 小于 10k 的文件
            -amin -3            # 过去 3 分钟之内被 access 的文件(同理还有 cmin、mmin )
            -atime -3           # 过去 3 天之内被 access 的文件(同理还有 ctime、mtime )
  • 筛选条件为数值类型时,有三种表示方式:
    n     # 等于
    +n    # 大于
    -n    # 小于
    
  • -size 选项可以使用以下单位:
    b     # blocks ,默认采用这种
    c     # bytes
    k     # kilo bytes
    M     # Mega bytes
    G     # Giga bytes
    
  • 例:
    [root@CentOS ~]# touch 1.py
    [root@CentOS ~]# ls -al
    total 8
    drwxrwxr-x.  2 root root   28 Jan  8 11:10 .
    drwx------. 14 root root 4096 Jan  8 10:52 ..
    -rw-rw-r--.  1 root root    0 Jan  8 11:10 1.py
    [root@CentOS ~]# find . -name *.py
    ./1.py
    [root@CentOS ~]# find . ! -name *.py            # 在筛选条件之前加冒号 ! 则表示反选
    .
    [root@CentOS ~]# find . ! -name *.py ! -name .  # 可以重复使用同一种筛选条件
    
  • 例:查找并删除文件
    find . -name *.log -exec rm -f {} \;
    find . -name *.log | xargs -n 1 rm -f
    

# locate

$ locate [pattern]  # 显示文件路径包含 pattern 的所有文件
         -i         # pattern 不区分大小写
         -r         # pattern 采用正则表达式
  • 安装: yum install mlocate
  • locate 命令会从数据库 /var/lib/mlocate/mlocate.db 中查找文件,速度较快。该数据库一般每天更新一次,执行命令 updatedb 可立即更新。

# 移动文件

# cp

$ cp <源路径> <目标路径>  # 将文件拷贝到目标路径
     -a                  # 相当于-dpr
     -d                  # 若源文件为 link file ,则复制 link file 的属性(而非文件本身)
     -p                  # 将源文件的权限、属性也拷贝过去(否则会使用默认属性)
     -r                  # 递归操作(用于复制目录)

     -f                  # 若目标文件已存在,则删除它再尝试拷贝
     -i                  # 若目标文件已存在,则提示用户是否进行覆盖
     -n                  # 若目标文件已存在,则不覆盖
     -u                  # --update ,当目标文件不存在,或源文件比目标文件更新时,才执行拷贝
     -v                  # 显示执行过程的详细信息
  • 系统一般设置了 alias cp='cp -i' ,所以-f 选项无效。
  • 例:
    cp f1 f2
    cp f1 f2 /tmp
    cp -r *  /tmp   # 源路径中包含通配符 * 时,不会拷贝隐藏文件
    cp -r .  /tmp
    cp -a *  /tmp
    
  • 执行 cp f1 f2 时,
    • 如果 f2 不存在,则会先创建一个空的 f2 文件,再将 f1 的内容拷贝过去。
    • 如果 f2 已存在,则会先将 f2 的内容清空,再将 f1 的内容拷贝过去,因此目标文件依然使用 f2 的 inode 号。 如果该过程中有其它进程也在修改 f2 ,则会导致 cp 出错。

# ln

$ ln <源路径> <目标路径>  # 创建文件的硬链接
     -s                  # 创建文件的软链接
     -f                  # 若目标文件已存在,则覆盖它
  • 创建链接时,源路径、目标路径都必须是绝对路径。
  • 例:
[root@CentOS ~]# ln -s /tmp tmp
[root@CentOS ~]# ls -lh
total 0
lrwxrwxrwx 1 root root 4 Jan  8 14:21 tmp -> /tmp
[root@CentOS ~]# rm -f tmp/               # 如果软链接文件指向一个目录,删除时不能加上 / 后缀,否则会被视作删除目标目录
rm: cannot remove ‘tmp/’: Is a directory
[root@CentOS ~]# rm -f tmp

# rename

$ rename <源字符串> <目标字符串> <文件>...   # 替换文件名中的字符串
         -v                                # 显示每个被处理的文件
  • rename 命令主要用于批量重命名文件,不能像 mv 命令一样移动文件。

# mv

$ mv <源路径>... <目标路径>  # 移动文件(也可以移动目录)
     -f                     # 若目标文件已存在,则覆盖它
     -i                     # 若目标文件已存在,则提示用户是否进行覆盖
     -u                     # 当目标文件不存在,或源文件比目标文件新时,才执行移动(即 update)
  • mv 命令可以用于移动文件,也可以用于重命名文件(此时源路径只能有一个)。
  • 如果是在同一文件系统内 mv 文件,则只会重命名文件路径,依然使用源文件的 inode 号 。
  • 如果是跨文件系统 mv 文件,则会先拷贝源文件到目标路径(使用新的 inode 号),再删除源文件,相当于 cp f1 f2 && rm f1
    • 如果 mv 文件、文件夹的过程失败,并不会删除源文件。
  • 例:
    • mv f1 f2
      • 如果 f2 不存在,则会将 f1 重命名为 f2 ,依然使用 f1 的 inode 号。
      • 如果 f2 已存在,则会先删除 f2 ,再将 f1 重命名为 f2 ,依然使用 f1 的 inode 号。
    • mv dir1/ dir2/
      • 如果 dir2 不存在,则将 dir1 重命名为 dir2 。
      • 如果 dir2 已存在,则将 dir1 移动到 dir2 之下,保存为 dir2/dir1 。
      • 如果 dir2/dir1 已存在且不为空,则会报错 cannot move 'dir1' to '/dir2/dir1': Directory not empty ,此时可以改用 cp -rf dir1 dir2 && rm -rf dir1
    • mv dir1/* dir2/
      • 将 dir1/ 目录下的所有文件移动到 dir2/ 目录下。
      • 当移动多个源文件时,目标路径只能是一个目录,因此末尾不加斜杆 / 也能自动识别。

# rm

$ rm <path>...  # 删除文件
     -d         # 删除目录(只能是空目录)
     -r         # 递归删除(用于删除非空目录)
     -i         # 若文件存在,则提示用户是否确认删除
     -f         # 强制删除(覆盖 -i 选项,且忽略文件不存在时的报错)
  • 例:
    rm -rf *      # 强制删除当前目录下的所有文件
    

# 压缩文件

# tar

:一种打包文件的格式,不会进行压缩,扩展名为 .tar 。

  • tar 命令也支持使用 gzip、bzip2、xz 压缩格式。
  • 打包命令:
    tar -cf <name>.tar <path>...          # 将指定文件或目录打包
        -c                                # 创建一个新文件
        -x                                # 提取文件。能根据文件头部的内容识别出 gzip、bzip2、xz 压缩格式,并自动解压
        -v                                # 显示处理过程的具体信息
        -f <name>.tar                     # 使用归档文件
        --exclude=./log*                  # 根据路径 pattern 将一些文件排除打包
        -g <snapshot>                     # 进行增量打包,生成一个快照文件
    
    • 打包文件时,如果输入的原路径是绝对路径,则会按绝对路径打包,生成多余的多层目录。因此,建议输入相对路径。
    • 如果已存在同名的包文件,则会覆盖它。
  • 解包命令:
    tar
        -xv <name>.tar                    # 解包到当前目录
              -C <dir>                    # 解包到指定目录(该目录必须已存在)
              [path]...                   # 只解压指定路径的文件(可以使用通配符)
              -p                          # 保留文件的原访问权限,否则将解压后文件的所有者变为当前用户。root 用户默认会启用该选项
        -tf <name>.tar                    # 不解包,只显示其中的文件列表
        -tf <name>.tar.gz | xargs rm -rf  # 删除解包生成的文件
    
  • 增量打包的示例:
    mkdir logs
    tar -g logs-snapshot -cvf logs-full-backup.tar logs/             # 先全量打包,作为起点
    touch logs/f1
    tar -g logs-snapshot -cvf logs-incremental-backup-1.tar logs/    # 增量打包
    touch logs/f2
    tar -g logs-snapshot -cvf logs-incremental-backup-2.tar logs/    # 增量打包
    
    • 用 -g 选项进行增量打包时,会在快照文件中记录此时打包的所有文件的 Modify time 。下一次增量打包时,如果有新增文件,或者老文件的 Modify time 更新了,才加入打包,否则生成的增量包为空。
    • 缺点是,增量打包时不能记录被删除的老文件。
    • 多次增量打包时,注意生成的 .tar.gz 包名不能重复,否则会覆盖之前的增量包。
    • 解包时,按照与打包时一致的顺序:
      tar -xvf logs-full-backup.tar
      tar -xvf logs-incremental-backup-1.tar
      tar -xvf logs-incremental-backup-2.tar
      

# gzip

:一种压缩文件格式,基于 Deflate 算法,扩展名为 .gz 。

  • 压缩命令:
    tar -zcvf <name>.tar.gz <path>...
        -z                              # 采用 gzip 格式
    
    • 压缩 tar 包时的扩展名为 .tar.gz ,可简写为 .tgz 。
  • 解压命令:
    tar -xf <archive>
    

# bzip2

:一种压缩文件格式,基于 Burrows–Wheeler 算法,扩展名为 .bz2 。

  • 安装:
    yum install bzip2
    
  • 命令:
    bzip2 <file>...
        -z            # --compress ,压缩。只支持压缩单个文件,因此需要用 tar 命令打包后压缩
        -d            # --decompress ,解压
        -k            # --keep ,在压缩、解压之后不删除输入文件,默认会删除
    
    • bzip2 命令只支持压缩单个文件,因此通常用 tar 命令打包后压缩。
  • 压缩命令:
    tar -jcvf <name>.tar.bz2 <path>...
        -j                              # 采用 bzip2 格式
    
  • 解压命令:
    tar -xf <archive>
    

# xz

:一种压缩文件格式,基于 LZMA 算法,扩展名为 .xz 。

  • xz 命令与 bzip2 命令的用法相似。
  • 压缩命令:
    tar -Jcvf <name>.tar.xz <path>...
        -J                              # 采用 xz 格式
    
  • 解压命令:
    tar -xf <archive>
    

# zip

:一种压缩文件格式,扩展名为 .zip 。

  • 支持多种压缩算法:

    • Deflate :Zip、GZip 的默认压缩算法,对应的解压算法称为 Inflate 。压缩率一般,但压缩、解压快。
    • Burrows–Wheeler :压缩率高,一般能压缩文件体积到 20% ,但压缩、解压慢。
    • LZMA :xz、7-Zip 的默认压缩算法,压缩率比 高。
  • 安装:

    yum install zip unzip
    
  • 压缩命令:

    $ zip <name>.zip <path>   # 压缩文件
          -r                  # 递归处理(用于处理目录)
          -s 500m             # --split-size ,将压缩包分割成多份,并设置每份的最大体积
          -i "*.py"           # --include ,只打包路径与 pattern 匹配的文件。注意 pattern 要用引号包住,避免先执行 * 去匹配文件名
          -x "logs/*" "*.log" # --exclude ,将一些文件排除打包。--exclude 比 --include 的优先级更高
          -P "..."            # 设置密码
          -q                  # 安静模式,不显示过程信息
    
    • 如果已存在同名的压缩包,则会加入其中。
      • 如果该压缩包中已存在同名的文件,则会覆盖它。
    • 例:批量压缩文件
      file_list=`ls *.mp4`
      for f in $file_list
      do
          zip $f.zip $f -P '******'
          rm -f $f
      done
      
  • 解压命令:

    $ unzip <name>.zip       # 解压一个压缩包
            [path]...        # 只解压指定路径的文件(可以使用通配符)
            -d <dir>         # 解压到指定目录(默认是当前目录)
            -o               # 当解压后的文件已存在时,直接覆盖
            -q               # 安静模式,不显示过程信息
    
            -l               # 不解压,而是显示其中的文件列表、修改时间
            -Z               # Zipinfo ,显示压缩包的详细信息,包括文件列表、模式、大小等
            -Z1              # 只显示文件列表
    
    • unzip 命令不支持直接解压分割成多份的压缩包。
      比如生成分割的压缩包:
      zip -s 10m -r tmp.zip /tmp    # 假设此时生成 tmp.z01  tmp.z02  tmp.zip ,其中 tmp.zip 是最后一份压缩包
      
      此时执行 unzip tmp.zip 会报错 bad zipfile offset ,需要用以下命令解压:
      zip -F tmp.zip --out tmp-fixd.zip   # 修复分割的压缩包,合并成一个包
      unzip tmp-fixd.zip
      

# 7-Zip

:一种压缩文件格式,扩展名为 .7z 。

  • 在加密方面:
    • Zip 采用传统的 ZipCrypto 算法,存在漏洞:如果知道文件中 12 字节的明文文本,则能大幅降低暴力破解的计算量。
    • 7-Zip 采用 AES-256 算法,又称为 7zAES:19 ,更安全。且支持将文件名也加密。

# 拷贝文件

# dd

:用于在本机上拷贝文件,并支持转换文件格式。

  • 命令:
    $ dd
          if=<file>          # 输入文件,默认为 stdin
          of=<file>          # 输出文件,默认为 stdout
    
          count=<blocks>     # 拷贝多少个块(默认为拷贝全部数据)
          bs=<bytes>         # 输入、输出的每个块的大小。默认为 512 ,取值过大会占用过多内存
          ibs=<bytes>        # 输入的每个块的大小
          obs=<bytes>        # 输出的每个块的大小
    
  • 例:
    dd if=/dev/sda1 of=/dev/sda1.back   # 从 if 所指目录拷贝数据到 of 所指目录
    dd if=/dev/cdrom of=cd.iso          # 拷贝文件并转换成 iso 文件
    
  • dd 命令可用于拷贝出一个指定大小的文件,也可以用于测试磁盘 IO 速度,如下:
    [root@CentOS ~]# dd if=/dev/zero of=f1 count=1024 bs=1M
    1024+0 records in
    1024+0 records out
    1073741824 bytes (1.1 GB) copied, 1.27441 s, 843 MB/s
    

# rsync

:用于跨主机拷贝文件。

  • 与 scp 相比:
    • rsync 服务默认采用 TCP 873 端口,且不是加密传输。不过可以改为基于 SSH 协议传输。
    • rsync 将每个文件分块传输,如果目标文件中已包含 md5 值相同的块,则不拷贝。因此适合断点续传、增量备份。
  • 命令:
    $ rsync
            SRC... DEST             # 在本机拷贝文件(此时相当于 cp 命令)
            SRC... [USER@]HOST:DEST # 将本机的文件拷贝到其它主机
            [USER@]HOST:SRC... DEST # 将其它主机的文件拷贝到本机
            -a                      # 保留文件属性,且递归处理
            -r                      # 递归处理
            -P                      # 显示传输进度
            -z                      # 压缩传输的数据
            -e "ssh -p 22"          # 基于 SSH 协议加密传输
            --delete                # 删除目标目录比源目录多出的文件
    
    • 当 SRC 是目录时,如果以 / 结尾,则相当于 cp SRC/* DEST ,否则相当于 cp SRC DEST
    • 如果 DEST 目录不存在,则会自动创建。
  • 例:
    rsync -a  /root/f1   root@10.10.0.1:/root
    rsync -aP -e ssh f1  10.0.0.1:/root
    

# dump

dump、restore 命令用于备份本机的文件或目录,适用于 etx2、etx3、etx4 文件系统。

已淘汰不用
$ dump [option] <path>  # 备份指定的文件或目录
       -f <file>        # 保存为指定文件
       -[0-9]           # 设置备份级别
       -j               # 压缩为 bzip2 格式
  • 备份级别有 0~9 十种,0 是完全备份,1~9 都是增量备份。
    • 在备份整个分区或磁盘时才能使用增量备份。比如用 0 完全备份一次之后,可用 1 增量备份。下一次备份时,用 2 就是增量备份(级别加一),用 1 就是差异备份(覆盖之前的同级数据)。
  • dump 生成的备份文件并不是普通的压缩文件,还包含了一些备份信息,不能直接解压,只能被 restore 命令使用。
  • 例:
    dump -j -0f root.back.bz2 /root/
    
$ restore
         -f <file> # 指定备份文件(会根据其中的备份信息,还原到原路径)
         -r        # 用备份文件进行还原
         -t        # 显示备份文件的内容
         -C        # 比较当前文件与备份文件的差异(比如旧文件被修改、删除,但是增加新文件时不算差异)
  • restore 会生成一个 restoresymtable 文件,便于在增量备份时传递信息,可以删除。
  • 例:
    restore -tf root.back.bz2
    

# 切割文件

# split

$ split [file] [prefix]   # 默认每隔 1000 行分割出一个子文件,子文件按 xaa、xab、xac 的格式命名,其中 prefix 默认为 x
        -l 10             # --lines ,每隔 10 行分割出一个子文件
        -b 10K            # --bytes ,每隔 10K Bytes 分割出一个子文件,单位可以是 K、M、G、T 等
        -d                # 让子文件从数字 00 开始编号
  • 例:
    [root@CentOS ~]# split -l 3 -d README.md README.md-
    [root@CentOS ~]# ls
    README.md  README.md-00  README.md-01  README.md-02  README.md-03  README.md-04
    [root@CentOS ~]# cat README.md-* > merged
    

# csplit

$ csplit [file] [pattern]...  # 根据 pattern 分割文件。分割出的文件默认按 xx00、xx01、xx02 的格式命名
        -f xx                 # 设置子文件名称的前缀,默认为 xx
        -k                    # --keep-files ,当执行出错时,保留已生成的子文件。默认会全部删除
        -s                    # --silent ,不打印每个子文件的 bytes 体积。默认会打印
        --suppress-matched    # 保存子文件时,排除匹配行
  • 例:
    csplit f1  2          # pattern 为数字时,表示从第 n 行分割出两个子文件,并且第 n 行保存到后一个子文件
    csplit f1  3 6 9      # 可指定多个 pattern ,这会分割多次
    csplit f1  /Hello/    # 寻找与 pattern 正则匹配的行,从每个匹配行分割文件,并且每个匹配行保存到后一个子文件
    csplit f1  %^Hello%   # %pattern% 表示排除匹配行之前的内容,将从匹配行开始的内容保存为子文件
    csplit f1  "/^Hello World/" {5}   # {n} 表示将前一个 pattern 重复执行 n 次,如果找不到匹配行则报错
    csplit f1  "/^Hello World/" {*}   # {*} 表示将前一个 pattern 重复执行尽可能多次,直到找不到匹配行。至少会生成一个子文件
    

# truncate

$ truncate [file]...
          -s 10M      # 将文件调整为指定大小,多余的空间会被截断,缺少的空间会用空字节填补

# 其它

# md5sum

$ md5sum [file]...    # 计算文件的 md5 哈希值
  • 例:
    [root@CentOS ~]# echo Hello | md5sum
    09f7e02f1290be211da707a266f153b3  -
    
  • 另外还有 sha1sum、sha256sum、sha512sum 命令,用法同理。

# 随机数据

Linux 提供了以下两个特殊的字符设备文件,用于产生随机数据:

  • /dev/random :消耗系统的熵池来产生随机数据,随机性很高,但是熵池不足时会阻塞读取它的进程。
  • /dev/urandom :重复使用熵池来产生随机数据,随机性略低,但一般足够安全。
  • 例:
    head -c 10m /dev/urandom > f1                            # 生成一个指定大小、随机内容的文件
    cat /dev/urandom | tr -cd A-Za-z0-9 | head -c 12 ; echo  # 生成随机字符串
    echo $[`cat /dev/urandom | tr -cd 0-9 | head -c 2`%5]    # 生成小于 5 的随机数