Bash 快速入门

基础知识

三级权限

目录的写权限是写入目录表的权限,例如文件的创建和删除、更名。写入目录下的文件,不需要目录的写权限。

目录有执行权限意味着 分析路径名 过程中可检索该目录,例如 cat /a/b/c ,则 /a /a/b 需要有 x 权限,c 需要有 r 权限。

Sticky (t) 权限:文件而言目前没有用。但若目录有 wt 权限,则只能被文件主删除。

权限验证过程:进程的主、组属性与文件的主、组属性比较。root 不受上述限制。

umask 用于设置初始权限。umask 022 可放到 bash 文件中,只在此文件生效。

SUID 权限

实际 UID 和有效 UID:后者是用来进行权限判断的 UID。

chmod u+s query

SUID 使得用户可以通过文件主提供的程序,以文件主的权限访问文件,但这种访问依赖于文件主提供的程序,进行有限的访问

shell 的启动形式

  • Login Shell:通过登录启动的 Shell

    • 执行 profile 文件。/etc/profile ~/.bash_profile ~/.bash_login ~/.profile

    • 推出之执行 logout 文件。

  • Interactive Shell:交互式解释器的 Shell。通过输入命令回车可得到命令结果

    • 执行 $HOME/.bashrc
  • Non Interactive Shell:通过 bash scriptname 执行的脚本

BASH 的执行形式

  • Fork 执行

    • bash < cmd

    • bash cmd

    • bash -x cmd

    • bash cmd args

  • Execve 执行

    • . cmd args

重定向

stdin 的 fd = 0

输入

  • sort < file 将 file 定向为 sort 的 stdin

  • 使用定界符:

    1cat << TOAST
    2* Now : `date`
    3* My Home Directory is $HOME
    4TOAST
    

    定界符内转义等同于双引号

    加单引号禁止替换

    1cat << 'TOAST'
    2* Now : `date`
    3* My Home Directory is $HOME
    4TOAST
    5pwd
    
  • 从命令行获取信息作为标准输入

    1 <<<word
    2    base64 <<< meiyoumima
    3    base64 <<< 'mei you mi ma
    

输出

stdout, fd = 1; stderr, fd = 2

  • > fname 覆盖输出

  • >> fname 追加输出

  • 2> fname 仅 stderr 输出

  • 2>&1 stderr 合并到 stdout 输出

  • 管道

    • ls -l | grep '^d' 前一命令的stdout作后一命令的stdin

    • cc try.c -o try 2>&1 | more 前一命令的stdout+stderr作为下一命令的stdin

开头

1#!/bin/bash

表明解释器。

变量

定义

注意 等号两侧不许多余空格

1myString="hallo!"
2myFiles=`ls -al` # 取执行结果

访问

1echo $myString
2echo ${addr}

默认:未定义变量,变量值为空字符串

  • set -u 当引用一个未定义的变量时,产生一个错误

  • set +u 当引用一个未定义的变量时,认为是一个空串(默认情形)

  • set -x 执行命令前打印出shell替换后的命令及参数, 为区别于正常的 shell输出, 前面冠以+号

  • set +x 取消上述设置

打印

  1. echo

  2. printf

  • printf '\033[01;33mConnect to %s Network\n' $proto

编辑文件

可以使用 ed 命令

 1printf 'Input IP address of server computer: '
 2read addr
 3ed myap.conf > /dev/null 2>&1 << TOAST
 4/SERVER
 5.d
 6i
 7SERVER $addr
 8.wq
 9TOAST
10echo Bye

替换

now=date 以命令date 的stdout替换date

now=$(date) 以命令date 的stdout替换$(date)

读命令行参数

  • $0 脚本文件本身的名字

  • $1 $2 1号命令行参数, 2号命令行参数,以此类推

  • $# 命令行参数的个数

  • $* 等同于”$1 $2 $3 $4 …”

  • $@ 等同于”$1” ”$2” ”$3”

  • $? 上一命令的返回码

判断返回值是否为 0

1if [ $? -ne 0 ]; then
2    exit_with_error "Failed to build."
3    exit 1
4fi

条件

test

/usr/bin/[ 程序:要求其最后一个命令行参数必须为 ]/usr/bin/test 无此要求)

1test -r /etc/motd
2[ -r /etc/motd ]

文件特性检测

-f 普通文件 -d 目录文件 -r 可读 -w 可写 -x 可执行 -s size>0

1test -r /etc/motd && echo readable
2[ -r /etc/motd ] && echo readable

字符串比较

1[ "$a" = "" ] && echo empty string 注意:$a的引号
2test $# = 0 && echo "No argument“
3level=8
4[ $level=0 ] && echo level is Zero

整数的比较

  • -eq

  • -ne

  • -gt

  • -ge

  • -lt

  • -le

例:

1 test `ls | wc -l` -ge 100 && echo "Too many files"

逻辑运算

! NOT(非) -o OR (或) -a AND (与)

下面的语句判断参数是否为空:

1if [ ! -n "$1" ] ;then
2    echo "you have not input a word!"
3else
4    echo "the word you input is $1"
5fi

短路条件

1pwd
2DIR=/usr/bin
3[ -d $DIR ] && (
4  cd $DIR
5  echo "Current Directory is `pwd`"
6  echo "`ls | wc -l` files"
7)
8pwd

(list) 在子shell中执行命令表list { list;} 在当前shell中执行命令表list

if 分支

1if condition
2  then list
3elif condition
4  then list
5else
6  list
7fi

case 分支

1case word in
2pattern1) list1;;
3pattern2) list2;;
4...
5esac

expr

shell不支持除字符串以外的数据类型,不支持加减乘除等算数运算和关于字符 串的正则表达式运算

需要这些功能,借助于shell之外的可执行程序/usr/bin/expr实现

1x=`expr $a \* \( $b + $c \)`
2y=`expr \( $a + 4 \< $b \) \& \( $c != 8 \)`
3x=`expr $a '*' '(' $b + $c ')'`
4y=`expr '(' $a + 4 '<' $b ')' '&' '(' $c != 8 ')'`

搞颜色

1echo -e "\033[30m黑色字\033[0m"  
2echo -e "\033[31m红色字\033[0m"  
3echo -e "\033[32m绿色字\033[0m"  
4echo -e "\033[33m黄色字\033[0m"  
5echo -e "\033[34m蓝色字\033[0m"  
6echo -e "\033[35m紫色字\033[0m"  
7echo -e "\033[36m天蓝字\033[0m"  
8echo -e "\033[37m白色字\033[0m"  

循环

例:等待文件lockfile消失:

1while test -r lockfile
2do
3  sleep 5
4done
1for i in `seq 1 254`
2do
3  ping -c 1 -w 1 192.168.0.$i
4done

遍历参数

 1for i
 2do
 3  PIDS=`ps -e | awk '/[0-9]:[0-9][0-9] '$i'$/ { printf("%d ", $1);}'`
 4  if [ "$PIDs" = "" ]
 5  then
 6    echo -e "No \"$i\" is killed."
 7  else
 8    echo "kill $PIDS ($i)"
 9    kill $PIDS
10  fi
11done

退出

使用 exit RETVAL

1exit 0 #正常退出

取扩展名

1basename xx.txt .txt
2# result is xx

函数

申明

1exit_with_error() {
2    echo -e "\033 [31m[ERROR]\033 [0m$1"
3}

调用

1exit_with_error "Failed to build."

特殊变量

变量 含义
$0 当前脚本的文件名
$n 传递给脚本或函数的参数。n 是一个数字,表示第几个参数。例如,第一个参数是$1,第二个参数是$2。
$# 传递给脚本或函数的参数个数。
$* 传递给脚本或函数的所有参数:"$1", "$2", “$3”, 每个变量是独立的。
$@ 传递给脚本或函数的所有参数:"$1 $2 $3",代表
$? 上个命令的退出状态,或函数的返回值。
? 当前Shell进程ID。对于 Shell 脚本,就是这些脚本所在的进程ID。

参数扩展

“扩展”可以理解为 C 对指针的解引用。

例如:

 1myvar1=hello
 2myvar2=myvar1
 3
 4echo hello      # 输出 hello
 5echo myvar1     # 输出 myvar1
 6echo $myvar1    # 输出 hello
 7echo myvar2     # 输出 myvar2
 8echo $myvar2    # 输出 myvar1
 9echo ${!myvar2} # 输出 hello
10
11myvar3=myvar2
12echo ${!myvar3} # 输出 myvar1

$ 是进行一次扩展。

${!ID} 是进行两次扩展。

判断参数是否为空:

1a=${b-c}
2echo $a # 输出 c
3echo $b # 输出 
4echo $c # 输出 
1b=whatever
2a=${b:c}
3echo $a # 输出 whatever

加上等号,可以判断不存在且不为空值(即 b=

参阅

bash脚本快速入门

Bash bash中命令行选项/参数处理

Extract filename and extension in Bash