前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux Shell 基础

Linux Shell 基础

作者头像
用户2987604
发布2020-06-15 15:57:11
11.9K0
发布2020-06-15 15:57:11
举报

接触过一些 shell 脚本,做服务端运维时也时常用到,是时候专门学习一下了。

基础

Here Script

使用 _EOF_ 将多行语句作为单句,避免转义字符的麻烦:

代码语言:javascript
复制
# echosecho "<html>"echo "<head>"echo "</head>"echo "</html>"
echo "<html> <head> </head> </html>"
cat << _EOF_<html><head></head></html>_EOF_

Variables

Rules:

  1. Names must start with a letter.
  2. A name must not contain embedded spaces. Use underscores instead.
  3. You cannot use punctuation marks.
代码语言:javascript
复制
#!/bin/bashtitle="System Information for"echo $title

Environment Variables

脚本文件启动前,系统已预设一些环境变量,在命令行中使用 printenv 查看这些变量:

代码语言:javascript
复制
[root@bhc004 ~]# printenvXDG_SESSION_ID=87HOSTNAME=bhc004TERM=linuxSHELL=/bin/bashHISTSIZE=10000SSH_CLIENT=125.117.180.232 57544 22SSH_TTY=/dev/pts/0USER=rootLS_COLORS=rs=0:di=01;34:ln=01;``````.spx=01;36:*.xspf=01;36:MAIL=/var/spool/mail/rootPATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/usr/local/jdk/binPWD=/rootLANG=en_US.UTF-8HISTCONTROL=ignoredupsSHLVL=1HOME=/rootLOGNAME=rootSSH_CONNECTION=125.117.180.232 57544 192.168.0.182 22LESSOPEN=||/usr/bin/lesspipe.sh %sXDG_RUNTIME_DIR=/run/user/0HISTTIMEFORMAT=%F %T root_=/usr/bin/printenv

在 shell 中可以使用这些环境变量 echo $HOSTNAME

命令替换和常量

Command Substitution and Constants

命令替换

代码语言:javascript
复制
echo Updated on $(date +"%x %r %Z") by $USER

$(date+"%x %r %Z")$() 告诉 shell 替换附带命令的执行结果,即插入 date+"%x %r %Z 的执行结果(当前时间)。

Be aware that there is an older, alternate syntax for "$(command)" that uses the backtick character "`". This older form is compatible with the original Bourne shell (sh). I tend not to use the older form since I am teaching modern bash here, not sh, and besides, I think backticks are ugly. The bash shell fully supports scripts written for sh, so the following forms are equivalent: $(command) `command`

把命令执行结果赋值给变量:

代码语言:javascript
复制
right_now=$(date +"%x %r %z")

常量

代码语言:javascript
复制
RIGHT_NOW=$(date +"%x %r %Z")TIME_STAMP="Updated on $RIGHT_NOW by $USER"

很少用,使用大写字母定义常量名(非强制)。

函数

自定义函数

代码语言:javascript
复制
#!/bin/bash
#### Functionsshow_uptime() {    echo "<h2>System uptime</h2>"    echo "<pre>"    uptime    echo "</pre>"}
drive_space() {    echo "<h2>Filesystem space</h2>"    echo "<pre>"    df    echo "</pre>"}
cat << _EOF_  $(show_uptime)  $(drive_space)_EOF_

函数参数

代码语言:javascript
复制
function generate_instance_conf() {  echo "configuring server $1"}generate_instance_conf server1

输出:

代码语言:javascript
复制
configuring server server1

date 格式化

Following are the list of available options for date command :

Format option

Part of Date

Desciption

Example Output

date +%a

Weekday

Name of weekday in short (like Sun, Mon, Tue, Wed, Thu, Fri, Sat)

Mon

date +%A

Weekday

Name of weekday in full (like Sunday, Monday, Tuesday)

Monday

date +%b

Month

Name of Month in short (like Jan, Feb, Mar )

Jan

date +%B

Month

Month name in full short (like January, February)

January

date +%d

Day

Day of month (e.g., 01)

04

date +%D

MM/DD/YY

Current Date; shown in MM/DD/YY

02/18/18

date +%F

YYYY-MM-DD

Date; shown in YYYY-MM-DD

2018-01-19

date +%H

Hour

Hour in 24-hour clock format

18

date +%I

Hour

Hour in 12-hour clock format

10

date +%j

Day

Day of year (001..366)

152

date +%m

Month

Number of month (01..12) (01 is January)

05

date +%M

Minutes

Minutes (00..59)

52

date +%S

Seconds

Seconds (00..59)

18

date +%N

Nanoseconds

Nanoseconds (000000000..999999999)

300231695

date +%T

HH:MM:SS

Time as HH:MM:SS (Hours in 24 Format)

18:55:42

date +%u

Day of Week

Day of week (1..7); 1 is Monday

7

date +%U

Week

Displays week number of year, with Sunday as first day of week (00..53)

23

date +%Y

Year

Displays full year i.e. YYYY

2018

date +%Z

Timezone

Time zone abbreviation (Ex: IST, GMT)

IST

You may use any of the above-mentioned format options (first column) for the date command in the aforementioned syntax.

流控制

if

if 语法如下:

代码语言:javascript
复制
if commands; thencommands[elif commands; thencommands...][elsecommands]fi

Exit Status

退出状态,即命令执行后会给系统一个值,0-255,0 代表 success:

代码语言:javascript
复制
[root@bhc004 ~]# true[root@bhc004 ~]# echo $?0[root@bhc004 ~]# false[root@bhc004 ~]# echo $?1

test

代码语言:javascript
复制
# First formtest expression# Second form[ expression ]

test 命令和 if 命令一起完成 true /false 判断,当表达式为 true,test 以 0 退出;为 false,test 以 1 退出。

例如:

代码语言:javascript
复制
if [ -f .bash_profile ]; then    echo "You have a .bash_profile. Things are fine."else    echo "Yikes! You have no .bash_profile!"fi

表达式 “-f .bashprofile” 表示 .bashprofile 是一个文件,若为 true,则执行 then 后的命令;否则执行 else 后的命令。

test 可以评估的表达式如下:

Expression

Description

-d file

True if file is a directory.

-e file

True if file exists.

-f file

True if file exists and is a regular file.

-L file

True if file is a symbolic link.

-r file

True if file is a file readable by you.

-w file

True if file is a file writable by you.

-x file

True if file is a file executable by you.

file1 -nt file2

True if file1 is newer than (according to modification time) file2

file1 -ot file2

True if file1 is older than file2

-z string

True if string is empty.

-n string

True if string is not empty.

string1 = string2

True if string1 equals string2.

string1 != string2

True if string1 does not equal string2.

不同的书写格式:

代码语言:javascript
复制
# Alternate formif [ -f .bash_profile ]then    echo "You have a .bash_profile. Things are fine."else    echo "Yikes! You have no .bash_profile!"fi
# Another alternate formif [ -f .bash_profile ]then echo "You have a .bash_profile. Things are fine."else echo "Yikes! You have no .bash_profile!"fi

exit

exit 命令可以立即结束此脚本,并设置退出状态:

代码语言:javascript
复制
exit 0

test for superuser

id 命令可以查看当前用户信息:

代码语言:javascript
复制
[root@bhc004 ~]# iduid=0(root) gid=0(root) groups=0(root)[root@bhc004 ~]# id -u0
代码语言:javascript
复制
if [ $(id -u) != "0" ]; then    # >&2 输出到标准错误    echo "You must be the superuser to run this script" >&2    # 1 向操作系统表示脚本执行不成功    exit 1else    echo "superuser"fi

Watching your script

在第一行加 +x 监控脚本执行状态:

代码语言:javascript
复制
#!/bin/bash -x

或者,使用 set-xset+x监控一段代码:

代码语言:javascript
复制
#!/bin/bash
number=1
set -xif [ $number = "1" ]; then    echo "Number equals 1"else    echo "Number does not equal 1"fiset +x

键盘输入和算术

用户交互。

read

从键盘输入并赋值给变量:

代码语言:javascript
复制
#!/bin/bash
echo -n "Enter some text > "read textecho "You entered: $text"

-n 使输入和字符串在同一行。read 有 -t-s 等参数:

  • -t 表示在指定时间内获得响应,当无论用户是否输入都要继续执行时使用;
  • is 表示不显示输入的内容;当输入密码时使用;
代码语言:javascript
复制
#!/bin/bash
echo -n "Hurry up and type something! > "if read -t 3 response; then    echo "Great, you made it in time!"else    echo "Sorry, you are too slow!"fi

算术

使用双括号计算算术表达式:

代码语言:javascript
复制
#!/bin/bash
first_num=0second_num=0
echo -n "Enter the first number --> "read first_numecho -n "Enter the second number -> "read second_num
echo "first number + second number = $((first_num + second_num))"echo "first number - second number = $((first_num - second_num))"echo "first number * second number = $((first_num * second_num))"echo "first number / second number = $((first_num / second_num))"echo "first number % second number = $((first_num % second_num))"echo "first number raised to the"echo "power of the second number   = $((first_num ** second_num))"

括号内的变量不需要 $ 就可以引用,

代码语言:javascript
复制
#!/bin/bash
number=0
echo -n "Enter a number > "read number
echo "Number is $number"if [ $((number % 2)) -eq 0 ]; then    echo "Number is even"else    echo "Number is odd"fi

流控制 2

更过分支:

代码语言:javascript
复制
#!/bin/bash
echo -n "Enter a number between 1 and 3 inclusive > "read characterif [ "$character" = "1" ]; then    echo "You entered one."elif [ "$character" = "2" ]; then    echo "You entered two."elif [ "$character" = "3" ]; then    echo "You entered three."else    echo "You did not enter a number between 1 and 3."fi

使用 case 优化分支结构

case

case 语句格式如下:

代码语言:javascript
复制
case word in    patterns ) commands ;;esac

改造如上语句:

代码语言:javascript
复制
#!/bin/bash
echo -n "Enter a number between 1 and 3 inclusive > "read charactercase $character in    1 ) echo "You entered one."        ;;    2 ) echo "You entered two."        ;;    3 ) echo "You entered three."        ;;    * ) echo "You did not enter a number between 1 and 3."esac

case 也可以匹配表达式:

代码语言:javascript
复制
#!/bin/bash
echo -n "Type a digit or a letter > "read charactercase $character in                                # Check for letters    [[:lower:]] | [[:upper:]] ) echo "You typed the letter $character"                                ;;
                                # Check for digits    [0-9] )                     echo "You typed the digit $character"                                ;;
                                # Check for anything else    * )                         echo "You did not type a letter or a digit"esac

* 通常用来检测无效输入。

循环

while
代码语言:javascript
复制
#!/bin/bash
number=0while [ "$number" -lt 10 ]; do    echo "Number = $number"    number=$((number + 1))done
util
代码语言:javascript
复制
#!/bin/bash
number=0until [ "$number" -ge 10 ]; do    echo "Number = $number"    number=$((number + 1))done
自定义菜单
代码语言:javascript
复制
#!/bin/bash
selection=until [ "$selection" = "0" ]; do    echo "    PROGRAM MENU    1 - Display free disk space    2 - Display free memory
    0 - exit program"    echo -n "Enter selection: "    read selection    echo ""    case $selection in        1 ) df ;;        2 ) free ;;        0 ) exit ;;        * ) echo "Please enter 1, 2, or 0"    esacdone

位置参数

代码语言:javascript
复制
#!/bin/bash
echo "Positional Parameters"echo '$0 = ' $0echo '$1 = ' $1echo '$2 = ' $2echo '$3 = ' $3
if [ "$1" != "" ]; then    echo "Positional parameter 1 contains something"else    echo "Positional parameter 1 is empty"fi

命令行选项

代码语言:javascript
复制
interactive=filename=~/sysinfo_page.html
while [ "$1" != "" ]; do    case $1 in        -f | --file )           shift                                filename=$1                                ;;        -i | --interactive )    interactive=1                                ;;        -h | --help )           usage                                exit                                ;;        * )                     usage                                exit 1    esac    shiftdone

shift

shift 是内建命令,每次调用 shift 使所有参数的索引减一,即 $2 变成 $1,$3 变成 $2:

代码语言:javascript
复制
#!/bin/bash
echo "You start with $# positional parameters"
# Loop until all parameters are used upwhile [ "$1" != "" ]; do    echo "Parameter 1 equals $1"    echo "You now have $# positional parameters"
    # Shift all the parameters down by one    shift
done

命令行处理器

代码语言:javascript
复制
#!/bin/bash
# sysinfo_page - A script to produce a system information HTML file
##### Constants
TITLE="System Information for $HOSTNAME"RIGHT_NOW=$(date +"%x %r %Z")TIME_STAMP="Updated on $RIGHT_NOW by $USER"
##### Functions
system_info(){    echo "<h2>System release info</h2>"    echo "<p>Function not yet implemented</p>"
}   # end of system_info

show_uptime(){    echo "<h2>System uptime</h2>"    echo "<pre>"    uptime    echo "</pre>"
}   # end of show_uptime

drive_space(){    echo "<h2>Filesystem space</h2>"    echo "<pre>"    df    echo "</pre>"
}   # end of drive_space

home_space(){    # Only the superuser can get this information
    if [ "$(id -u)" = "0" ]; then        echo "<h2>Home directory space by user</h2>"        echo "<pre>"        echo "Bytes Directory"        du -s /home/* | sort -nr        echo "</pre>"    fi
}   # end of home_space

write_page(){    cat <<- _EOF_    <html>        <head>        <title>$TITLE</title>        </head>        <body>        <h1>$TITLE</h1>        <p>$TIME_STAMP</p>        $(system_info)        $(show_uptime)        $(drive_space)        $(home_space)        </body>    </html>_EOF_
}
usage(){    echo "usage: sysinfo_page [[[-f file ] [-i]] | [-h]]"}

##### Main
interactive=filename=~/sysinfo_page.html
while [ "$1" != "" ]; do    case $1 in        -f | --file )           shift                                filename=$1                                ;;        -i | --interactive )    interactive=1                                ;;        -h | --help )           usage                                exit                                ;;        * )                     usage                                exit 1    esac    shiftdone

# Test code to verify command line processing
if [ "$interactive" = "1" ]; then    response=
    echo -n "Enter name of output file [$filename] > "    read response    if [ -n "$response" ]; then        filename=$response    fi
    if [ -f $filename ]; then        echo -n "Output file exists. Overwrite? (y/n) > "        read response        if [ "$response" != "y" ]; then            echo "Exiting program."            exit 1        fi    fifiecho "output file = $filename"

# Write page (comment out until testing is complete)
# write_page > $filename

for 循环

处理位置参数

for 命令可以处理命令行参数列表:

代码语言:javascript
复制
#!/bin/bash
for i in "$@"; do    echo $idone

处理文件列表

比较两个文件夹中的文件:

代码语言:javascript
复制
#!/bin/bash
# cmp_dir - program to compare two directories
# Check for required argumentsif [ $# -ne 2 ]; then    echo "usage: $0 directory_1 directory_2" 1>&2    exit 1fi
# Make sure both arguments are directoriesif [ ! -d $1 ]; then    echo "$1 is not a directory!" 1>&2    exit 1fi
if [ ! -d $2 ]; then    echo "$2 is not a directory!" 1>&2    exit 1fi
# Process each file in directory_1, comparing it to directory_2missing=0for filename in $1/*; do    fn=$(basename "$filename")    if [ -f "$filename" ]; then        if [ ! -f "$2/$fn" ]; then            echo "$fn is missing from $2"            missing=$((missing + 1))        fi    fidoneecho "$missing files missing"

home_space

代码语言:javascript
复制
home_space(){    echo "<h2>Home directory space by user</h2>"    echo "<pre>"    format="%8s%10s%10s   %-s\n"    printf "$format" "Dirs" "Files" "Blocks" "Directory"    printf "$format" "----" "-----" "------" "---------"    if [ $(id -u) = "0" ]; then        dir_list="/home/*"    else        dir_list=$HOME    fi    for home_dir in $dir_list; do        total_dirs=$(find $home_dir -type d | wc -l)        total_files=$(find $home_dir -type f | wc -l)        total_blocks=$(du -s $home_dir)        printf "$format" $total_dirs $total_files $total_blocks    done    echo "</pre>"
}   # end of home_space

使用 printf 格式化输出,参考:

  • GNU Awk User's Guide - Control Letters
  • GNU Awk User's Guide - Format Modifiers

其它

鲁棒性

提高 shell 脚本的鲁棒性,处理 exit status,否则会发生意想不到的事情。

代码语言:javascript
复制
cd $some_directoryrm *

若 $somedirectory 不存在,进入指定目录失败,则删除当前工作文件夹下的所有文件,--

clean_up 功能

代码语言:javascript
复制
#!/bin/bash
# Program to print a text file with headers and footers
TEMP_FILE=/tmp/printfile.txt
clean_up() {
    # Perform program exit housekeeping    rm $TEMP_FILE    exit}
trap clean_up SIGHUP SIGINT SIGTERM
pr $1 > $TEMP_FILE
echo -n "Print file? [y/n]: "readif [ "$REPLY" = "y" ]; then    lpr $TEMP_FILEficlean_up

结束一个脚本时,应清理一些文件:

代码语言:javascript
复制
#!/bin/bash
# Program to print a text file with headers and footers
# Usage: printfile file
# Create a temporary file name that gives preference# to the user's local tmp directory and has a name# that is resistant to "temp race attacks"
if [ -d "~/tmp" ]; then    TEMP_DIR=~/tmpelse    TEMP_DIR=/tmpfiTEMP_FILE=$TEMP_DIR/printfile.$$.$RANDOMPROGNAME=$(basename $0)
usage() {
    # Display usage message on standard error    echo "Usage: $PROGNAME file" 1>&2}
clean_up() {
    # Perform program exit housekeeping    # Optionally accepts an exit status    rm -f $TEMP_FILE    exit $1}
error_exit() {
    # Display error message and exit    echo "${PROGNAME}: ${1:-"Unknown Error"}" 1>&2    clean_up 1}
trap clean_up SIGHUP SIGINT SIGTERM
if [ $# != "1" ]; then    usage    error_exit "one file to print must be specified"fiif [ ! -f "$1" ]; then    error_exit "file $1 cannot be read"fi
pr $1 > $TEMP_FILE || error_exit "cannot format file"
echo -n "Print file? [y/n]: "readif [ "$REPLY" = "y" ]; then    lpr $TEMP_FILE || error_exit "cannot print file"ficlean_up

安全的临时文件

/tmp 会有很多程序放置临时文件在里面,文件名重复会覆盖内容(可预测,predictable);

建议创建本地临时文件夹 ~/tmp,即用户目录下的子目录;

代码语言:javascript
复制
TEMP_FILE=$TEMP_DIR/printfile.$$.$RANDOM
  • $TEMP_DIR 是 /tmp~/tmp
  • printfile 是应用程序的名字
  • $$ 是 shell 变量,将 process id (pid) 嵌入文件名
  • $RANDOM 是追加随机数

这样就生成了唯一且不易重复的文件名,例如 tomcat

代码语言:javascript
复制
tomcat.5141431142496395497.9000tomcat.6486338835837423954.9000tomcat.8674130370990688323.9000tomcat-docbase.2197772788338189182.9000tomcat-docbase.30706145223021005.9000

参考

http://linuxcommand.org/lc3writingshell_scripts.php

(完)

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 董亮亮的开发笔记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 基础
    • Here Script
      • Variables
        • Environment Variables
        • 命令替换和常量
          • 命令替换
            • 常量
            • 函数
              • 函数参数
                • date 格式化
                • 流控制
                  • if
                    • Exit Status
                      • test
                        • exit
                          • test for superuser
                            • Watching your script
                            • 键盘输入和算术
                              • read
                                • 算术
                                • 流控制 2
                                  • case
                                    • 循环
                                      • while
                                      • util
                                      • 自定义菜单
                                  • 位置参数
                                    • 命令行选项
                                      • shift
                                        • 命令行处理器
                                        • for 循环
                                          • 处理位置参数
                                            • 处理文件列表
                                              • home_space
                                              • 其它
                                                • 鲁棒性
                                                  • clean_up 功能
                                                    • 安全的临时文件
                                                    • 参考
                                                    领券
                                                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档