Linux bash 进阶

作者:じ☆ve不哭

发布时间:2023-08-21T09:43:47

Bash 进阶 P101

变量重游

内部变量

变量名($名称) 意思
BASH Bash 的二进制程序文件的路径
BASH_ENV Bash的启动文件
BASH_SUBSHELL 提示子shell的层次
BASH_VERSINFO[n] 6个元素的数组, 它包含了所安装的Bash的版本信息
BASH_VERSION Bash版本号
DIRSTACK 在目录栈中最顶端的值
EDITOR 脚本所调用的默认编辑器, 通常情况下是vi或者是emacs.
EUID "有效"用户ID
FUNCNAME 当前函数的名字
GLOBIGNORE 一个文件名的模式匹配列表
GROUPS 目前用户所属的组
HOME 用户的home目录, 一般是 /home/username
HOSTNAME 在一个初始化脚本中, 在系统启动的时候分配一个系统名字
$HOSTTYPE 主机类型
IFS 内部域分隔符 决定Bash在解释字符串时如何识别域, 或者单词边界.
IGNOREEOF 忽略EOF: 告诉shell在log out之前要忽略多少文件结束符(control-D)
LC_COLLATE 常在 .bashrc 或 /etc/profile 中设置, 这个变量用来控制文件名扩展和模式匹配的展开顺序
LC_CTYPE 来控制通配(globbing)和模式匹配中的字符串解释
LINENO 自身在脚本中所在的行号
MACHTYPE 机器类型
OLDPWD 之前的工作目录("OLD-print-working-directory", 就是之前你所在的目录)
OSTYPE 操作系统类型
PATH 可执行文件的搜索路径, 一般为 /usr/bin/ , /usr/X11R6/bin/ , /usr/local/bin , 等等.
PIPESTATUS 最后一个运行的 前台 管道的退出状态码.
PPID 进程的 $PPID 就是这个进程的父进程的进程ID( pid )
PROMPT_COMMAND 主提示符 $PS1 显示之前需要执行的命令
PS1 ​PS2 PS3 PS4 第几条提示符
PWD 工作目录(你当前所在的目录) 与内建命令pwd作用相同
REPLY 当没有参数变量提供给read命令的时候, 这个变量会作为默认变量提供给read命令.
SECONDS 脚本已经运行的时间
SHELLOPTS hell中已经激活的选项的列表, 这是一个只读变量
SHLVL Shell级别, 就是Bash被嵌套的深度. 如果是在命令行中, 那么$SHLVL为1, 如果在脚本中那么​$SHLVL为2
TMOUT 如果 $TMOUT 环境变量被设置为非零值 time 的话, 那么经过 time 秒后, shell提示符将会超时. 这将
会导致登出(logout).
UID 用户ID号 当前用户的用户标识号, 记录在 /etc/passwd 文件中
$0 , ​$1 , ​$2 位置参数, 从命令行传递到脚本, 或者传递给函数 或者set给变量
$#
$*
$@
命令行参数 或者位置参数的个数
所有的位置参数都被看作为一个单词
与$*相同, 但是每个参数都是一个独立的引用字符串
$-
$!
$_
$?
$$
传递给脚本的标记
运行在后台的最后一个作业的PID(进程ID)
之前执行的命令的最后一个参数的值.
命令, 函数, 或者是脚本本身的退出状态码
脚本自身的进程ID.
#!/bin/bash
# $IFS 处理空白与处理其他字符不同.

output_args_one_per_line()
{
for arg
do echo "[$arg]"
done
}

echo; echo "IFS=\" \""
echo "-------"

IFS=" "
var=" a b c "
output_args_one_per_line $var #
output_args_one_per_line `echo " a b c "`
#
# [a]
# [b]
# [c]


echo; echo "IFS=:"
echo "-----"

IFS=:
var=":a::b:c:::" # 与上边一样, 但是用" "替换
了":".
output_args_one_per_line $var
#
# []
# [a]
# []
# [b]
# [c]
# []
# []
# []

# 同样的事情也会发生在awk的"FS"域中.
echo

exit 0
#!/bin/bash
# reply.sh
# REPLY是提供给'read'命令的默认变量.

echo
echo -n "What is your favorite vegetable? "
read

echo "Your favorite vegetable is $REPLY."
 # 当且仅当没有变量提供给"read"命令时,
#!/bin/bash
# timed-input.sh

# TMOUT=3 在新一些的Bash版本上也能运行的很好.


TIMELIMIT=3 # 这个例子中设置的是3秒. 也可以设置为其他的时间值.

PrintAnswer()
{
if [ "$answer" = TIMEOUT ]
then
echo $answer
else # 别和上边的例子弄混了.
echo "Your favorite veggie is $answer"
kill $! # 不再需要后台运行的TimerOn函数了, kill了吧.
# $! 变量是上一个在后台运行的作业的PID.
fi

}



TimerOn()
{
sleep $TIMELIMIT && kill -s 14 $$ &
# 等待3秒, 然后给脚本发送一个信号.
}

Int14Vector()
{
answer="TIMEOUT"
PrintAnswer
exit 14
}

trap Int14Vector 14 # 定时中断(14)会暗中给定时间限制.

echo "What is your favorite vegetable "
TimerOn
read answer
PrintAnswer


# 无可否认, 这是一个定时输入的复杂实现,
#+ 然而"read"命令的"-t"选项可以简化这个任务.
# 参考后边的"t-out.sh".

# 如果你需要一个真正优雅的写法...
#+ 建议你使用C或C++来重写这个应用,
#+ 你可以使用合适的函数库, 比如'alarm'和'setitimer'来完成这个任务.

exit 0
#!/bin/bash
# t-out.sh
# 从"syngin seven"的建议中得到的灵感 (感谢).


TIMELIMIT=4 # 4秒

read -t $TIMELIMIT variable <&1
# ^^^
# 在这个例子中, 对于Bash 1.x和2.x就需要"<&1"了,
# 但是Bash 3.x就不需要.

echo

if [ -z "$variable" ] # 值为null?
then
echo "Timed out, variable still unset."
else
echo "variable = $variable"
fi

exit 0
#!/bin/bash
# arglist.sh
# 多使用几个参数来调用这个脚本, 比如"one two three".

E_BADARGS=65

if [ ! -n "$1" ]
then
echo "Usage: `basename $0` argument1 argument2 etc."
exit $E_BADARGS
fi

echo

index=1 # 起始计数.

echo "Listing args with \"\$*\":"
for arg in "$*" # 如果"$*"不被""引用,那么将不能正常地工作.
do
echo "Arg #$index = $arg"
let "index+=1"
done # $* 将所有的参数看成一个单词.
echo "Entire arg list seen as single word."

echo

index=1 # 重置计数(译者注: 从1开始).
# 如果你写这句会发生什么?

echo "Listing args with \"\$@\":"
for arg in "$@"
do
echo "Arg #$index = $arg"
let "index+=1"
done # $@ 把每个参数都看成是单独的单词.
echo "Arg list seen as separate words."

echo

index=1 # 重置计数(译者注: 从1开始).

echo "Listing args with \$* (unquoted):"
for arg in $*
do
echo "Arg #$index = $arg"
let "index+=1"
done # 未引用的$*将会把参数看成单独的单词.
echo "Arg list seen as separate words."

exit 0
#!/bin/bash
# 使用 ./scriptname 1 2 3 4 5 来调用这个脚本

echo "$@" # 1 2 3 4 5
shift
echo "$@" # 2 3 4 5
shift
echo "$@" # 3 4 5

# 每次"shift"都会丢弃$1.
# "$@" 将包含剩下的参数.

操作字符串

expr 介绍

将表达式的值列印到标准输出,分隔符下面的空行可提升算式优先级。 可用的表达式有:

ARG1 | ARG2 若ARG1 的值不为0 或者为空,则返回ARG1,否则返回ARG2

ARG1 & ARG2 若两边的值都不为0 或为空,则返回ARG1,否则返回 0

ARG1 < ARG2 ARG1 小于ARG2 ARG1 <= ARG2 ARG1 小于或等于ARG2 ARG1 = ARG2 ARG1 等于ARG2 ARG1 != ARG2 ARG1 不等于ARG2 ARG1 >= ARG2 ARG1 大于或等于ARG2 ARG1 > ARG2 ARG1 大于ARG2

ARG1 + ARG2 计算 ARG1 与ARG2 相加之和 ARG1 - ARG2 计算 ARG1 与ARG2 相减之差

ARG1 * ARG2 计算 ARG1 与ARG2 相乘之积 ARG1 / ARG2 计算 ARG1 与ARG2 相除之商 ARG1 % ARG2 计算 ARG1 与ARG2 相除之余数

字符串 : 表达式 定位字符串中匹配表达式的模式

match 字符串 表达式 等于"字符串 :表达式" substr 字符串 偏移量 长度 替换字符串的子串,偏移的数值从 1 起计 index 字符串 字符 在字符串中发现字符的地方建立下标,或者标0 length 字符串 字符串的长度

  • 记号 将记号解析为字符串,即使它是一个类似"match"或 运算符"/"那样的关键字

( 表达式 ) 表达式的值

请注意有许多运算操作符都可能需要由 shell 先实施转义。 如果参与运算的 ARG 自变量都是数字,比较符就会被视作数学符号,否则就是多义的。 模式匹配会返回""和""之间被匹配的子字符串或空(null);如果未使用""和"", 则会返回匹配字符数量或是 0。

若表达式的值既不是空也不是 0,退出状态值为 0;若表达式的值为空或为 0, 退出状态值为 1。如果表达式的句法无效,则会在出错时返回退出状态值 3。

字符串长度

  • ${#string}
  • expr length $string
  • expr "$string" : '.*
stringZ=abcABC123ABCabc

echo ${#stringZ} # 15
echo `expr length $stringZ` # 15
echo `expr "$stringZ" : '.*'` # 15

匹配字符串开头的子串长度

  • expr match "$string" '$substring'
  • expr "$string" :'$substring' '$substring' 为正则表达式
stringZ=abcABC123ABCabc
 # |------|

echo `expr match "$stringZ" 'abc[A-Z]*.2'` # 8
echo `expr "$stringZ" : 'abc[A-Z]*.2'` # 8

索引

expr index $string $substring

stringZ=abcABC123ABCabc
echo `expr index "$stringZ" C12` # 6
# C 字符的位置.

echo `expr index "$stringZ" 1c` # 3
# 'c' (in #3 position) matches before '1'.

提取子串

  • ${string:position 从位置$position 开始提取子串}
  • ${string:position:length 从位置 $position 开始提取 $length 长度的子串.}
  • expr substr $string $position $length 在 $string 中从 ​$position 开始提取 ​$length 长度的子串.
  • expr match "$string" '\(​$substring\)'
  • expr "$string" : '\($substring\)'
stringZ=abcABC123ABCabc
# 0123456789.....
# 0-based indexing.

echo ${stringZ:0} # abcABC123ABCabc
echo ${stringZ:1} # bcABC123ABCabc
echo ${stringZ:7} # 23ABCabc

echo ${stringZ:7:3} # 23A
# 提取子串长度为3.

# 能不能从字符串的右边(也就是结尾)部分开始提取子串?

echo ${stringZ:-4} # abcABC123ABCabc
# 默认是提取整个字符串, 就象${parameter:-default}一样.
# 然而 . . .

echo ${stringZ:(-4)} # Cabc
echo ${stringZ: -4} # Cabc
# 这样, 它就可以工作了.
# 使用圆括号或者添加一个空格可以"转义"这个位置参数.
stringZ=abcABC123ABCabc
2 # 123456789......
3 # 以1开始计算.
4
5 echo `expr substr $stringZ 1 2` # ab
6 echo `expr substr $stringZ 4 3` # ABC

子串削除

  • ${string#substring 从 $string 的 开头 位置截掉 最短 匹配的 ​$substring .}
  • ${string##substring 从 $string 的 开头 位置截掉 最长 匹配的 $substring .}
  • ${string%substring 从 $string 的 结尾 位置截掉 最短 匹配的 $substring .}
  • ${string%%substring 从 $string 的 结尾 位置截掉 最长 匹配的 $substring .}
stringZ=abcABC123ABCabc
#       |----|
#       |----------|
echo ${stringZ#a*C} # 123ABCabc
# 截掉'a'到'C'之间最短的匹配字符串.

echo ${stringZ##a*C} # abc
# 截掉'a'到'C'之间最长的匹配字符串.

stringZ=abcABC123ABCabc
#                    ||
#        |------------|

echo ${stringZ%b*c} # abcABC123ABCa
# 从$stringZ的结尾位置截掉'b'到'c'之间最短的匹配.

echo ${stringZ%%b*c} # a
# 从$stringZ的结尾位置截掉'b'到'c'之间最长的匹配.
#!/bin/bash
# getopt-simple.sh


getopt_simple()
{
echo "getopt_simple()"
echo "Parameters are '$*'"
until [ -z "$1" ]
do
echo "Processing parameter of: '$1'"
if [ ${1:0:1} = '/' ]
then
tmp=${1:1} # 去掉开头的'/' . . .
parameter=${tmp%%=*} # 提取参数名.
value=${tmp##*=} # 提取参数值.
echo "Parameter: '$parameter', value: '$value'"
eval $parameter=$value
fi
shift
done
}

# 把所有选项传给函数getopt_simple().
getopt_simple $*

echo "test is '$test'"
echo "test2 is '$test2'"

exit 0

---

sh getopt_example.sh /test=value1 /test2=value2

Parameters are '/test=value1 /test2=value2'
Processing parameter of: '/test=value1'
Parameter: 'test', value: 'value1'
Processing parameter of: '/test2=value2'
Parameter: 'test2', value: 'value2'
test is 'value1'
test2 is 'value2'

子串替换

  • ${string/substring/replacement 替换第一个匹配}
  • ${string//substring/replacement 替换第所有匹配}
  • ${string/#substring/replacement 替换开头匹配}
  • ${string/%substring/replacement 替换结尾匹配}
stringZ=abcABC123ABCabc
echo ${stringZ/abc/xyz}  # xyzABC123ABCabc
echo ${stringZ//abc/xyz} # xyzABC123ABCxyz
echo ${stringZ/#abc/XYZ} # XYZABC123ABCabc
echo ${stringZ/%abc/XYZ} # abcABC123ABCXYZ

使用awk来处理字符串

#!/bin/bash
# substring-extraction.sh

String=23skidoo1
#      012345678 Bash
#      123456789 awk
# 注意不同的字符串索引系统:
# Bash的第一个字符是从'0'开始记录的.
# Awk的第一个字符是从'1'开始记录的.

echo ${String:2:4} # 位置 3 (0-1-2), 4 个字符长
# skid

# awk中等价于${string:pos:length}的命令是substr(string,pos,length).
echo | awk '
{ print substr("'"${String}"'",3,4) # skid
}
'
# 使用一个空的"echo"通过管道传递给awk一个假的输入,
#+ 这样就不必提供一个文件名给awk.

exit 0

参数替换

处理和(或)扩展变量

  • ${parameter 与 $parameter 相同, 也就是变量 parameter 的值}
  • ${parameter-default 如果变量parameter没被声明, 那么就使用默认值}
  • ${parameter:-default 如果变量parameter没被设置, 那么就使用默认值.}
  • ${parameter=default , ${parameter:=default} 如果变量parameter没被设置, 那么就使用默认值.}}
  • ${parameter+alt_value , ${parameter:+alt_value} parameter没声明或者设置, 么就使用null ,否则使用alt_value.}}
  • ${parameter?err_msg 未声明 打印 err_msg}
  • ${parameter:?err_msg未设置 打印 err_msg}
#!/bin/bash
# param-sub.sh

# 一个变量是否被声明或设置,
#+ 将会影响这个变量是否使用默认值,
#+ 即使这个变量值为空(null).

username0=
echo "username0 has been declared, but is set to null."
echo "username0 = ${username0-`whoami`}"
# 不会有输出.

echo

echo username1 has not been declared.
echo "username1 = ${username1-`whoami`}"
# 将会输出默