跳到主要内容

[toc]

shell特殊位置环境变量

变量含义
$0脚本名称
$n位置变量,n代表数字,超过10要用括起来
$#参数个数
$*、$@列出参数
$?上一个命令的执行结果返回值
$()表示先执行里边的内容
$"金庸新著" 和 "金庸新"著 用于区分变量
$!获取上一个脚本的PID
$_获取上一个脚本的最后一个参数
$-显示shell使用的当前选项,与set命令功能相同

$0 当前脚本的文件名,如果执行脚本包含了路径,那么就包括脚本路径

#编辑脚本内容
$ cat >a.sh <<'EOF'
#!/usr/bin/env bash
echo "当前脚本名为:$0"
EOF

#执行脚本
$ sh a.sh
当前脚本名为:a.sh

#执行路径+脚本名
$ sh /root/a.sh
当前脚本名为:/root/a.sh

$n 位置变量,获取当前执行的脚本的第n个参数,n=1..9,如果n大于9,用大括号括起来$10

#编辑脚本内容
$ cat > b.sh <<'EOF'
#!/usr/bin/env bash
echo "脚本的第1个参数为:$1"
echo "脚本的第10个参数为:${10}"
EOF


#执行脚本
$ sh b.sh 10 9 8 7 6 5 4 3 2 1
脚本的第1个参数为:10
脚本的第10个参数为:1

$# 获取当前执行脚本后接的参数总个数

#编辑脚本内容
$ cat > c.sh <<'EOF'
#!/usr/lib/env bash
echo "脚本参数总个数为:$#"
EOF

#执行脚本
$ sh c.sh 1 2 3 4 5 6
脚本参数总个数为:6

$*和¥@ 列出所有参数

  • $ 以"参数1 参数2 参数3 ..."的形式列出所有参数*

  • $@ 以"参数1" "参数2" "参数3"的形式列出所有参数

*不加引号时,$和$@输出一样

#$*
$ set -- a b c
$ for i in $*;do echo $i;done
a
b
c


#$@
$ set -- a b c
$ for i in $@;do echo $i;done
a
b
c

*加入引号时,$和$@输出不一样

#$*
$ set -- a b c
$ for i in "$*";do echo $i;done
a b c


#$@
$ set -- a b c
$ for i in "$@";do echo $i;done
a
b
c

$? 上一个命令的执行结果返回值

常见返回值

#命令错误,返回值127
$ lp
-bash: lp: 未找到命令
$ echo $?
127

#参数不正确或者文件目录不存在
$ ls -e
ls:无效选项 -- e
Try 'ls --help' for more information.
$ echo $?
2

#权限拒绝,不是目录或文件等等
$ touch /opt/txt
touch: cannot touch ‘/opt/txt’: Permission denied
$ echo $?
1

#命令正确执行
$ ls
$ echo $?
0

$() 表示先执行里边的内容

$()等同于``

#脚本中想要把一个命令的输出复值给一个变量
$ ip=$(ip a s eth0|awk -F'[ /]'+ 'NR==3{print $3}')
$ echo $ip
10.0.0.10

$ "金庸新著" 和 "金庸新"著 用于区分变量

#想要输出你好a,但是因为没有加{},所以aa就成了最终的变量,但是变量名是a
$ a='你好'
$ echo $aa
输出内容为空

#用{}解决以上问题
$ a='你好'
$ echo ${a}a
你好a

$! 获取最后运行的后台进程的PID

⚠️必须是后台进程

#下载nginx官方的一张图片
$ nohup wget nginx.org/nginx.png &
$ echo $!
1065

$_ 获取上一个脚本的最后一个参数

#编辑脚本内容
$ cat > b.sh <<'EOF'
#!/usr/bin/env bash
echo "脚本的第1个参数为:$1"
echo "脚本的第10个参数为:${10}"
EOF

#执行脚本
$ sh b.sh 10 9 8 7 6 5 4 3 2 1
脚本的第1个参数为:10
脚本的第10个参数为:1

#获取上一个脚本的最后一个参数
$ echo $_
10

$- 显示shell使用的当前选项,与set命令功能相同

#shell默认选项是himBH
$ echo $-
himBH

#
$ set -x
++ printf '\033]0;%s@%s:%s\007' root test1 '~'
$ echo $-
+ echo himxBH
himxBH
++ printf '\033]0;%s@%s:%s\007' root test1 '~'

shell默认选项himBH,每个字母都代表了一个 shell 选项

h - hashall         
i - interactive-comments
m - monitor
B - braceexpand
H- history

⚠️⚠️⚠️注意这里的 "设置-" 和 "取消+" 是反人类的:设置用 -,关闭反而是用 +

h - hashall

bash 的 hash 功能,可以实现让某些 command 和 具体路径 绑定在一起

查看默认选项

$ echo $-
himBH
$ hash -p /tmp/aaadate date
$ hash -l |grep aaadate
builtin hash -p /tmp/aaadate date

#此时再执行命令date,会发现原先的/usr/bin/date变成了/tmpaaadate
$ date
-bash: /tmp/aaadate: No such file or directory

#执行set +h +h表示去掉h,也就是imBH 命令不能和具体路径绑定,因此date命令能正确执行
$ set +h
$ echo $-
imBH
$ date
2020年 06月 24日 星期三 00:26:50 CST

#加上h 命令可以和具体路径绑定
$ set -h
$ echo $-
himBH
$ date
-bash: /tmp/aaadate: 没有那个文件或目录

#恢复
$ hash -d date
$ date
2020年 06月 24日 星期三 00:29:14 CST

i - interactive-comments

配置在交互 shell 模式下,是否允许注释

恢复默认选项

$ echo $-
himBH

必须使用set +o interactive-comments,set +i不好使,原因未知

#设置命令行不允许注释
$ set +o interactive-comments
$ echo $-
himBH

#在命令行加上# 此时是不被允许的
$ #testcomment
-bash: #testcomment: 未找到命令


#取消设置
$ set -o interactive-comments
$ echo $-
himBH
$ #testcomment

m - monitor

配置是否打开控制 Job control 功能

恢复默认选项

$ echo $-
himBH

Job control 是什么? 即可以控制进程的停止、继续,后台或者前台执行等。

开启 job control 后,如果执行了一个比较耗时的命令,可以按下 CTRL+Z 让它在后台运行:

$ sleep 50
^Z
[1]+ 已停止 sleep 50

然后, 可以用 fg 命令将后台运行的任务恢复到前台执行

$ fg 1
sleep 50

如果关闭这个选项,就会失去控制 Job 的能力

$ set +m
$ echo $-
hiBH

#此时使用ctrl+z不管用
$ sleep 50
^Z^Z^Z^Z^Z^C

$ fg
-bash: fg: 无任务控制

B - braceexpand

关于大括号使用的flag,打开后可以快捷地实现某些效果

恢复默认选项

$ echo $-
himBH

利用大括号输出文件

echo {1..5}.txt
1.txt 2.txt 3.txt 4.txt 5.txt

关闭大括号效果

$ set +B
$ echo $-
himH

再次执行命令发现不生效了

echo {1..5}.txt
{1..5}.txt

H - histexpand

是否允許用 "感叹号 !+ history number" 来执行历史命令

!! 返回并执行最近的一个历史命令 !n 返回并执行第 n 个历史命令

恢复默认选项

$ echo $-
himBH
#执行命令
$ ls /tmp/
ks-script-2R8Xnq yum.log

#使用!运行上一次以l开头的命令
$ !l
ls /tmp/
ks-script-2R8Xnq yum.log

#取消!功能
$ set +H

#再次执行就会报错
$ !l
-bash: !l: 未找到命令

问题

由于 histexpand 打开的时候,"!" 带特殊含义; 因此 histexpand 打开状态下,"!" 不能出现在双引号中, 否则会报错 -bash: !": event not found

$ echo $-
himBH

#想要输出hehe!,但是却报错了,原因是命令行下,双引号里面用了!的话,Shell 会以为要执行历史展开,从而导致报错
$ echo "hehe!"
-bash: !": event not found

解决方法一 关闭histexpand

$ echo $-
himBH
$ set +H
$ echo $-
himB
$ echo "hehe!"
hehe!

解决方法二 使用单引号

$ echo $-
himBH
$ echo 'hehe!'
hehe!