符号

shell非常的灵活,一条简单的命令,往往能做很大的事情。别人说,web界面是方便输入的,而shell这种,更方便批量操作。

但是shell有时候非常的晦涩,尤其是大量的符号,比如无限复制进程的fork炸弹代码如下:

:() { :|:& };:

符号虽然抽象,但是功能强大啊。一句胜过千言万语。

另外,听同事讲,shell里面的命令,基本上都是命令。比如 ((i=i+1)),两个括号就是命令,括号内的语法,模仿c语言的。中括号,也差不多。为啥要留空格呢,怕被识别成一个变量???

花括号

定义函数体时,使用。

greet(){
    echo "hello",$1
}

#调用
greet zhangsan

代码块:重定向输出

{
    echo 1
    echo 2
} > 22.txt

# 错误重定向,标准输出无内容,错误输出有内容。  
{
    echo 1
    echo 2
} >&2

代码块:后台运行

不需要单独写个函数,然后封装成脚本后再运行,非常方便。

{
    while true;do
    echo hello world
    sleep 1
    done
} &

变量配合

见变量部分。

${hello}

多个参数

就像在bash中,会自动解析*的作用一样,会将花括号内的多个解析出来。

格式:花括号内,用逗号分隔出多项。

可以使用echo 来查看展开的内容。如:echo demo.txt{,.bak}

echo {1,2,3,4}.txt 无论有没有这些文件,都会展开出来。

cp demo.txt{,.bak}   # 等价于  cp demo.txt demo.txt.bak
ls cloud/{back,front}*/*.php


# 使用转移符号 
echo -e {0..9}{0..9}{0..9}'\n'
# 嵌套使用
echo {{1..20},3}
echo {{a..z},3}
echo {{a..z},3,4,5,{b..g}}

中括号

单中括号[]

判断。配合特殊的语法,来实现逻辑运算。但是,记得中括号内两侧有空格。

[ -z "$1" ]   # 如果字符串为空,true
[ "$1" ]      # $1字符串不为空时,true
[ -eq $1 10 ]  # 数字比较,相等
[ ! -eq $1 10 ]  # 感叹号 否定
# 其他符号  -a 且   -o  或

双中括号[[]]

$[]

中括号两侧的空白符号,可有可无。

# 数学表达式计算
$[ 1002/12 ]

echo hello$[ 1024*10 ]

综合

#!/bin/bash
read -p "Please input your score: " score
# 使用了正则
if [[ $score =~ [^0-9] ]] ;then
    echo "please input a int"
    exit 10
elif [ $score -gt 100 ];then
    echo "Your score is wrong"
    exit 20
elif [ $score -ge 85 ];then
    echo "Your score is very good"
elif [ $score -ge 60 ];then
    echo "Your score is soso"
else
    echo "You are loser"
fi

小括号

新进程

新起一个进程运行。注意,括号内的变量,在外部访问不到。

(while true;do echo hello world;sleep 1;hello=1;done) 

# 停止运行后,echo $hello   是没有输出的。而去掉括号,则可以。

封装返回

同样,使用于函数的返回

demo=$(ls -alh)
echo "$demo"   # 原样输出
echo $demo     # 输出一行
# 在字符串内,也会被解析。
demo="$(ls -alh)"

双小括号

比如for循环等等。运算符号。

for((i=0;i<10;i++));do 
    ((b=i+2)); # 独立的语句,还不能直接 echo
    ((c=2*i))
    echo $b;
done

`符号

封装返回。跟$( some code )效果类似。

demo=`ls -alh`
echo "$demo"

变量

所有的变量以$前缀。

对于脚本参数,以 $1、$2...数字前缀。 $0当前的脚本

函数类似。

参数个数。

变量运算,参见字符串处理文章。

$?

上次的执行结果。如果正常,则等于0。

# 判断上次执行结果
if [ $? -eq 0 ];then echo ok ;else echo fail ;fi

其实,另外一种方式

感叹号

# 执行最后1条命令
!!

# history中第n条命令
!7

单引号

定义文本变量,其内的$、*等符号,会被保护起来。不会被解析。可以保持文本的换行符号。

保护功能,参加error文章。

双引号

定义文本变量,允许变量注入。可以保持文本的换行符号。

heredoc

3种格式如下:

呼应的并一定非要EOL,任何和正文不冲突的符号都行。结尾,必须要顶头写,不能有空白字符。

var=hello
<<EOL
		多行文本,这个里面的 $var 变量会被 shell解析
EOL


<<'EOL'
		多行文本,这个里面的 $var 变量会不会被 shell解析
EOL


<<-EOL
		多行文本,这个里面的 $var 变量会被 shell解析,制表符号会被移除。
EOL

# 组合形式   <<-'EOL'

备注:-EOL只能移除制表符号时,空格不会处理。

字符串相加

其实没有相加符号,只要两个文本或变量想靠近即可。

var1=haha
var2=qie
echo $var1$var2
echo "hello"$var2


# 在命令行中
now=`date '+%Y%m%d%H%M%S'`
sed -i 's#ver=version#ver='$now'#g' "$each"

星号 *

bash会自动对*解析,然后转换成多个参数,注入到脚本中。

echo my*.txt

如果想避免解析,那么应该使用单引号、双引号保护起来。

find查找命令,对于下面的*.html,带不带双引号,有很大的区别。不带双引号保护,会被shell解析,然后,

find . -type f -name "*.html"`;do

如果,变量需要内部的命令去解析,也要先用引号保护起来。

# 比如,有个demo.sh 接收一个参数,参数实际上用在内部的sed上。
sed -i "s///g" $1

# 调用bash时, demo.sh  'backend/*.php' ,这个时候,'backend/*.php'在sed命令行中展开。

如果,文件名中有空格,用引号保护,但是*号放在引号外。示例如下:

#Spring 事务一网打尽 (P12. 12.Spring事务传播性之MANDATORY).flv 
ls "Spring 事务一网打尽 (P12"*    

任意路径**

非常好的命令,能避免很多场景下的for循环。比如,grep target_string /path/to/**/*.php

# 查看
shopt 
# globstar        off
# 开启
shopt -s globstar 

开启后,就可以使用任意路径功能了。

好出非常的大,最起码避免先查找文件、然后在for循环里面处理文件了。

如:

grep sometxt  cloud/frontend/**/*.php

问号

路径参数中使用。

作用,只能替代一个字符。

touch a1.txt
touch a2.txt
# 查找出a1、a2两个文件
ls a?.txt
# 查找不到内容
ls a??.txt

逻辑运算符号 && ||

跟编程语言的逻辑运算符号类似,也有短路效果。

如下,即判断是否存在hello这个文件夹,如果报错,则提示报错

cd  hello && echo exist hello  ||  echo no hello dir

上面那种,是成功、或失败了才会执行相应的分支。

其实还有一种情况,不管成功与否都要执行,其实,就是简单的分号,即可。

domesomething; ding.sh

&

后台

程序需要挂到后台运行。

nohup /yd/td2/shell/import_job.sh >> /yd/td2/runtime/cron/log.txt 2>&1

定时任务,合并输入、错误流

01 00 * * * /yd/td2/shell/import_job.sh >> /yd/td2/runtime/cron/log.txt 2>&1

# 简写形式
ding.sh >& log.txt
ding.sh &> log.txt

# 追加 ,只能按下面这样写
ding.sh &>> log.txt

dockerfile的套路

在dockerfile,很喜欢将多条语句合成1条语句执行。如下,两个示例

方式一

RUN  echo hello && \
    echo 1234 && \
    echo 4567 

方式二

RUN  echo hello \
    && echo 1234 \
    && echo 4567 

分号

代码的分隔符号。因为shell的结尾;不是必须的,如果要写在一行的话,则必须写。这跟jsgolang都很像。

比如docker需要执行多条命令,使用下面方式。

bash -c 'echo 123;echo 456'

输入、输出

输入、输出符号

输入内容

简单的编辑代码

cat > test.sh <<EOL
echo 123
echo 456
echo 789
EOL
# 没有echo  直接 重定向到文件,效果:清空文件
> backup.sh

# 将输出重定想到文件
exec > 2.txt 

管道符号|

一个程序的输出,作为另外一个程序的输入,注意,不是作为,另一个程序的参数。

seq 10|sed 's#^#big#'|while read line;do echo $line"   chuangqi";done |cat -n

作为参数时,有两种方案:

xargs

seq 10|xargs echo

seq 10|xargs -n 1  echo

sed/awk… + bash

相当于动态的生成一个shell,然后再去执行。

seq 10|sed 's#^#echo #'|sh

管道,合并错误 |&

grep hello test.txt|&cat -n    # 通过-n参数,观测到,通过管道传递给cat
     1  grep: test.txt: No such file or directory

短横线

一般习惯将 - 表示,从输入流获取内容。

比如,tar、cat、nc等

在文件末尾追加多行。-表示的输入刘。

cat exa1.txt - > output <<EOF
some lines you want to insert
go here
EOF

-- 缺省

kubectl exec -it qingcloud-844ffd6575-pgjq2 -n qing-cloud -- bash

转义符号 \

如find命令后,exec的参数需要转移。

find . -type d -exec echo {} \;

hello=1
echo $hello
echo \$hello

字符串类的转义


echo -e "hello\nworld"

正则转义