shell bash 技巧说明 shell参数 特殊书写格式

pimshell 2020-04-20

shell bash 技巧说明 shell参数 特殊书写格式

执行shell脚本三种方式的区别( sh,bash,xxx.sh / source,. / exec )
执行shell脚本三种方式的区别( sh,bash,xxx.sh / source,. / exec )

1.bash/sh /shell script执行时,另起一个子shell,继承父shell的环境变量,子shelll的变量执行完后不影响父shell。 sh 或 script.sh执行脚本时,在子shell中执行脚本。脚本执行完毕,退出子shell,回到父shell。./script.sh与sh script.sh等效。

2.source script.sh (source也叫点命令)方式,在当前上下文中执行脚本(也就是当前进程中执行脚本),不会生成新的进程。脚本执行完毕,依然是当前shell,设定的局部变量仍然有效。

3.exec(shell内建命令之一),exec command方式不启用新的shell进程,会用command进程替换当前shell进程(保持PID不变)。执行完毕,直接退出(当然就不会回到之前的shell环境)。比如直接在Terminal中输入 exec echo hello回车,会立即退出终端.

感叹号的使用(转义)

!/bin/bash

test=‘#$%-test‘ # 注意,等号两边不能有空格,否则会将叹号当做历史命令调用来处理
echo $test
echo "you !!!"
test2="test2!"
echo $test2

注意: shell编程里,等号两边不应该有空格(跟自己以前习惯不一样).

shell特殊 参数 变量 $#,,$,$$,$?,$0,$1的含义解释
整理时间: 20180718 Chenxin
shell的特殊字符
$# 是传给脚本的参数个数(不含脚本文件自身)
$0 是脚本本身的名字
$1~$n 是传递给该shell脚本的第1-n个参数
所有参数列表.以"$1" "$2" … "$n" 的形式输出所有参数。
$
所有参数列表.以"$1 $2 … $n"的形式输出所有参数。是以一个单字符串显示所有向脚本传递的参数.
$$ 是脚本运行的当前进程ID号
$? 是显示最后命令的退出状态,0表示没有错误,其他表示有错误
${!#} 输出最后一个参数
$! Shell最后运行的后台Process的PID

完整的特殊字符含义:
https://linux.cn/article-5657-1.html

参数的操作
参数总个数不确定,从脚本参数的第3个位置开始取参数方法 参数位置 获取技巧 ${@???b}
参考: https://www.jianshu.com/p/eaa3406b7cff

格式: ${@???b} 从第a个参数开始取,合计取b个. 如取到最后一个,则":b"可以不写.有些类似python的切片方式.

./t.sh 1001 1002 1003 1004 1005 目的要赋值给变量a=1005 , b="1001 1002 1003 1004",则脚本内容为
a=${!#} #取最后一个参数
b=${@:1:$#-1} #从第1个参数开始,合计取$#-1个.其中是列表形式列出所有的传入参数,然后:1是从第一个参数开始,后面不加任何东西的话是一直到结尾,若加:$#-1是"$#参数总个数-1",即从第1个参数开始,合计取$#-1个参数.

若要取倒数第二个参数即c=1004: c=${@??#-1:1} #@:开始位置(倒数第2个):合计取1个
从第3个参数位置获取: directory=${@:3:$#} #从第3个参数开始,取所有(取$#个,显然取不到这么多个,但取到最后的时候,shell自动会判断结束)

其他示例说明:
从第2个开始,合计取参数个数减2个: a=${@:2:$#-2}
取最后一个: b=${!#}
取倒数第3个位置开始,取参数总个数这么多(能够取到的参数肯定比参数总个数少,因为是从倒数第三个开始取的.除非从第一个开始取): c=${@??#-3:$#}
取倒数第3个位置开始,全部取完.同上: d=${@??#-3}

变量字符串 / 命令串 / 变量数学计算 的操作
括号总结 shell内置的 变量与字符串处理 总结(括号)
20121030 Chenxin
() 代码块执行(一串命令执行);命令替换;数组定义A=(a b c def)(见备注2);
{} 代码块执行(一串命令执行);变量替换;更为详细的见备注1;
[] test测试命令,test和[是bash的内部命令;数组元素;绝大多数情况下,[ [[ test 这3个是通用的。详细见备注4;
$() 命令替换,类似``和$(command): $(date +%Y%m%d%H%M).
$(($number)) 做整数运算的,number前可以加$,也可以不加,都会被作为数值运算.
[$var] 测试,同test. $[var]没这种用法.
$[6/3] 数值运算
$((6/3)) 数值运算

$var 变量
${var} 变量原型
${#var} 变量个数,同$#
${var[@]} 变量中包含的所有值(变量本身就是个数值)
${var[*]} 同${}
${var[0]} 变量数组的第一个元素,同$var,以及${var}.${var[1]} 变量数组的第二个元素.var[0] var[1]

备注一 ${} 的功能和用法
使用这个功能的原因(性能)
性能比较.在shell中,通过awk,sed,expr 等都可以实现字符串操作。但速度相差上百倍,调用外部命令处理,与内置操作符性能相差非常大。在shell编程中,尽量用内置操作符或者函数完成。

以下操作均来源于变量: 定义 file=/dir1/dir2/dir3/my.file.txt 这个file变量.(因来源于不同人总结,以下string也就是file).

字符长度
${#string} $string的长度.${#变量名}得到字符串长度
举例
[ ~]$ test=‘I love china‘
[ ~]$ echo ${#test}
12

字符截取/删除(根据特征截取)
${变量名#substring正则表达式}从字符串开头开始配备substring,删除匹配上的表达式。分为:
${file#/}:拿掉第一条 / 及其左边的字串:dir1/dir2/dir3/my.file.txt
${file##
/}:拿掉最后一条 / 及其左边的字串: my.file.txt
${file#.}:拿掉第一個 . 及其左边的字串:file.txt
${file##
.}:拿掉最后一個 . 及其左边的字串:txt

${变量名%substring正则表达式}从字符串结尾开始配备substring,删除匹配上的表达式。
${file%/}:拿掉最后条 / 及其右边的字串:/dir1/dir2/dir3
${file%%/
}:拿掉第一条 / 及其右边的字串:(空值)
${file%.}:拿掉最后一個 . 及其右边的字串:/dir1/dir2/dir3/my.file
${file%%.
}:拿掉第一個 . 及其右边的字串:/dir1/dir2/dir3/my

记忆的方法为:

是去掉左边(在键盘上 # 在 $ 之左边)

% 是去掉右边(在键盘上 % 在 $ 之右边)
单个符号是最小匹配﹔两个符号是最大匹配。

举例:
字符串删除
[ ~]$ test=‘c:/windows/boot.ini‘
[ ~]$ echo ${test#/}
c:/windows/boot.ini
[ ~]$ echo ${test#/}
windows/boot.ini
[ ~]$ echo ${test##
/}
boot.ini
[ ~]$ echo ${test%/}
c:/windows
[ ~]$ echo ${test%%/
}

获取文件名 和 目录路径 的最简单方法
${test##/},${test%/} 分别是得到文件名,或者目录地址最简单方法。

截取(根据位置截取)
截取,类似于python的切片
${file:0:5}:提取最左边的 5 個字節:/dir1
${file:5:6}:提取第 5 個字節右边的連續 6 個字節:/dir2/ .${变量名:起始:长度}得到子字符串

举例:
[ ~]$ test=‘I love china‘
[ ~]$ echo ${test:5}
e china
[ ~]$ echo ${test:5:10}
e china

替换(根据匹配内容替换)
${变量/查找/替换值} 一个“/”表示替换第一个,”//”表示替换所有,当查找中出现了”/”请加转义符”/”表示。
${file/dir/path}:将第一個 dir 替换为path:/path1/dir2/dir3/my.file.txt .${string/substring/replacement} 使用$replacement, 来代替第一个匹配的$substring
${file//dir/path}:将全部 dir 替换为 path:/path1/path2/path3/my.file.txt. ${string//substring/replacement} 使用$replacement, 代替所有匹配的$substring
${string/#substring/replacement} 如果$string的前缀匹配$substring, 那么就用$replacement来代替匹配到的$substring
${string/%substring/replacement} 如果$string的后缀匹配$substring, 那么就用$replacement来代替匹配到的$substring
说明:"* $substring”可以是一个正则表达式.

举例:
字符串替换
[ ~]$ test=‘c:/windows/boot.ini‘
[ ~]$ echo ${test///\}
c:\windows/boot.ini
[ ~]$ echo ${test////\}
c:\windows\boot.ini

根据变量状态进行赋值

利用 ${ } 还可针对不同的变量状态赋值(沒设定、空值、非空值):
${file-my.file.txt} :假如 $file 沒有设定,则使用 my.file.txt 作传回值。(空值及非空值時不作处理)
${file:-my.file.txt} :假如 $file 沒有设定或为空值,则使用 my.file.txt 作传回值。 (非空值時不作处理)
${file+my.file.txt} :假如 $file 设为空值或非空值,均使用 my.file.txt 作传回值。(沒设定時不作处理)
${file:+my.file.txt} :若 $file 为非空值,则使用 my.file.txt 作传回值。 (沒设定及空值時不作处理)
${file=my.file.txt} :若 $file 沒设定,则使用 my.file.txt 作传回值,同時将 $file 賦值为 my.file.txt 。 (空值及非空值時不作处理)
${file:=my.file.txt} :若 $file 沒设定或为空值,则使用 my.file.txt 作传回值,同時将 $file 賦值为 my.file.txt 。 (非空值時不作处理)
${file?my.file.txt} :若 $file 沒设定,则将 my.file.txt 輸出至 STDERR。 (空值及非空值時不作处理)
${file:?my.file.txt} :若 $file 沒设定或为空值,则将 my.file.txt 輸出至 STDERR。 (非空值時不作处理)
以下内容相同,方便了解.
${var} 变量var的值, 与$var相同
${var-DEFAULT} 如果var没有被声明, 那么就以$DEFAULT作为其值 *
${var:-DEFAULT} 如果var没有被声明, 或者其值为空, 那么就以$DEFAULT作为其值 *
${var=DEFAULT} 如果var没有被声明, 那么就以$DEFAULT作为其值 *
${var:=DEFAULT} 如果var没有被声明, 或者其值为空, 那么就以$DEFAULT作为其值 *
${var+OTHER} 如果var声明了, 那么其值就是$OTHER, 否则就为null字符串
${var:+OTHER} 如果var被设置了, 那么其值就是$OTHER, 否则就为null字符串
${var?ERR_MSG} 如果var没被声明, 那么就打印$ERR_MSG *
${var:?ERR_MSG} 如果var没被设置, 那么就打印$ERR_MSG *
${!varprefix} 匹配之前所有以varprefix开头进行声明的变量
${} 匹配之前所有以varprefix开头进行声明的变量
加入了“
” 不是意思是: 当然, 如果变量var已经被设置的话, 那么其值就是$var.
举例
[ ~]$ echo ${abc-‘ok‘}
ok
[ ~]$ echo $abc
[ ~]$ echo ${abc=‘ok‘}
ok
[ ~]$ echo $abc
ok
如果abc 没有声明“=" 还会给abc赋值。
[ ~]$ var1=11;var2=12;var3=
[ ~]$ echo ${}
var1 var2 var3
[ ~]$ echo ${!v*}
var1 var2 var3

${!varprefix*}与${}相似,可以通过变量名前缀字符,搜索已经定义的变量,无论是否为空值。

备注二 bash数组
接下來,再为大家介稍一下 bash 的組数(array)处理方法。
一般而言,A="a b c def" 这样的变量只是将 $A 替换为一个单一的字串,
但是改为 A=(a b c def) ,则是将 $A 定义为組数...

备注三 单纯使用(())
事实上,单纯用 (( )) 也可重定义变量值,或作 testing:
a=5; ((a++)) 可将 $a 重定義为 6
a=5; ((a--)) 则为 a=4
a=5; b=7; ((a < b)) 會得到 0 (true) 的返回值。
常見的用於 (( )) 的測試符號有如下這些:
<:小於

:大於
<=:小於或等於
=:大於或等於
==:等於
!=:不等於
不過,使用 (( )) 作整数測試時,請不要跟 [ ] 的整数測試搞混亂了。

备注四 中括号用法 [][[]] 用法
shell 中括号的用法
test , [] , [[]] 三个用法类似.
以 bash 为例 (其他兼容 shell 差不多):

  1. test 和 [ 是 bash 的内部命令,GNU/linux 系统的 coreutils 软件包通常带 /usr/bin/test 和 /usr/bin/[ 命令。如果我们不用绝对路径指 明,通常我们用的都是bash 自带的命令。

  2. [[ 是 bash 程序语言的关键字!
    $ ls -l /usr/bin/[ /usr/bin/test
    -rwxr-xr-x 1 root root 37400 9月 18 15:25 /usr/bin/[
    -rwxr-xr-x 1 root root 33920 9月 18 15:25 /usr/bin/test
    $ type [ [[ test
    [ is a shell builtin
    [[ is a shell keyword
    test is a shell builtin
    绝大多数情况下,这个三个功能通用 。但是命令和关键字总是有区别的。命令和 关键字的差别有多大呢?
    如果是命令,它就和参数组合为一体被 shell 解释,那样比如 ">" "<" 就被 shell 解释为重定向符号了。关键字却不这样。
    在 [[ 中使用 && 和 ||: [ 中使用 -a 和 -o 表示逻辑与和逻辑或。
    [[ 中可以使用通配符

arch=i486
[[ $arch = i86 ]] && echo "arch is x86!"
[[ 中匹配字符串或通配符,不需要引号
[[ $arch_com = i386 || $ARCH = i
86 ]] &&
cat >> $TFS_REPO <<EOF
[tfs-i386]
name=GTES11.3 prelim1
baseurl=${BASEURL}i386/
enabled=1
EOF

备注五 $() ${} () {} (())

  1. 符号$后的括号
    ${a}
    变量a的值, 在不引起歧义的情况下可以省略大括号.
    $(cmd)
    命令替换, 结果为shell命令cmd的输出, 和cmd效果相同, 不过某些Shell版本不支持$()形式的命令替换, 如tcsh.
    $((exp))
    expr exp效果相同, 计算数学表达式exp的数值, 其中exp只要符合C语言的运算规则即可, 甚至三目运算符和逻辑表达式都可以计算.

  2. 多条命令执行
    (cmd1;cmd2;cmd3) 新开一个子shell顺序执行命令cmd1,cmd2,cmd3, 各命令之间用分号隔开, 最后一个命令后可以没有分号.
    { cmd1;cmd2;cmd3;} 在当前shell顺序执行命令cmd1,cmd2,cmd3, 各命令之间用分号隔开, 最后一个命令后必须有分号, 第一条命令和左括号之间必须用空格隔开.
    对{}和()而言, 括号中的重定向符只影响该条命令, 而括号外的重定向符影响到括号中的所有命令.

  3. 双括号的特殊用法
    (( ))
    (()) 增强括号的用法, 常用于算术运算比较. 双括号中的变量可以不使用$符号前缀, 只要括号中的表达式符合C语言运算规则, 支持多个表达式用逗号分开.
    比如可以直接使用for((i=0;i<5;i++)), 如果不使用双括号, 则为for i in seq 0 4或者for i in {0..4}.
    再如可以直接使用if (($i<5)), 如果不使用双括号, 则为if [ $i -lt 5 ].
    [[ ]]
    [[]] 增强方括号用法, 常用于字符串的比较. 主要用于条件测试, 双括号中的表达式可以使用&&, ||, <, >等C语言语法.
    比如可以直接使用if [[ $a != 1 && $a != 2 ]], 如果不适用双括号, 则为if [ $a -ne 1] && [ $a != 2 ]或者if [ $a -ne 1 -a $a != 2 ].

Shell编程中shift的用法

位置参数可以用shift命令左移(把最前面的参数给吃掉)。比如shift 3表示原来的$4现在变成$1,原来的$5现在变成$2,原来的$1、$2、$3丢弃,$0不移动。
不带参数的shift命令相当于shift 1。

若用户要求 Shell 在不知道位置变量个数的情况下,还能逐个的把参数一一处理,也就是在 $1 后为 $2,在 $2 后面为 $3 等。在 shift 命令执行前变量 $1 的值在 shift 命令执行后就不可用了。

示例如下:
until [ $# -eq 0 ]
do
echo "第一个参数为: $1 参数个数为: $#"
shift
done
执行以上程序x_shift.sh:
$./x_shift.sh 1 2 3 4
结果显示如下:
第一个参数为: 1 参数个数为: 4
第一个参数为: 2 参数个数为: 3
第一个参数为: 3 参数个数为: 2
第一个参数为: 4 参数个数为: 1

shift 命令还有另外一个重要用途, Bash 定义了9个位置变量,从 $1 到 $9,这并不意味着用户在命令行只能使用9个参数,借助 shift 命令可以访问多于9个的参数。shift 命令一次移动参数的个数由其所带的参数指定。例如当 shell 程序处理完前九个命令行参数后,可以使用 shift 9 命令把 $10 移到 $1。

shell中的 -- 含义 (两个横线的含义)
两个横线代表选项的结束,两个横线后面的部分都会被认为是参数了,而不再是前面的命令的选项了。
[ ~]$ echo -- -e hello
-- -e hello
[ ~]$ echo -e hello
hello
echo -- -e hello和echo -e hello是不一样的,前者-e是一个普通参数,后者-e则是echo的一个选项.
在你的这个场合下,set -- mysqld表示重设脚本的参数为mysqld,会影响到$argv变量和$1,$#等和参数有关的变量(-- 表示将任何剩余的参数分配给位置参数, 如果没有剩余的参数, 就会将位置参数复位).
-- 代表后面的字符不是set命令的选项( 比如 set -- "" "-h",表示用set指令将参数追加一个-h).
Chanix-LGdeMacBook-Pro:shell_test chanix$ echo
Chanix-LGdeMacBook-Pro:shell_test chanix$ set -- "" "-h"
Chanix-LGdeMacBook-Pro:shell_test chanix$ echo
-h

getopts的用法
知道用基本的$*,执行的情况,大概就是$0 $1 $2 $3……
那么,那些系统命令里的参数又是怎么做出来的呢?我们自己的脚本如何搞出来$0-$1的效果呢?这就是getopts的作用了。

语法格式:
getopts [option[:]] [DESCPRITION] VARIABLE
如while getopts ":pq:" optname

option: 表示为某个脚本可以使用的选项
":" : 如果某个选项(option)后面出现了冒号(":"),则表示这个选项后面需要接参数(即一段描述信息DESCPRITION).

while getopts "???bc:" opt #第一个冒号表示忽略错误(如果命令行中包含了没有在getopts列表中的选项,会有警告信息,如果在整个getopts字符串前面也加上个:,就能消除警告信息了);字符后面的冒号表示该选项必须有自己的参数。

VARIABLE:表示将某个选项保存在变量VARIABLE中,比如取名为 optname.

getopts是linux系统中的一个内置变量,一般用在循环中。每当执行循环时,getopts会检查下一个命令选项,如果这些选项出现在option中,则表示是合法选项。并将这些合法选项保存在VARIABLE这个变量中。

getopts还包含两个内置变量,及OPTARG和OPTIND
OPTARG就是将选项后面的参数(或者描述信息DESCPRITION)保存在这个变量当中。
OPTIND:这个表示命令行的下一个选项或参数的索引(文件名不算选项或参数).

选项参数识别完成之后,如果要取剩余的其它命令行参数,可以使用shift把选项参数抹去,就像例子里面的那样,对整个参数列表进行左移操作,最左边的参数就丢失了(已经用case判断并进行了处理,不再需要了),位移的长度正好是刚才case循环完毕之后的OPTIND - 1,因为参数从1开始编号,选项处理完毕之后,正好指向剩余其它参数的第一个。在这里还要知道,getopts在处理参数的时候,处理一个开关型选项,OPTIND加1,处理一个带值的选项参数,OPTIND则会加2。

举例如下:

!/bin/bash

echo "OPTIND starts at $OPTIND"
while getopts ":pq:" optname # 这里p后没有冒号,代表-p后不跟参数.如 xxx.sh -p -q yyy格式.区别:p:q:,p:q,p:q: ,:pq: 写法
do
case "$optname" in
"p")
echo "Option $optname is specified"
;;
"q")
echo "Option $optname has value $OPTARG"
;;
"?")
echo "Unknown option $OPTARG"
;;
"??
echo "No argument value for option $OPTARG"
;;
*)
# Should not occur
echo "Unknown error while processing options"
;;
esac
echo "OPTIND is now $OPTIND"
done
在使用getopts命令的时候,shell会自动产生两个变量OPTIND和OPTARG。
OPTIND初始值为1,其含义是下一个待处理的参数的索引。只要存在,getopts命令返回true,所以一般getopts命令使用while循环;并且OPTIND的体现并不明显。
OPTARG就是选项后面跟的参数值,比如/usr/local/nginx/sbin/nginx -s stop 其中stop就是$OPTARG的值。
OPTARG是当getopts获取到其期望的参数后存入的位置。而如果不在其期望内,则$optname被设为?并将该意外值存入OPTARG;如果$optname需要拥有具体设置值而实际却没有,则$optname被设为:并将丢失设置值的optname存入OPTARG;
对于$optname,可以用后标:来表示是否需要值;而前标:则表示是否开启静默模式。

综上bash示例(用于java进程的启动脚本)
shell示例:
function show_usage() {
echo "Arguments:"
echo "--module_name (-n): 模块名称"
echo "--config_dir (-c): 配置路径"
echo "--log_dir (-l): 日志路径"
echo "--memory (-m): 内存大小";
exit 0;
}

for param in ""; do
shift
case "$param" in
"--help") set -- "" "-h" ;; # -- 表示将任何剩余的参数分配给位置参数, 如果没有剩余的参数, 就会将位置参数复位.
"--module_name") set -- "" "-n" ;;
"--config_dir") set -- "" "-c" ;;
"--memory") set -- "" "-m" ;;
"--log_dir") set -- "" "-l" ;;
*) set -- "" "$param"
esac
done

MODULE_NAME=
CONFIG_PATH=
MEM=
LOG_DIR=
while getopts "n:c??n:l:" opt
do
case ${opt} in
n)
MODULE_NAME=$OPTARG
;;
c)
CONFIG_PATH=$OPTARG
;;
m)
MEM=$OPTARG
;;
l)
LOG_DIR=$OPTARG
;;
?)
show_usage
;;
esac
done

相关推荐