首页 > 代码库 > Linux 笔记--脚本

Linux 笔记--脚本

脚本的参数

参数变量

$*传递给脚本/函数的所有参数(把所有参数当成一个字符串)
$@参数名字
$0文件名字(first.sh…)
$#总共有多少个参数
$$脚本的 PID
$!上一个被执行的命令的 PID(后台运行的进程)
IFS=, # 将函数参数的分隔符改成用‘,‘ 例如 IFS=:


var=test
echo ${#var}
4
echo $#var
0var
echo $# == 0
# 2014-10-16 16:21:41

## 测试 $* 和 $# 的区别
function countargs {
    echo $# args;
}
countargs "$*"
countargs "$@"
## 将上面的代码保存为一个文件, sh FILENAME a b c
## 3 args
## 1 args

使用默认参数

${varname:-word}如果 varname 为 null,返回 word, (${count:-0})
${varname:+word}如果 varname 为 null,返回 null,否则返回 word,测试一个变量的存在性.${count:+1}返回 1,即‘true‘
${varname:=word}如果 varname 为 null,将其设置为 word, 不能设置位置参数和特殊参数
${varname:?message}如果 varname 为 null,打印 varname:后跟信息 message,并退出当前命令或脚本,捕获为定义变量导致的错误
${varname:NUM返回从 NUM 开始的字符串, 开头以 0 计算
${varname:NUM:LENG返回从 NUM 开始的 LENG 长度字符串



count=frogfotman
${count:4}    返回 footman
${count:4:4}  返回 foot

sort -nr $1 | head -${2:-10}   # the first version
# 当后面没有接文件的时候, 会有一些奇怪的信息

filename=${1:?"filename missing"}
howmany=${2:-10}
sort -nr $filename | head -$howmany   # the second version
# 当后面没有接文件的时候, 输出的错误信息有点不尽人意

filename=$1
filename=${filename:?"filename missing."}
howmany=${2:-10}
sort -nr $filename | head -$howmany    # the third version

filename=$1
filename=${filename:?"filename missing."}
howmany=${2:=1}
sort -nr $filename | head -$howmany    # the fourth version 
# 会把 1 赋值给 $2, shell 会提示出错, "cannot assign in this way"

filename=$1
filename=${filename:?"filename missing"}
howmany=$2
sort -nr $filename | head -${howmany:=10}   # the fifth version

filename=$1
filename=${filename:?"filename missing"}
howmany=$3
header=$2
echo -e -n ${header:+"ablums artist\n"}
# 如果变量为空什么都不输出, 连空行也不输出, 如果不为空就输出
# echo $head   # 的值不是后面的字符串...注意区分,他只是返回
sort -nr $filename | head -${howmany:=3}

getopts


while getopts ":ab:c" opt; do
    case $opt in
        a) echo ‘this is a‘ ;;
        b) echo ‘this is b‘
            echo "args b is $OPTARG" ;;
        c) echo ‘this is c‘ ;;
        \?) echo ‘usage: alice [-a][-b bargs] [-c] args...‘
            exit 1;;
    esac
done
echo $OPTIND
shift $(($OPTIND - 1))

# 输入: ./my.sh -a -a -b
# 输出:
# this is a
# this is b
# 4

# 输入: ./my.sh -a -b 5 -c
# 输出:
# this is a
# this is b
# args b is 5
# this is c
# 5

# 输入: ./my.sh  -acb 4
# this is a
# this is c
# this is b
# args b is 4
# 3

数组


name[0]=abc;
name[1]=def;
name[2]=ghi;
##   ==
name=(abc def ghi)
##   ==
name=([0]=abc [1]=def [2]=ghi)

for i in "${name[@]}"; do
    echo $i
done

文件描述符

<> FILE: 文件作为输入和输出
2> FILE: 将错误写入文件  或者 2>>FILE
&> FILE: 定向标准输出和标准错误到 FILE  或者 &>>FILE
2>&1: 把 2 输出到 1
2<&0: 把 2 出入到 0

模式匹配

${VALUE#pattern}匹配以 pattern 开头的部分,只匹配一次
${VALUE##pattern}匹配以 pattern 开头的部分,匹配至多次
${VALUE%pattern}匹配以 pattern 结尾的部分,只匹配一次
${VALUE%%pattern}匹配以 pattern 结尾的部分,匹配至多次
${VALUE/OLDSTRING/NEWSTRING}替换字符串,只替换一个匹配到
${VALUE//OLDSTRING/NEWSTRING}替换字符串,替换全部匹配到
# 从前面开始往后面删除匹配到的, 留下最后没被匹配的
% 从后面往前面开始删除, 留下前面的


temp=/home/sunx/Work/bin
echo ${temp#*/}      # home/sunx/Work/bin
echo ${temp%%/*}     # 

echo ${temp##*/}     # bin
echo ${temp%/*}      # /home/sunx/Work/bin


temp=test.c
file1=${temp#*\.}
echo ${file1}        # c

file2=${temp%*\.*}   # test

temp=test.c.cpp
file=${temp#*\.}     # c.cpp
file=${temp##*\.}    # cpp
file=${temp%\.*}     # test.c
file=${temp%%\.*}    #

脚本控制流

test

文件属性

-e该名字是否存在
-f该名字是否存在且为档案 file
-d改名字是否存在且为目录 dirctory
-b该名字是否存在且为 block device 装置
-c该名字是否存在且为 charcter device
-S该名字是否存在且为 socket 档案
-p该名字是否存在且为 FIFO(pipe) 档案
-L该当名是否存在且为一个 link 档案

文件权限

-r是否可读
-w是否可写
-x是否可执行

2 个文件比较

-nt(newer than)file1 是否比 file2 新
-ot(older than)file1 是否比 file2 旧
-ef是否相等, 判断 hard link 经常使用

2 个数值比较

-eq是否相等
-nenot equal
-gt(greater than)大于
-lt(less than) 小于
-ge(greater than or equal) 大于等于
-le(less than or equal) 小于等于

字符串

test -z string判定字符串是否为 0? 若 string 为空字符串, 则是 true
test -n string判定字符串是否非 0?
test str1 = str2判定 str1 是否等于 str2
test str1 != str2判定是否不等,一定要加空格
判断相等的时候 等于号左右留空格, 否则被当成赋值.

test 中使用逻辑判断

-atest -r file -a -x file
-otest -r file -o -x file
test ! -x file, 当 file 不具有 x 权限时, 才是 true

[] 判断

[] 等价于 test


[ "$HOME" == "$MAIL" ]
# 在 bash 里面用 = 和用 == 是一样的
# [ ] 前后都要加空格

read -p "Please input (Y/N): " yn
[ "$yn" == "Y" -o "$yn" == "y" ] && echo "OK, continue" && exit 0
[ "$yn" == "N" -o "$yn" == "n" ] && echo "Oh, interrupt!" && exit 0
echo "I don‘t know what your choice is" && exit 0

[ "$yn" == "Y" -o "$yn" == "y" ]
# 等价于
[ "$yn" == "Y" ] || [ "$yn" == "y"]

if 语句


if [ 条件判断式 ]; then
    ...
fi

read -p "please input(Y/N):" yn
if [ "$yn" == "Y" ]||[ "$yn" == "y" ]; then
    echo "ok, yes continue"
    exit 0
fi

if [ 条件判断式 ]; then
    ...
else
    ...
fi

if [ 条件判断式 ]; then
    ...
elif [ 条件判断式二 ]; then
    ...
else
    ...
fi

for


for animal in dog cat elephant; do
    echo "there are ${animal}s..."
done
# dogs
# cats
# elephants

users=$(cut -d ‘:‘ -f1 /etc/passwd)
# users=`cut -d ‘:‘ -f1 /etc/passwd`
for usename in $users; do
    id $username
    finger $username
done
# $((...))      对数学表达式求值
   # echo $((2*10))    20
   # echo $((2**3))    8

# echo $((s))   # 结果是 0, 说明这样的操作 shell 会自动给他 0,接下来看下面
for((i=0; i<=10; ++i)) do
    s=$(($s+$i))   # s = s + i
done

while


while [ "$yn" != "yes" -a "$yn" != "YES" ]; do
    read -p "please input yes/YES to stop this program: " yn
done
echo "ok! you input the correct answer."

until


until [ "$yn" == "yes" -o "$yn" == "YES" ]; do
    read -p "please input yes/YES to stop this program: " yn
done
echo "ok! you input the correct answer."
# 直到条件满足才退出循环.

case


case $1 in
    "hello")
        echo "hello, how are you?"
        ;;
    "")
        echo "you must input parameters, example: {$0 someword}"
        ;;
    *)   # 相当于 C 语言的 default
        echo "usae $0 {hello}"
        ;;
# 也可以在 case 里面用 | 來表示或, 例如
case $1 in
        123 | 456)
            ...
esac  # end

select


select name in LIST; do
    ...
done

temp=$(ls *.c)  # a.c b.c
select file in $temp; do
    if [ $file == "a.c" ]; then
        echo "a.c"; fi
    if [ $file == "b.c" ]; then
        echo "b.c"; fi
done
1) a.c
2) b.c
#? 1
a.c
#? 2
b.c
#? 3
bash: [: ==: unary operator expected
bash: [: ==: unary operator expected
#? 1
a.c
#?

脚本函数

如果从别的文件中调用函数, 例文件名字是 a.sh

. /PATH_TO/a.sh
或者是
source ./PATH_TO/a.sh



function FUNCTNAME() {
}
FUNCTNAMENAME() {
}
## 这 2 种形式都是可以声明和定义一个函数的
unset -f FUNCTNAME
function afunc {
    echo in function : $0 $1 $2
    var1="in function"
    echo var1: $var1
}
var1="outside function"
echo var1: $var1
echo $0: $1 $2
afunc funcarg1 funcarg2
echo var1: $var1
echo $0: $1 $2

# var1: outside function
# ./Function.sh: arg1 arg2
# in function : ./Function.sh funcarg1 funcarg2  # 从这里开始调用函数
# var1: in function
# var1: in function    # var1 还是 in function
# ./Function.sh: arg1 arg2
function afunc {
    # local var1
    echo in function : $0 $1 $2
    # var1="in function"     # 如果这句话被注释掉, 下面以行的 var1 的值就变成了空, 但是如果同时把 local 的声明也注释掉就是 outside...
    echo var1: $var1
}                            # 由上面的注释可以知道, 再脚本里面定义的变量可以在函数里面使用, 但是如果函数定义了该变量就不行了
var1="outside function"
echo var1: $var1
echo $0: $1 $2
afunc funcarg1 funcarg2
echo var1: $var1
echo $0: $1 $2

# var1: outside function
# ./Function.sh: arg1 arg2
# in function : ./Function.sh funcarg1 funcarg2
# var1: in function
# var1: outside function  # var1 的值还是函数的, 函数外的 var1 的值没有影响
# ./Function.sh: arg1 arg2
function printit() {
    echo -n "Your choice is $1"
}
echo "This program will print your selection !"
case $1 in
    "one")
        printit; echo $1 | tr ‘a-z‘ ‘A-Z‘ # 将参数做大小写转换!
        ;;
    "two")
        printit; echo $1 | tr ‘a-z‘ ‘A-Z‘
        ;;
    "three")
        printit; echo $1 | tr ‘a-z‘ ‘A-Z‘
        ;;
    *)
        echo "Usage $0 {one|two|three}"
        ;;
esac
##############上面是程序的的参数, ..区别于函数的的参数. .........###############

## 程序的参数是在 shell 命令中跟在脚本名字后面的, 函数的参数是在脚本里面调用函数时候, 跟在脚本后面的

################下面是函数的参数, ..区别于程序的参数. .........#################
function printit() {
    echo -n "Your choice is $1  "

echo "This program will print your selection !"
case $1 in
    "one")
        printit 1; echo $1 | tr ‘a-z‘ ‘A-Z‘ # 将参数做大小写转换!
        ;;
    "two")
        printit 2; echo $1 | tr ‘a-z‘ ‘A-Z‘
        ;;
    "three")
        printit 3; echo $1 | tr ‘a-z‘ ‘A-Z‘
        ;;
    *)
        echo "Usage $0 {one|two|three}"
        ;;
esac
}
printit one

# result
# Your choice is one  This program will print your selection !
# Your choice is 1  This program will print your selection !  # 注意 printit 1 也会继续调用函数
# Usage a.sh {one|two|three}
# ONE

脚本调试

sh [-nvx] FILE
-n不执行,仅检查语法问题
-v执行前,先输出脚本
-x将使用到的脚本输出


cat a.sh
echo "hello" || echo "123"

sh -n a.sh   # 检查语法问题, 没输出,说明没问题,似乎没用 :D

sh -v a.sh
echo "hello" || echo "123"
hello

sh -x a.sh
+ echo hello
hello

Linux 笔记--脚本