Post

shell

shell

A Little Book on Shell

常用 command

  1. file cp mv mkdir rm ln 其中ln 命令 ln file link, 默认 创建 hard link, ln -s file link 才 为 soft link, soft link 同样增加 file 的link count
  2. Working with Commands (type which help man apropos info whatis alias)
1
2
3
4
5
6
7
8
9
10
| command | meaning                                           |
|---------|---------------------------------------------------|
| type    | Indicate how a command name is interpreted        |
| which   | Display which executable program will be executed |
| help    | Get help for shell builtins                       |
| man     | Display a command's manual page                   |
| apropos | Display a list of appropriate commands            |
| info    | Display a command's info entry                    |
| whatis  | Display one-line manual page descriptions         |
| alias   | Create an alias for a command                     |
  1. commands 的来源:

    • An executable program: 例如 /usr/bin 下面的 可执行文件,
    • A command built into the shell itself.: bash 支持的内建 的 命令
    • A shell function: shell 函数 Shell functions are miniature shell scripts incorporated into the environment
    • An alias: Aliases are commands that we can define ourselves, built from other commands.
  2. man 详细内容: Display a Program’s Manual Page。 手册内容 被分为 几个 章节, 除了 使用 man command, 之外 可以使用 man 1 command 来显示 User commands 章节

    sectioncontents
    1User commands
    2Programming interfaces for kernel system calls
    3Programming interfaces to the C library
    4Special files such as device nodes and drivers
    5File formats
    6Games and amusements such as screen savers
    7Miscellaneous
    8System administration commands
  3. apropos – Display Appropriate Commands 展示相关的 命令。通过 apropos ls 可以获得 lscpu, lshw, 等一系列 命令
  4. whatis – Display One-line Manual Page Descriptions: 展示一行关于 command的简单描述
  5. info 另一种展现形式的 man
  6. alias: alias name=’string’ 来构建 名为 name 的command line, type name 可以获得 name 对应的 具体string 内容

Redirection

  1. cat sort uniq grep wc head tail tee (Read from standard input and write to standard output and files)
  2. command line 数据流 有: 标准输入 标准输出 标准错误输出,即: stdin, stdout, stderr, 0, 1, 2
  3. 重定向 stdout, 使用 > 来将 输出 重定向到 file 中,file中内容将被覆盖。  » 将 数据重定向 到file中,不覆盖 追加到 file 末尾中
  4. 重定向 stderr, 类似 重定向 stdout 使用 2>, 2» 进行 标准错误输出 的数据重定向
  5. 将stdout & stderr 重定向 到一个 file 中:
  • ls -l /bin/usr > ls-output.txt 2>&1 , 注意 其中的 2>&1 的写法,以及, > 与 2>&1 的顺序, 其中原因,为shell 语法需要 控制 两次重定向 打开的是同一个文件
  • ls -l /bin/usr &> ls-output.txt 也可以这样 &> 代表 stdout stderr, ls -l /bin/usr &» ls-output.txt 则代表 将stdout stderr 数据流 追加到 文件中
    1. Disposing of Unwanted Output: ls -l /bin/usr 2> /dev/null 将 数据流 重定向 到 /dev/null 则可以起到忽略 数据流的作用
    2. 重定向 stdin, 使用 < 来重定向 stdin 从 键盘 到 file 上, 但是并不是特别有用(很少用到)
    3. Pipelines: 使用 pipe operator将 一个command 的标准输出 输送 到 一个command 的标准输入中。 command1command2
    4. Pipelines 与 重定向的 区别: 重定向只能 定向到 file, 而 pipelines 则可以 重定向到 一个command

Seeing the World as the Shell Sees It

扩展 Expansion: how a simple character sequence, for example *, can have a lot of meaning to the shell. The process that makes this happen is called expansion. With expansion, we enter some- thing and it is expanded into something else before the shell acts upon it. 也就是 说 在 传递 参数给 command, command 接收参数处理前,会被 进行处理,该处理过程 即是: expansion。 {}

  1. echo 是如何 显式化 看到 expansion 结果的 重要方式
  2. Pathname Expansion (路径扩展): 如下释义:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
       [me@linuxbox ~]$ ls
       Desktop ls-output.txt Pictures Templates Documents Music Public Videos
    
       [me@linuxbox ~]$ echo D*
       Desktop Documents
    
       [me@linuxbox ~]$ echo *s
       Documents Pictures Templates Videos
    
       [me@linuxbox ~]$ echo [[:upper:]]*
       Desktop Documents Music Pictures Public Templates Videos
    
       [me@linuxbox ~]$ echo /usr/*/share
       /usr/kerberos/share /usr/local/share
    
    
  3. Arithmetic Expansion: $((expression)), expression 是 算术表达式, 操作数 只能是整数, 操作符 有 +, -, *, /, %, **

    1
    
     [me@linuxbox ~]$ echo $(($((5**2)) * 3))
    
  4. Brace Expansion:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
       [me@linuxbox ~]$ echo Front-{A,B,C}-Back
       Front-A-Back Front-B-Back Front-C-Back
    
       [me@linuxbox ~]$ echo Number_{1..5}
       Number_1 Number_2 Number_3 Number_4 Number_5
    
       [me@linuxbox ~]$ echo {01..15}
       01 02 03 04 05 06 07 08 09 10 11 12 13 14 15
    
       [me@linuxbox ~]$ echo {001..15}
       001 002 003 004 005 006 007 008 009 010 011 012 013 014 015
    
       [me@linuxbox ~]$ echo {Z..A}
       Z Y X W V U T S R Q P ON M L K J I H G F E D C B A
    
    
       [me@linuxbox ~]$ mkdir Photos
       [me@linuxbox ~]$ cd Photos
       [me@linuxbox Photos]$ mkdir {2007..2009}-{01..12} 
       [me@linuxbox Photos]$ ls
       2007-01 2007-07 2008-01 2008-07 2009-01 2009-07 2007-02 2007-08 2008-02 2008-08 2009-02 2009-08 2007-03 2007-09 2008-03 2008-09 2009-03 2009-09 2007-04 2007-10 2008-04 2008-10 2009-04 2009-10 2007-05 2007-11 2008-05 2008-11 2009-05 2009-11 2007-06 2007-12 2008-06 2008-12 2009-06 2009-12
    
  5. Parameter Expansion

    1
    2
    
     [me@linuxbox ~]$ echo $USER
     me
    
  6. Command Substitution: 子命令, 允许在表达式中 执行子命令 并展开. $(command sub)

    1
    2
    
     [me@linuxbox ~]$ echo $(ls)
     Desktop Documents ls-output.txt Music Pictures Public Templates Videos
    
  7. Quoting: 可以用来控制 是否进行 扩展 展开。
    • 下面两个示例:

      1
      2
      3
      4
      5
      
      [me@linuxbox ~]$ echo this is a    test
      this is a test
      
      [me@linuxbox ~]$ echo The total is $100.00
      The total is 00.00
      

    注意 这两个 的存在的问题 1. 第一个中 shell 将 params 中多余的空格 去掉了, 即是: ‘a test’中多余的空格, 因为 shell 将 通过 空格 分隔 参数,认为 a test 为两个参数。 2. $100.00 展开为了 00.00 是因为 $1 不存在的缘故

  • Double Quotes: 将参数 加上 “” 之后, ““内的内容 将被视为 一个 参数, 但 parameter expansion, arithmetic expansion, and command substitution 依然 有效。 如下示例:

    1
    2
    3
    4
    5
    6
    
     [me@linuxbox ~]$ ls -l two words.txt
     ls: cannot access two: No such file or directory
     ls: cannot access words.txt: No such file or directory
    
     [me@linuxbox ~]$ ls -l "two words.txt"
     -rw-rw-r-- 1 me me 18 2016-02-20 13:03 two words.txt [me@linuxbox ~]$ mv "two words.txt" two_words.txt
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
     [me@linuxbox ~]$ echo this is a    test
     this is a test
    
     [me@linuxbox ~]$ echo "this is a   test"
     this is a   test
    
    
    
    
     (calvagrant@precise64:~$ echo $(cal)
     September 2020 Su Mo Tu We Th Fr Sa 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 28 29 30
    
     vagrant@precise64:~$ echo "$(cal)"
        September 2020
     Su Mo Tu We Th Fr Sa
            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 28 29 30
    
    
  • Single Quotes: 单引号 中的内容 扩展 全部 失效。
  • Escaping Characters: \
escape sequencemeaning
\aBell
\bBackspace
\nNewline
\rCarriage return
\tTab
  1. Signals: Signals are one of several ways that the operating system communicates with programs
  • kill: The kill command doesn’t exactly “kill” processes: rather it sends them signals kill [-signal] PID…

    keyboardsignal
    Ctrl-cINT
    Ctrl-zTSTP
    NumberNameMeaning
    1HUPHangup. This is a vestige of the good old days when terminals were attached to remote computers with phone lines and modems. The signal is used to indicate to programs that the controlling terminal has “hung up.” The effect of this signal can be demonstrated by closing a terminal session. The foreground program running on the terminal will be sent the signal and will terminate.
    2INTInterrupt. This performs the same function as a Ctrl-c sent from the terminal. It will usually terminate a program.
    9KILLKill. This signal is special. Whereas programs may choose to handle signals sent to them in different ways, including ignoring them all together, the KILL signal is never actually sent to the target program. Rather, the kernel immediately terminates the process. When a process is terminated in this manner, it is given no opportunity to “clean up” after itself or save its work. For this reason, the KILL signal should be used only as a last resort when other termination signals fail.
    15TERMTerminate. This is the default signal sent by the kill command. If a program is still “alive” enough to receive signals, it will terminate.
    18CONTContinue. This will restore a process after a STOP or TSTP signal. This signal is sent by the bg and fg commands.
    19STOPStop. This signal causes a process to pause without terminating. Like the KILL signal, it is not sent to the target process, and thus it cannot be ignored.
    20TSTPTerminal stop. This is the signal sent by the terminal when Ctrl-z is pressed. Unlike the STOP signal, the TSTP signal is received by the program, but the program may choose to ignore it.
    3QUITQuit
    11SEGVSegmentation violation. This signal is sent if a program makes illegal use of memory, that is, if it tried to write somewhere it was not allowed to write.
    28WINCHWindow change. This is the signal sent by the system when a window changes size. Some programs , such as top and less will respond to this signal by redrawing themselves to fit the new window dimensions.
  • example, kill -number-Name 也即是说 kill 可以接受 number 或者 显示的名称
    1
    2
    
    [me@linuxbox ~]$ kill -1 13546
    [me@linuxbox ~]$ kill -SIGINT 13608
    

Configuration and the Environment

  1. Environment: shell维护一个shell的 session信息 称为 环境; 两种形式: environment variables + shell variables。但是无法区分 两种类型的变量。

    • printenv: 用来展现所有的 变量
    • set: 不带参数 展现所有变量以及 shell函数
    • alias: 展现所有 alias 相关的
    • export: 设定环境变量,在之后的程序可见
  2. Environment 中的变量是 如何定义的:
    • A login shell session: A login shell session is one in which we are prompted for our username and password. This happens when we start a virtual console session, for example.
    • A non-login shell session: A non-login shell session typically occurs when we launch a terminal session in the GUI.
    • Login Shell Sessions 读取配置文件 顺序:

      FileContents
      /etc/profileA global configuration script that applies to all users.
      ~/.bash_profileA user’s personal startup file. This can be used to extend or override settings in the global configuration script.
      ~/.bash_loginIf ~/.bash_profile is not found, bash attempts to read this script.
      ~/.profileIf neither ~/.bash_profile nor ~/.bash_login is found, bash attempts to read this file. This is the default in Debian-based distributions, such as Ubuntu.
    • Non-Login Shell Sessions 读取配置文件: non-login shells inherit the environ- ment from their parent process, usually a login shell. Non-login 会 继承 Login shell 的环境

      FileContents
      /etc/bash.bashrcA global configuration script that applies to all users
      ~/.bashrcA user’s personal startup file. It can be used to extend or override settings in the global configuration script.
        
  3. 修改env的常见规则: 在 .bash_profile 中修改PATH 或者定义其他的环境变量。 .bashrc 所有的其他的一切
  4. source ~/.bashrc 强制加载 配置文件, (一般在更改 .bashrc之后只有重启 terminal 才会生效,如果想立即生效,可以使用source ~/.bashrc 或者 . ~/.bashrc)
  5. 命令查找: ls 命令的 定义在哪里, 又是如何找到的呢?
    • shell 从 PATH 变量中 包含的 Path 中 顺序查找
    1
    2
    3
    4
    5
    
      PATH=$PATH:$HOME/bin
      export PATH
    
      简单的将 $HOME/bin 添加到 PATH 中 (注意 $HOME 会在此处求值)
      export PATH 让 shell之后的process 中的PATH都改变
    
  6. Customizing the Prompt: 由 PS1变量控制 PS1 (short for “prompt string 1”).

    Tools:

    网络:

  7. ping, traceroute, ip(ifconfig), netstat, ftp, wget, ssh, scp, sftp

    查找文件

  8. locate : 非常简单有效,只能使用 filename 用来查找。 locate 足够高效 因为 其从 updatedb command 更新的数据库中来进行查找,updatedb 经常 放在cron job 来执行(需要确认下,因为没有找到 相关的配置文件)
  9. find 寻找文件 则显得 复杂 而详尽。可以根据给定的 目录 以及 各个限定 来查找文件。 可选参数与 含义

    参数可选值
    -typeb: Block special device file; c: Character special device file; d: Directory; f: regular file; l Symbolic link
    -sizec Bytes; w: 2-byte words; k: kilobytes; M: megabytes; G: Gigabytes;
    -cmin nMatch files or directories whose content or attributes were last modified exactly n minutes ago. To specify less than n minutes ago, use -n, and to specify more than n minutes ago, use +n.
    -cnewer fileMatch files or directories whose contents or attributes were last modified more recently than those of file.
    -ctime nMatch files or directories whose contents or attributes were last modified n*24 hours ago.
    -emptyMatch empty files and directories.
    -iname patternLike the -name test but case-insensitive.
    -inum nMatch files with inode number n. This is helpful for finding all the hard links to a particular inode.
    -mmin nMatch files or directories whose contents were last modified n minutes ago.
    -mtime nMatch files or directories whose contents were last modified n*24 hours ago.
    -name patternMatch files and directories with the specified wildcard pattern.
    -newer fileMatch files and directories whose contents were modified more recently than the specified file. This is useful when writing shell scripts that perform file backups. Each time you make a backup, update a file (such as a log) and then use find to determine which files have changed since the last update
    -samefile nameSimilar to the -inum test. Match files that share the same inode number as file name
    -user nameMatch files or directories belonging to user name. The user may be expressed by a username or by a numeric user ID.
  • for example:

    1
    
      find ~ -type f -name "*.JPG" -size +1M | wc -l
    

    注意 其中的 -name 参数需要添加 “” 来防止 pathname expansion, size: 则使用 +1M 表示大于 1M 的文件

  • find logical Operators: find 则可以更复杂的 使用 -and -or -not () 等来进行 logic 之间的 与或 操作 来设定更复杂的 test 关系

    1
    
      ( expression 1 ) -or ( expression 2 )
    
  • Predefined Actions:

    ActionMeaning
    -deletedelete match file
    -lsls -dils match file
    -printoutput full pathname of match file
    -quitQuit once a match has been made
  • User-Defined Actions: -exec rm ‘{}’ ‘;’: {} 代表 match file 的pathname。 这里面 存在的问题是: -exec 中的命令会被 实例化 多次,在每次match file的时候 就会实例化一次。可以简单的 实例化多次 修改为 实例化一次 。 即:

    1
    2
    3
    4
    5
    6
    7
    8
    
     find ~ -type f -name 'foo*' -exec ls -l '{}' ';'
     -rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo 
     -rw-r--r-- 1 me me 0 2016-09-19 12:53 /home/me/foo.txt
    
     // 修改后
     find ~ -type f -name 'foo*' -exec ls -l '{}' +
     -rwxr-xr-x 1 me me 224 2007-10-29 18:44 /home/me/bin/foo 
     -rw-r--r-- 1 me me 0 2016-09-19 12:53 /home/me/foo.txt
    
  • xargs: 用于将 接受的input 的信息 作为参数 传递给 command. xargs存在的原因在于: 一些 命令 接受 命令行参数+标准输入,但是其他一些命令 则 只接受命令行 输入,所以需要 xargs 将标准输入 转换为 命令行参数 Some commands such as grep and awk can take input either as command-line arguments or from the standard input. However, others such as cp and echo can only take input as arguments, which is why xargs is necessary. [name] xargs 中存在一些 问题,主要是 关于 filename 中的空格,等分隔符号 对整个 shell的参数接收 都有影响。 所以 接受filename 的时候 –null 参数 将 是xargs 不被 ‘ ’分隔( 使用 空字符串 作为分隔), -d ‘

Archiving and Backup:

  1. compressor command: gzip bzip2, gzip options

    OptionLong OptionDesc
    -c–stdoutWrite output to standard output and keep the original files.
    -d–decompressDecompress This causes gzip act like gunzip
    -f–forceforce compress event if a compressed file already exists
    -l–list应用 已压缩文件 展示 压缩信息
    -r–recursive递归压缩目录下的文件(目录下的文件各自压缩为 各自的压缩文件,所以 依然需要archive 程序)
    -v–verboseDisplay verbose messages while compressing.
    -number Set amount of compression. number is an integer in the range of 1 (fastest, least compression) to 9 (slowest, most compression). The values 1 and 9 may also be expressed as –fast and –best, respectively. The default value is 6.

    bzip2 同gzip 一样 为压缩程序,其中的参数 都大概相同,除了-r -number 外。 bunzip2 bzcat 用于解压缩。 bzip2recover 可以恢复受损的 压缩文件

  2. archive command: tar zip: Archiving is the process of gathering up many files and bundling them together into a single large file.

    ModeMeaning
    cCreate an archive from a list of files and/or directories.
    xExtract an archive.
    rAppend specified pathnames to the end of an archive
    tList the content of an archive
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
     [me@linuxbox ~]$ gzip foo.txt
     [me@linuxbox ~]$ ls -l foo.*
     -rw-r--r-- 1 me me 3230 2018-10-14 07:15 foo.txt.gz
    
     [me@linuxbox ~]$ gzip -d foo.txt.gz
    
     [me@linuxbox ~]$ gunzip foo.txt
     [me@linuxbox ~]$ ls -l foo.*
     -rw-r--r-- 1 me me 15738 2018-10-14 07:15 foo.txt
    
    
     [me@linuxbox ~]$ bzip2 foo.txt
     [me@linuxbox ~]$ ls -l foo.txt.bz2
     -rw-r--r-- 1 me me 2792 2018-10-17 13:51 foo.txt.bz2
     [me@linuxbox ~]$ bunzip2 foo.txt.bz2
    

    tar: 只能以 相对路径 archive 文件。unarchive 的时候 在 当前路径下 以相对路径 恢复文件。example

    1
    2
    3
    4
    5
    6
    
    cd foo
    tar xf ../playground2.tar
    ls
    home playground
    tar cf playground2.tar ~/playground
    

–wildcards 可以用来过滤掉 特定的 match 文件 \n find 经常用来 与 tar 配合进行 批量 archive

1
2
3
4
5
```shell
find playground -name 'file-A' -exec tar rf playground.tar '{}' '+'

find playground -name 'file-A' | tar cf - --files-from=- | gzip > playground.tgz
```

第二条命令比较 特殊,在其中 tar cf - –files-from=- 中, - 代表 标准 标准输入或者输出 tar 可以通过添加 z j 参数,直接 使用gzip bzip2 进行压缩, z: gzip .tgz, j: bzip2 .tbz

1
2
3
```shell
find playground -name 'file-A' | tar czf playground.tgz -T -
```

通过网络进行 文件备份:

1
  ssh remote-sys 'tar cf - Documents' | tar xf -

zip, unzip: 的命令 比较详细,所以只列出简短 的示例:

1
2
3
  zip -r playground.zip playground // -r 是必须,这样才能 得到 playground 下的所有 archive
  unzip ../playground.zip // 不同与 tar, zip 使用unzip 来进行 unarchive
  unzip -l ../playground.zip
  1. sync command: rsync rsync options source destination where source and destination are one of the following:
  • A local file or directory
  • A remote file or directory in the form of [user@]host:path
  • A remote rsync server specified with a URI of rsync://[user@]host[:port]/path

注意: source destination 其中之一 必须 为 本地的文件, 远程 到 远程的 copy 是不被允许的。 示例:

1
2
3
4
5
  rsync -av source destination // -a 代表 archive mode, v verbose output
  rsync -av source/ destination
  // 两种方式不同的地方在于 后一种 只拷贝 source 中的内容到 destination, 而 第一种 则将source 目录也 拷贝到 destination 中.

  rsync -av --delete source/ destination   // delete 参数 为 完全拷贝, source 中删除掉的file 将在 destination 中删除掉。
  1. Using rsync Over a Network: 的两种方式
    • source 安装了 rsync 的机器 以及 destination 安装了 远程shell 程序, 如: ssh
    • destination 安装了 rsync server, rsync 可以配置为 daemon 模式 等待 sync 请求
1
2
3
  sudo rsync -av --delete --rsh=ssh /etc /home /usr/local remote-sys:/backup
  // 这里面 --rsh 指定为 ssh, 使 rsync 能够 使用ssh 来进行同步操作
  rsync -av –delete rsync://archive.linux.duke.edu/ fedora/linux/development/rawhide/Everything/x86_64/os/ fedora-devel

Text Processing:

cat, sort, uniq, cut, paste, join, comm, diff, patch, tr, sed, aspell

  • grep :
  • cat:

    1
    2
    3
    
    cat > foo.txt // ctrl-d结束输入
    cat -A foo.txt // 其中 ^I 代表 tab, $ 代表 line末尾, 所以可以用此来 区分 tab 与 space
    cat -nA foo.txt // n 显式 line number
    
  • sort : 对输入进行排序, 是一个比较复杂,有用的命令:下面是详细参数

    OptionLong Optionmeaning
    -b–ignore-leading-blanks使忽略掉 行前的 空格,使用第一个非空格排序
    -f–ignore-case 
    -n–numeric-sort将字符串当做number 来进行比较
    -r–reversereverse order 排序
    -k–key=field1[,field2]使用 field1..field2 作为排序的key, field1 不存在则为1, field2 不存在则 从field1直至末尾, field1 都可以是如此形式 f[.c][opts] .c 为内部的offset
    -m–merge将每个参数作为预排序文件的名称。 将多个文件合并为单个排序结果,而不执行任何其他排序。
    -o–output=file输出到 file 而非 标准输出
    -t–field-separator=char使用 char 作为分隔符,默认为 space 或者tab
     –debug将用作sort 的key 进行标记

    下面是几个示例

    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    
      [me@linuxbox ~]$ du -s /usr/share/* | head
      252 /usr/share/aclocal
      96 /usr/share/acpi-support
      8 /usr/share/adduser
      196 /usr/share/alacarte 344 /usr/share/alsa
      8 /usr/share/alsa-base 12488 /usr/share/anthy
      8 /usr/share/apmd
    
    
      //  下面对结果进行排序 其中 -nr 将string作为number 处理并 翻转排序, 这里面之所有管用,是因为 第一列 为 数字, 即 默认按照第一列进行排序
      [me@linuxbox ~]$ du -s /usr/share/* | sort -nr | head
      509940 /usr/share/locale-langpack
      242660 /usr/share/doc
      197560 /usr/share/fonts
      179144 /usr/share/gnome
      146764 /usr/share/myspell
      144304 /usr/share/gimp
      135880 /usr/share/dict
      76508 /usr/share/icons
      68072 /usr/share/apps
      62844 /usr/share/foomatic
    
      // 如果是这样的又如何排序?
      [shaohua.li@10-11-112-3 ~]$ ls -l /usr/bin/ | head
      total 58404
      -rwxr-xr-x  1 root root     33408 Nov 10  2015 [
      -rwxr-xr-x  1 root root    106792 Nov 10  2015 a2p
      -rwxr-xr-x. 1 root root     14984 Aug 18  2010 acpi_listen
      -rwxr-xr-x. 1 root root     23488 Nov 11  2010 addftinfo
      -rwxr-xr-x  1 root root     24904 Jul 23  2015 addr2line
      -rwxr-xr-x. 1 root root      1786 Feb 21  2013 apropos
      -rwxr-xr-x  1 root root     56624 Jul 23  2015 ar
      -rwxr-xr-x  1 root root    328392 Jul 23  2015 as
      -rwxr-xr-x. 1 root root     10400 Sep 23  2011 attr
    
      // -k 5 使用 第 5 field 作为key 用作 排序使用
      [shaohua.li@10-11-112-3 ~]$ ls -l /usr/bin/ | sort -nr -k 5 | head
      -rwxr-xr-x  1 root root   3214440 Dec 12  2016 mysql
      -rwxr-xr-x  1 root root   3051080 Dec 12  2016 mysqlbinlog
      -rwxr-xr-x  1 root root   2998400 Dec 12  2016 mysqldump
      -rwxr-xr-x  1 root root   2948832 Dec 12  2016 mysqlslap
      -rwxr-xr-x  1 root root   2936680 Dec 12  2016 mysqladmin
      -rwxr-xr-x  1 root root   2935688 Dec 12  2016 mysqlcheck
      -rwxr-xr-x  1 root root   2933128 Dec 12  2016 mysqlimport
      -rwxr-xr-x  1 root root   2931712 Dec 12  2016 mysqlshow
      -rwxr-xr-x  1 root root   2814328 Dec 12  2016 my_print_defaults
      -rwxr-xr-x  1 root root   2811544 Dec 12  2016 mysql_waitpid
    
    
      // 下面是 比较复杂的示例
      root@precise64:~/shell_test#  cat distros.txt
      Fedora  5    03/20/2006
      Fedora  6    10/24/2006
      Fedora  7    05/31/2007
      Fedora  8    11/08/2007
      Fedora  9    05/13/2008
      Fedora  10   11/25/2008
      SUSE    10.1 05/11/2006
      SUSE    10.2 12/07/2006
      SUSE    10.3 10/04/2007
      SUSE    11.0 06/19/2008
      Ubuntu  6.06 06/01/2006
      Ubuntu  6.10 10/26/2006
      Ubuntu  7.04 04/19/2007
      Ubuntu  7.10 10/18/2007
      Ubuntu  8.04 04/24/2008
      Ubuntu  8.10 10/30/2008
    
      // 如何对 distros 按照其 发布的版本 以及 发布时间 进行排序呢?
    
      // 单纯的 按照 发布版本排序
      root@precise64:~/shell_test# sort distros.txt  -nrk 2
      SUSE    11.0 06/19/2008
      SUSE    10.3 10/04/2007
      SUSE    10.2 12/07/2006
      SUSE    10.1 05/11/2006
      Fedora  10   11/25/2008
      Fedora  9    05/13/2008
      Ubuntu  8.10 10/30/2008
      Ubuntu  8.04 04/24/2008
      Fedora  8    11/08/2007
      Ubuntu  7.10 10/18/2007
      Ubuntu  7.04 04/19/2007
      Fedora  7    05/31/2007
      Ubuntu  6.10 10/26/2006
      Ubuntu  6.06 06/01/2006
      Fedora  6    10/24/2006
      Fedora  5    03/20/2006
    
      // 综合排序, 使用多个key, 版本,以及版本号 进行综合排序
      root@precise64:~/shell_test# sort --key=1,1 --key=2n distros.txt
      Fedora  5    03/20/2006
      Fedora  6    10/24/2006
      Fedora  7    05/31/2007
      Fedora  8    11/08/2007
      Fedora  9    05/13/2008
      Fedora  10   11/25/2008
      SUSE    10.1 05/11/2006
      SUSE    10.2 12/07/2006
      SUSE    10.3 10/04/2007
      SUSE    11.0 06/19/2008
      Ubuntu  6.06 06/01/2006
      Ubuntu  6.10 10/26/2006
      Ubuntu  7.04 04/19/2007
      Ubuntu  7.10 10/18/2007
      Ubuntu  8.04 04/24/2008
      Ubuntu  8.10 10/30/2008
    
      // k 可以为 f[.c][opts]  可以指定 field 中的 c pos 用来比较
      root@precise64:~/shell_test# sort -k 3.7nbr -k 3.1nbr -k 3.4nbr distros.txt
      Fedora  10   11/25/2008
      Ubuntu  8.10 10/30/2008
      SUSE    11.0 06/19/2008
      Fedora  9    05/13/2008
      Ubuntu  8.04 04/24/2008
      Fedora  8    11/08/2007
      Ubuntu  7.10 10/18/2007
      SUSE    10.3 10/04/2007
      Fedora  7    05/31/2007
      Ubuntu  7.04 04/19/2007
      SUSE    10.2 12/07/2006
      Ubuntu  6.10 10/26/2006
      Fedora  6    10/24/2006
      Ubuntu  6.06 06/01/2006
      SUSE    10.1 05/11/2006
      Fedora  5    03/20/2006
    
    
      // --debug 选项 是比较有意思的,用来在 不知道 key 以及sort情况时候,用来展现 其内部sort key 的方式
      root@precise64:~/shell_test# cat /etc/passwd | sort -t ':' -k 7 --debug | head
      sort: using `en_US' sorting rules
      root:x:0:0:root:/root:/bin/bash
                            _________
      _______________________________
      vagrant:x:1000:1000:vagrant,,,:/home/vagrant:/bin/bash
                                                   _________
      ______________________________________________________
      messagebus:x:102:105::/var/run/dbus:/bin/false
                                          __________
      ______________________________________________
      mysql:x:106:111:MySQL Server,,,:/nonexistent:/bin/false
    
  • uniq: 去除 重复的 条目, 比较有意思的是 需要在sort之后 使用,也就是说 uniq只能去除掉 相邻的 重复的条目

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    [me@linuxbox ~]$ cat > foo.txt 
    a
    b
    c
    a
    b
    c
    
    [me@linuxbox ~]$ uniq foo.txt 
    a
    b
    c
    a
    b
    c
    
    [me@linuxbox ~]$ sort foo.txt | uniq 
    a
    b
    c
    
  • cut: 用于 使用 -d, –delimiter=DELIM 默认为tab 分隔line, 然后提取 field -f list, characters 等

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    root@precise64:~/shell_test# cat /etc/passwd | cut -d ':' -f 1 | head
    root
    daemon
    bin
    sys
    sync
    games
    man
    lp
    mail
    news
    
  • comm: 用于比较 两个文本的变化差异: comm file1 file2 其结果 第一列 显式 file1 独有的,第二列 显示 file2 独有的, 第三列 显示 file1 file2 共同的

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    root@precise64:~/shell_test# cat file1.text
    a
    b
    c
    d
    
    root@precise64:~/shell_test# cat file2.text
    b
    c
    d
    e
    
    // 注意 其展现形式, 第一列only file1 have  第二列 only file2 have  第三列 comm
    root@precise64:~/shell_test# comm file1.text file2.text
    a
            b
            c
            d
        e
    
  • https://toroid.org/unix-pipe-implementation
  • diff: diff file1.txt file2.txt; diff 展现的格式 都为 更改 file1.txt 转变到 file2.txt 的操作序列,即是 字符串 之间最小编辑记录 在文件中的应用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    root@precise64:~/shell_test# cat file1.text
    a
    b
    c
    d
    root@precise64:~/shell_test# cat file2.text
    b
    c
    d
    e
    
    root@precise64:~/shell_test# diff file1.text  file2.text
    1d0
    < a
    4a4
    > e
    
    Changemeaning
    r1ar2将 file2 中的 r2行 追加到 file1 中的 r1 行中
    r1cr2将 file1 中 r1 行 替换为 file2中的 r2 行
    r1dr2将 file1中的r1行删除掉,下一行将出现在 file2中的 r2 行

    此格式为默认格式,但是因为不够直观,所以这种格式并不常用。 上下文格式:diff -c file1.txt file2.txt

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
    root@precise64:~/shell_test# diff -c file1.text  file2.text
    *** file1.text	2020-09-24 08:02:08.202406914 +0000 // ** 代表 file1.txt,其后是 时间戳
    --- file2.text	2020-09-24 08:02:15.854515682 +0000 // -- 代表 file2.txt 其后是 时间戳
    ***************
    *** 1,4 **** // ** 代表 file1.txt
    - a
      b
      c
      d
    --- 1,4 ---- // -- 代表 file2.txt
      b
      c
      d
    + e
    
    IndicatorMeaning
    blank此行 两个文件没有差别
    -需要删除该行(只会出现在 file1中,因为目的是 将file1 转向 file2)
    +添加该行 (只会出现在file2中,代表 需要将该行添加到 file1中)
    !两个文件中都会出现,代表 file1中的该行 需要被 file2中的 对应行 替换

    统一格式: diff -u file1.txt file2.txt

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    root@precise64:~/shell_test# diff -u file1.text file2.text
    --- file1.text	2020-09-24 08:02:08.202406914 +0000
    +++ file2.text	2020-09-25 04:41:15.154310271 +0000
    @@ -1,4 +1,4 @@
    -a
     b
     c
     d
    +e
    
    CharacterMeaning
    blankno change
    -从file1 文件中删除该行
    +从file1 文件中添加该行

    也就是说 统一格式 中,将 !替换操作去除了,通过 - + 来实现了 替换操作

  • patch: 用来将diff 出的结果 apply。 即是: diff -Naur old_file new_file > diff_file; patch < diff_file; 之后 old_file 会转变为 same as new_file
  • tr: 基于字符的替换工具, tr 只能基于 单个字符进行替换操作, 无法进行 多个字符串的匹配替换操作. -s squeeze delete 将连续的字符进行删除操作 for example:

    1
    2
    3
    
    echo "lowercase letters" | tr a-z A-Z // LOWERCASE LETTERS
    
    echo "aaabbbccc" | tr -s ab // abccc  
    
  • sed:

    Text format: nl, fold, fmt, pr, printf, groff

    shell 语法

    The #! character sequence is, in fact, a special construct called a shebang. The shebang is used to tell the kernel the name of the interpreter that should be used to execute the script that follows. Every shell script should include this as its first line

  1. Good Locations for Scripts The ~/bin directory is a good place to put scripts intended for personal use. If we write a script that everyone on a system is allowed to use, the traditional location is /usr/ local/bin. Scripts intended for use by the system administrator are often located in / usr/local/sbin. In most cases, locally supplied software, whether scripts or com- piled programs, should be placed in the /usr/local hierarchy and not in /bin or / usr/bin. These directories are specified by the Linux Filesystem Hierarchy Standard to contain only files supplied and maintained by the Linux distributor.

  2. Variables and Constants

    • shell 中的变量 是 动态的,不需要预先声明 与类型指定(因为没有类型,可能都为字符串),对于 使用 未定义 未赋值 的变量 其 数值 为 空。 所以我们需要 注意自己的拼写错误,因为 shell可能会将其视为 新变量。
    • 常量: 规范 使用 全大写 命名 常量,以区分于 普通变量。 也可以使用 declare -r TITLE=”Page Title” 来进行声明
    • 赋值: variable=value shell 并不区分 value的类型, value 全部被视为 string, 注意= 左右没有空格
    • 变量数值引用 需要注意, 因为语法原因 可能 需要使用 {} 来避免 变量名与表达式 的歧义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  a=z # a 赋值为 string z
  b="a string"
  c="a string and $b" # c 

  d="$(ls -l foo.txt)" # value 为子命令结果
  e=$((5 * 7)) # 数值计算展开

  a=5 b="string" # 多个变量可以 同时声明

  filename="myfile"
  touch $filename
  mv "$filename" "$filename1"  # 这里面的本意是 更改myfile 文件为 myfile1,但是因为并没有 区分 $filename1 是变量还是表达式, 所以这里需要 使用新的形势 来进行区分
  mv: missing destination file operand after `myfile'
  Try `mv --help' for more information.

  mv "$filename" "${filename}1" # 使用 {} 来解决歧义

here doc: 优势在于在 Here doc内部的 “ ‘ 都会失去含义, 可以方便的构建json

1
2
3
4
5
6
7
8
```shell
command << token 
 text
token
# command is the name of command that accepts standard input and token is a string used to indicate the end of the embedded text


```

function define:

1
2
3
4
5
6
7
8
9
10
  function name {
      commands
      return
  }

  name() {
      commands
      return
  }

  • local var: 局部变量, local foo=
  • function 之间的数据传递: 有三种 答案在这里: https://stackoverflow.com/questions/8742783/returning-value-from-called-function-in-a-shell-script
  • 其中使用echo 方式传递 返回值 的方式 类似于 local name=$(cat xxxgrep) 等, 即: 使用 Process Substitution 来分离出一个process 执行 shell 代码, 通过 stdin stdout stderr 等来实现 process的处理结果

Flow Control:

  • if
1
2
3
4
5
6
7
  if commands; then 
    commands
  [elif commands; then 
    commands...]
  [else 
    commands]
  fi
  • exit status: 一般为 为一个 0-255 的数值, 0 代表 success, 其他值代表不同的错误。所以0 代表true,false 代表1, 不同于其他语言中的惯例。$? 代表 上个命令执行的返回结果
1
2
3
4
5
6
7
8
  root@precise64:~/shell_test# ls -d /usr/bin/
  /usr/bin/
  root@precise64:~/shell_test# echo $?
  0
  root@precise64:~/shell_test# ls -d /bin/usr
  ls: cannot access /bin/usr: No such file or directory
  root@precise64:~/shell_test# echo $?
  2
  • test: 配合if 使用,返回condition 结果: 两种形式. 成功返回0, 失败 返回1

    1
    2
    3
    
    test expression
    
    [ expression ]
    

    相关的测试expression

    • file Expression

      ExpressionIs True?
      file1 -ef file2file1 inode eq file2 inode true
      file1 -nt file2file1 is newer than file2
      file1 -ot file2file1 is older than file2
      -b filefile exist and is a block-special file
      -c filefile exist and is a char-special file
      -d filefile exists and is a dir
      -e filefile exists
      -f filefile exists and a regular file
      -g filefile exists and is set-group-id
      -G filefile exists and is owner by the effective group ID
      -k filefile exists and has its ‘sticky bit’ sit
      -L filefile exists and is a sym link
      -p filefile exists and a named pipe
      -r filefile exists and can readable
      -s filefile exists and has a length greater than zero
      -S filefile exists and a socket
      -w filefile exists and writable
      -x fielfile exists and executeable
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
      if [ -e "$FILE" ]; then
          if [ -f "$FILE" ]; then
              echo "$FILE is a regular file."
          fi
          if [ -d "$FILE" ]; then
              echo "$FILE is a directory."
          fi
          if [ -r "$FILE" ]; then
              echo "$FILE is readable."
          fi
      fi
    
  • string expression

    ExpressionMeaning
    stringstring is not null
    -n stringstring len is not zero
    -z stringstring len is zero
    str1 == str2equal
    str1 != str2not equal
    str1 > str2str1 sorts after str2
    str1 < str2str1 sort before str2
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    
     ANSWER=maybe
     if [ -z "$ANSWER" ]; then
         echo "There is no answer." >&2
         exit 1
     fi
     if [ "$ANSWER" = "yes" ]; then
         echo "The answer is YES."
     elif [ "$ANSWER" = "no" ]; then
         echo "The answer is NO."
     elif [ "$ANSWER" = "maybe" ]; then
         echo "The answer is MAYBE."
     else
         echo "The answer is UNKNOWN."
     fi
    

    这里面 需要注意: the > and < expression operators must be quoted (or escaped with a backslash) when used with test. If they are not, they will be interpreted by the shell as redirection operators

  • Integer Expressions

    ExpressionMeaning
    int1 -eq int2equal
    int1 -ne int2not equal
    int1 -le int2int1 <= int2
    int1 -lt int2int1 < int2
    int1 -ge int2int1 >= int2
    int1 -gt int2int1 > int2
  • [[ expression ]] : 类似于 [ expression ], 区别在于: 增加了 string1 =~ regex 测试方法 2. == 支持 pathname expansion 检测
  • (( )): Designed for Integers, 用于测试 数学计算, 如果数值 为非0 则是true, 0则为false。在(()) 中变量可以直接 使用,不用带 $, 例如, 在其中可以使用所有的数学表达式,比如 >, <, >=, <=, ==, %, /, * etc
1
2
3
4
  INT=5
  if ((INT == 0)); then
      echo "INT is zero"
  fi
  • Combining Expressions:

    Operationtest[[]] and (())
    AND-a&&
    OR-o 
    NOT!!
  • for example:

    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
    
    #!/bin/bash
    
    MIN_VAL=1
    MAX_VAL=100
    
    
    INT=50
    if [[ "$INT" =~ ^-?[0-9]+$ ]]; then
        if [[ "$INT" -ge "$MIN_VAL" && "$INT" -le "$MAX_VAL" ]]; then
            echo "$INT is within $MIN_VAL to $MAX_VAL."
        else
            echo "$INT is out of range."
        fi
    else
        echo "INT is not an integer." >&2
        exit 1
    fi
    
    
    # 等价的另一种方式
    
    if [ "$INT" -ge "$MIN_VAL" -a "$INT" -le "$MAX_VAL" ]; then
      echo "$INT is within $MIN_VAL to $MAX_VAL."
    else
      echo "$INT is out of range."
    fi
    

read: read 用于 从标准输入中 读取数值。

OptionsDesc
-a array将输入赋值(转化)给 数组
-e使用 Readline 模式 处理输入
-i string默认数值,在玩家仅仅按 enter的时候 有用
-p prompt输入的 提示信息
-rRaw mode. Do not interpret backslash characters as escapes.
-sslient mode, 用于密码输入
-t secondsTimeout after seconds
-u fd使用file 作为输入,而不是标准输入
  • read:将 标准输入 转到 变量 的使用格式: read [-options] [variable…]
  • read 存在默认变量 REPLY,即当没有 variable 传递的时候
  • for example
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
  #!/bin/bash

  echo -n "please enter an integer -> "
  read int

  if [[ "$int" =~ ^-?[0-9]+$ ]]; then
      if (( int == 0 )); then
          echo "int is zero"
      else
          if (( int < 0)); then
              echo "$int is negative"
          else
              echo "$int is positive"
          fi
      fi
  fi

  #  read 多个var 测试, 与ruby array 复制差不多,
  # 即是:当多个 参数数目 > 变量数目 时 剩余的变量为空值,当 参数数目 < 变量数目时 最后后一个变量 存储多个数值

  #!/bin/bash
  # read-multiple: read multiple values from keyboard
  echo -n "Enter one or more values > "
  read var1 var2 var3 var4 var5
  echo "var1 = '$var1'"
  echo "var2 = '$var2'"
  echo "var3 = '$var3'"
  echo "var4 = '$var4'"
  echo "var5 = '$var5'"

  vagrant@precise64:/vagrant_data/shell_test$ ./read-multiple.sh
  Enter one or more values > 1 2 3 4 4 45 5
  var1 = '1'
  var2 = '2'
  var3 = '3'
  var4 = '4'
  var5 = '4 45 5'

  vagrant@precise64:/vagrant_data/shell_test$ ./read-multiple.sh
  Enter one or more values > 1
  var1 = '1'
  var2 = ''
  var3 = ''
  var4 = ''
  var5 = ''

  # read 不传递 var 时候,默认使用变量 REPLY
  #!/bin/bash
  # read-single: read multiple values into default variable
  echo -n "Enter one or more values > "
  read
  echo "REPLY = '$REPLY'"

  vagrant@precise64:/vagrant_data/shell_test$ ./read-single.sh
  Enter one or more values > 1
  REPLY = '1'



  • IFS (Internal Field Separator) : 用来控制 read 分隔line 的分隔符, 默认的IFS 包含 space, tab, newline
  • 使用read的 这种方式, 可以很简单的 做 字符串的 split

    1
    2
    
    IFS=":" read user pw uid gid name home shell <<< "root:x:0:0:root:/root:/bin/bash"
    echo "string: ${user}, pw: ${pw}, uid: ${uid}, shell: ${shell}"
    

Flow Control: Looping with while/until

  • while: shell 中 同样存在 continue break 可供使用,以便提前 循环、或者 终止循环

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
      while commands; do
          commands
      done
    
    
      count=1
      while [[ "$count" -le 5 ]];do 
          echo "$count"
          count=$((count + 1))
      done
      echo "Finished."
    
  • until: 与while 同样的基本结构,不过测试条件相反

    1
    2
    3
    4
    5
    6
    7
    
    #!/bin/bash
    # until-count: display a series of numbers count=1
    until [[ "$count" -gt 5 ]]; do
        echo "$count"
        count=$((count + 1))
    done
    echo "Finished."
    

    读取文件的循环示例:

    1
    2
    3
    4
    5
    6
    7
    8
    
      #!/bin/bash
      # while-read: read lines from a file
      while read distro version release; do
          printf "Distro: %s\tVersion: %s\tReleased: %s\n" \
              "$distro" \
              "$version" \
              "$release"
      done < distros.txt
    
  • shell 的测试: 1) 除了 print 大法好(在这里是echo 之外) 2) #!/bin/bash -x 这样在脚本运行期间,将会 展示详细信息, 可以 使用 set +x 关闭 tracing set -x开启 tracing

  • case: 使用 pattern 进行匹配,) 结束 匹配。 还可以参考链接 http://tiswww.case.edu/php/chet/bash/bashref.html#SEC21 http://tldp.org/LDP/abs/html/testbranch.html 因为这里面介绍的十分简单

    PatternDesc
    a)Match if word equal ‘a’
    [[:aplpha:]])Match if word is a single alphabetic char
    ???)Match if word is exactly three char long
    *.txt)Match if word ends with the char “.txt”
    *)Matches anyy value of word
    1
    2
    3
    
    case word in
        [pattern [| pattern]...) commands ;;]...
    esac
    

    for example

    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
    
      #!/bin/bash -x
    
      read -p "Enter selection [0-3] > "
      case "$REPLY" in
          0) echo "Program terminated."
             exit
             ;;
          1) echo "Hostname: $HOSTNAME"
             uptime
             ;;
          2) df -h
             ;;
          3) if [[ "$(id -u)" -eq 0 ]]; then
                 echo "Home Space Utilization (All Users)"
                 du -sh /home/*
             else
                 echo "Home Space Utilization ($USER)"
                 du -sh "$HOME"
             fi
             ;;
          *) echo "Invalid entry" >&2
             exit 1
             ;;
      esac
    
    
  • for loop:

    • 传统形式

      1
      2
      3
      
         for variable [in words]; do 
           commands
         done
      
    • c语言形式: 只支持 在对 数字 进行操作的时候

      1
      2
      3
      4
      5
      6
      7
      8
      9
      
      ```shell
      for (( expression1; expression2; expression3 )); do
        commands
      done
      
      for (( i=0; i<5; i=i+1 )); do
          echo $i
      done
      ```
      
    • example

      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
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      
        [me@linuxbox ~]$ for i in A B C D; do echo $i; done
        A
        B
        C
        D
      
      
        for i in {A..D}; do echo $i; done
        A
        B
        C
        D
      
        [me@linuxbox ~]$ for i in distros*.txt; do echo "$i"; done
        distros-by-date.txt
        distros-dates.txt
        distros-key-names.txt
        distros-key-vernums.txt
        distros-names.txt
        distros.txt
        distros-vernums.txt
        distros-versions.txt
      
      
      
        # ./for_test.sh file
        for i; do
            echo "i in ---------- ${i} \n"
        done
      
      
        # 可以使用如此的方式,循环打印 command line 参数
        root@precise64:/vagrant_data/shell_test# ./for_test.sh  a b c d e f j
        i in ---------- a \n
        i in ---------- b \n
        i in ---------- c \n
        i in ---------- d \n
        i in ---------- e \n
        i in ---------- f \n
        i in ---------- j \n
      
      

Accessing the Command Line:

  • shell 中 通过 $0-$9 来接受 command line 传递的参数。其中9 并不是 参数个数的上线,可以使用 更多的比如 $11, $100000 来使用 第 1000000 个参数。
  • $# 标志 参数个数。其中 $0 总是为 shell本身
  • shift: shift可以将 将 $1后续的变量,转移到 $1上, 同时 $# 减少

    1
    2
    3
    4
    5
    6
    7
    
      #!/bin/bash
      # posit-param2: script to display all arguments count=1
      while [[ $# -gt 0 ]]; do
        echo "Argument $count = $1"
        count=$((count + 1))
        shift
      done
    
  • 函数 传递参数的方式 同上,使用 $0, $1 etc
  • 常量: PROGNAME当前shell运行的函数, FUNCNAME 为shell当前运行的shell函数
  • $* $@ “$*” “$@” : “$@” 在这里面是最为 重要的,因为保留了 原来参数传递的样式

    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    
       // fun_test.sh file
       #!/bin/bash
       # posit-params3: script to demonstrate $* and $@
       print_params () {
           echo "\$1 = $1"
           echo "\$2 = $2"
           echo "\$3 = $3"
           echo "\$4 = $4"
       }
       pass_params () {
           echo -e "\n" '$*'; print_params $*
           echo -e "\n" '$*'; print_params "$*"
           echo -e "\n" '$@'; print_params $@
           echo -e "\n" '$@'; print_params "$@"
       }
       pass_params "word" "words with spaces"
    
    
       // ./fun_test.sh 测试
       root@precise64:/vagrant_data/shell_test# ./fun_test.sh
    
        $*
       $1 = word
       $2 = words
       $3 = with
       $4 = spaces
    
        $*
       $1 = word words with spaces
       $2 =
       $3 =
       $4 =
    
        $@
       $1 = word
       $2 = words
       $3 = with
       $4 = spaces
    
        $@
       $1 = word
       $2 = words with spaces
       $3 =
       $4 =
    

    Strings and Numbers

    expressionmeaning
    ${para:-word}para 为空 express result 为 word
    ${para:=word}para 为空 express & para result 为 word (position 参数不能够如此赋值)
    ${pars:?word}pars 为空 则exit,word被输出到 stderr
    ${para:+word}para不为空,则 expres 为 word
    ${!prefix*} 或者 ${!prefix@}返回 以 prefix 为前缀的 变量名称
    ${#para}para length, 如果 para 为 @ 或者 * 则 展开为 command line params size
    ${para:offset} ${para:offset:length}用于string 的片段截取,没有length时候,则直到末尾, para为 @时候, 则为 参数 从 offset开始 到结尾
    ${para#pattern} ${para##pattern}将字符串remove pattern match的部分,结果为 剩下的部分, #pattern remove 最短的 match 部分, ## 则remove 最长的match
    ${para%pattern} ${para%%pattern}同上,但是 remove 片段从string 的末尾开始,而非开头开始
    ${para/pattern/string} ${para//pattern/string} ${para/#pattern/string} ${para/%pattern/string}string 的查找替换操作,使用 string 替换 para中的 pattern matched,第一个只替换第一个, 第二个则全部替换, 第三个 则替换开头, 第四个只替换末尾
  • case conversion parameter

    FormatResult
    ${pars,,}展开为 para 的 全部小写形式
    ${para,}展开式 para 首字母 小写
    ${para^^}展开为 para 的全部 大写形式
    ${para^}展开为 para 首字母 大写形式

数字操作: $((expression)) 基本形式

  • Operators

    OperatorDesc  
    +   
    -*  
    *   
    /   
    **   
    %   
    para = value   
    para += value   
    para -= value   
    para *= value   
    para /= value   
    para %= value   
    para ++   
    para –   
    ++ para   
    – para   
    <=   
    >=   
    <   
    >   
    !=   
    &&   
    expre1?expre2:expre3   

Array: bash version2 才得到支持,在原先的shell中 并不支持 array

  • Create a Array :

    1
    2
    3
    4
    
    a[1]=foo
    echo ${a[1]}
    
    declare -a a
    
  • Assigning Values to an Array: name[subscript]=value; name=(value1 value2 …)
  • Array Operations: 遍历数组

    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    
     [me@linuxbox ~]$ animals=("a dog" "a cat" "a fish") 
     [me@linuxbox ~]$ for i in ${animals[*]}; do echo $i; done 
     a
     dog
     a
     cat
     a
     fish
    
     [me@linuxbox ~]$ for i in ${animals[@]}; do echo $i; done a
     dog
     a
     cat
     a
     fish
    
     [me@linuxbox ~]$ for i in "${animals[*]}"; do echo $i; done
     a dog a cat a fish
    
     [me@linuxbox ~]$ for i in "${animals[@]}"; do echo $i; done
     a dog
     a cat
     a fish
    
     # "${!array[*]}", "${!array[@]}"
     [me@linuxbox ~]$ foo=([2]=a [4]=b [6]=c)
    
     [me@linuxbox ~]$ for i in "${foo[@]}"; do echo $i; done 
     a
     b
     c
    
     # 展示数组 有value的 indexs
     [me@linuxbox ~]$ for i in "${!foo[@]}"; do echo $i; done
     2
     4
     6
    
    
  • Sorting an Array:

    1
    2
    3
    4
    5
    6
    
     #!/bin/bash
     # array-sort: Sort an array a=(f e d c b a)
     echo "Original array: ${a[@]}"
     # 传统的数组排序方式,因为shell并不会构建复杂的 类型系统 来进行 数组函数排序
     a_sorted=($(for i in "${a[@]}"; do echo $i; done | sort))
     echo "Sorted array: ${a_sorted[@]}"
    
  • Deleting an Array: unset array; unset ‘array[index]’ array=xxxx 修改 array[0] 中的数值

    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
    28
    29
    
     [me@linuxbox ~]$ foo=(a b c d e f)
     [me@linuxbox ~]$ echo ${foo[@]}
     a b c d e f
     [me@linuxbox ~]$ unset foo
     [me@linuxbox ~]$ echo ${foo[@]}
    
     [me@linuxbox ~]$
    
    
     [me@linuxbox ~]$ foo=(a b c d e f)
     [me@linuxbox ~]$ echo ${foo[@]}
     a b c d e f
     [me@linuxbox ~]$ unset 'foo[2]'
     [me@linuxbox ~]$ echo ${foo[@]}
     a b d e f
    
    
     [me@linuxbox ~]$ foo=(a b c d e f)
     [me@linuxbox ~]$ foo=
     [me@linuxbox ~]$ echo ${foo[@]}
     b c d e f
    
    
     [me@linuxbox ~]$ foo=(a b c d e f)
     [me@linuxbox ~]$ echo ${foo[@]}
     a b c d e f
     [me@linuxbox ~]$ foo=A
     [me@linuxbox ~]$ echo ${foo[@]}
     A b c d e f
    

Group Commands and Subshells:

  • Group Command: { command1; command2; [command3; …] }
  • Subshells: ( command1; command2; [command3;…] ) group commands 可以将 其中的command 的结果 很方便的 合并到一个 数据流汇总,例如:

    1
    2
    3
    4
    5
    6
    7
    8
    
     ( ls -l; echo "Listing of foo.txt"; cat foo.txt) > output.txt
     { ls -l; echo "Listing of foo.txt"; cat foo.txt; } > output.txt
    
     #等同于:
     ls -l > output.txt
     echo "Listing of foo.txt" >> output.txt
     cat foo.txt >> output.txt
    
    

Process Substitution: 区别于 group commands , Process sub 运行在 子进程 中,而group command 则运行在当前进程中, 所以从效率上来说 group command 要快于 process substitution, 该技术使得 子进程 中的输出 到当前进程中 进行处理。通常 将 子进程中的数据流 输出到当前 进程使用 read 处理。

  • 形式为: <(list), >(list)。 for example

    1
    2
    3
    4
    5
    
     #!/bin/bash
     # pro-sub: demo of process substitution
     while read attr links owner group size date time filename; do
     done < <(ls -l | tail -n +2)
    
  • Traps: 处理 信号。trap argument signal [signal…] 其中 argument 为string, 例如:

    1
    2
    3
    4
    
    trap "echo 'I am ignoring you.'" SIGINT SIGTERM
    
    trap exit_on_signal_SIGINT SIGINT
    trap exit_on_signal_SIGTERM SIGTERM
    

    Shell 使用中的一些问题:

  • IFS 的重要性, IFS 对于 shell处理非常重要, 比如 echo的使用,在如下场景中存在问题:
    通过 tail -c offset file 来获取 文件最新追加数据, 并保存到var(变量) 中, 以供后续 处理,匹配。

    1
    2
    3
    
    content=$(tail -c ${offset} ${file})
    echo ${content} // 会导致  newline (\n) 丢失, 丢失,导致后面的 grep 可能失败, 不能够正常表现, 所以需要 使用 echo "${content}" 来实现 newline 的保留, (newline 并没有因为保存在变量中,而被丢弃,只是因为IFS 包含 newline 没有表现出来) , 相关参考在 https://www.baeldung.com/linux/variable-preserve-linebreaks?utm_source=pocket_mylist
    match=$(echo "${content}" | grep 'app error: exception:' |  grep -E "unexpected token at 'null' params|undefined method \`split' for nil:NilClass" -v)
    

错误重定向: /home/shaohua.li/error_monitor.sh » /home/shaohua.li/crontab_shell/task.log 2»&1 错误,在bash中会产生错误, zsh 则没有问题。所以这里的写法 应该是 » xxxx 2>&1 2会追加到 1 所以应该控制 1 的打开方式,但是这是否会存在 1 为 write, 2 也只能为 write 不能为append? 这里面的思考方式,应该是 写程序, 关于fd 的方式 (透过现象看本质,即 同一个程序 使用同一个 fd,以及打开fd 两次, 导致的偏移量的区别)

  • cd , cd ~, cd ~user_name
  • options & arguments: command -options arguments, GNU project also support two dash command –options arguments,
  • mkdir directory… When three periods follow an argument in the description of a com- mand (as above), it means that the argument can be repeated, thus the following com- mand:
  • ls -a (list all files) -A (like -a not include . and ..) -l (long format) -h (long fomart display file size in k,m,g ) -S (sort by file size) -t (sort by modification time) -r (reverse order)
  • Wildcards expand : t provides special characters to help us rapidly specify groups of filenames.:
    • (any)
  • 单个位置: ? (single char) [chars] (match any chars that is a member of this set), [!characters] Character Ranges [A-Z] [a-z] [0-9] 可嵌套:[[:lower:]123]
This post is licensed under CC BY 4.0 by the author.