cndaqiang Web Linux DFT

bash学习(一)基本语法

2017-08-07
cndaqiang
RSS

这篇文章之前写在简书上的,简书和这个博客的md语法有些差异,调节起来太麻烦了,以后复习bash时再重新整理

参考

Linux bash总结(一) 基础部分(适合初学者学习和非初学者参考)

Linux 入门公开课

C语言中文网-Shell教程

处理linux的echo命令不输出转义符号的问题

runoob-Shell 教程

Linux标准输入/输出和重定向

Linux 输入输出重定向, &>file, 2>&1, 1>&2 等

exec的重定向 - GitHub

linux shell 管道命令(pipe)使用及与shell重定向区别

数据类型

1.数值

数值类型转换

因为bc不支持科学计数法,所以有时候需要转换格式,使用printf

cndaqiang@mommint:~$ printf "%20.15f\n" 1.234e-6
   0.000001234000000
cndaqiang@mommint:~$ printf "%20.15e\n"  0.000001234000000
1.234000000000000e-06
#awk中示例
fermi=$(grep fermi_energy $PWprefix.save/data-file-schema.xml | head -1 | awk   -F"<" '{ print $2 }' | awk   -F">" '{ printf ("%20.15f", $2) }')

2.字符串

字符串可以用单引号,也可以用双引号,也可以不用引号。单双引号的区别跟PHP类似

2.1单引号

单引号的特点:

  • 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;
  • 单引号字串中不能出现单引号(对单引号使用转义符后也不行
    [cndaqiang@localhost tmp]$ echo 'hello$val'
    hello$val
    

2.2双引号

双引号的优点:

  • 双引号里可以有变量
  • 双引号里可以出现转义字符
    [cndaqiang@localhost tmp]$ echo "${val}hello\'"
    1hello\'
    

    字符串更多操作,见下面的 运算/2字符串运算

变量&数组

1.变量

1.1定义,使用

  • 变量的定义不加$
  • 不加$表示变量本身,可以val++,加$表示变量存储的值val=$val+1
  • $((i++))返回i本身,(++i))返回i+1,处理后i=i+1
  • val=anything 等号两侧不能有空格
    ubuntu@VM-10-194-ubuntu:/tmp$ VAR="some"
    ubuntu@VM-10-194-ubuntu:/tmp$ NUM=123
    # 注意:等号两侧不能有空格! 变量名称全部用大写是编程惯例
    ubuntu@VM-10-194-ubuntu:/tmp$ echo VAR
    VAR
    ubuntu@VM-10-194-ubuntu:/tmp$ echo $VAR
    some
    # 调用变量要加 $
    [cndaqiang@localhost tmp]$ $val=hello
    -bash: 1=hello: command not found
    #$val表示变量存储的值,不能作为左值
    #=左右有空格时报错
    cndaqiang@girl:~/code/cndaqiang.github.io$ a = b
    a: command not found
    cndaqiang@girl:~/code/cndaqiang.github.io$ a= b
    b: command not found
    cndaqiang@girl:~/code/cndaqiang.github.io$ a=b
    cndaqiang@girl:~/code/cndaqiang.github.io$ 
    

1.2只读变量

格式 readonly val

[cndaqiang@localhost tmp]$ val=hello
[cndaqiang@localhost tmp]$ readonly val
[cndaqiang@localhost tmp]$ val=1
-bash: val: readonly variable

1.3删除变量

格式 unset val

1.4变量类型

运行shell时,会同时存在三种变量:

  • 局部变量 局部变量在脚本或命令中定义,仅在当前shell实例中有效,其他shell启动的程序不能访问局部变量。
  • 环境变量 所有的程序,包括shell启动的程序,都能访问环境变量,有些程序需要环境变量来保证其正常运行。必要的时候shell脚本也可以定义环境变量。
  • shell变量 shell变量是由shell程序设置的特殊变量。shell变量中有一部分是环境变量,有一部分是局部变量,这些变量保证了shell的正常运行

2.数组

2.1定义,使用

定义 数组名=(值1 值2 ... 值n) 或者 数组名[下标]=值

使用 ${数组名[下标]}

#创建 a=( )
ubuntu@VM-10-194-ubuntu:/tmp$ a=(1 2 3 f g)
ubuntu@VM-10-194-ubuntu:/tmp$ echo $a
1
#显示所有元素或者echo ${a[*]}    {}表示内部是一个整体,也可以 ${val},传入参数$10时也要${10}
ubuntu@VM-10-194-ubuntu:/tmp$ echo ${a[@]}
1 2 3 f g
#显示数组元素,0代表第1个元素
ubuntu@VM-10-194-ubuntu:/tmp$ echo ${a[2]}   
3
ubuntu@VM-10-194-ubuntu:/tmp$ a["sth"]=TT  #这样也行
ubuntu@VM-10-194-ubuntu:/tmp$ echo ${a[@]} 
TT 2 3 f g
#或者逐个创建
ubuntu@VM-10-194-ubuntu:/tmp$ a[6]=100
ubuntu@VM-10-194-ubuntu:/tmp$ echo ${a[@]}
TT 2 3 f g 100
#显示数组元素删除a[2]的值,并不删除a[2]
ubuntu@VM-10-194-ubuntu:/tmp$ unset a[2]   
ubuntu@VM-10-194-ubuntu:/tmp$ echo ${a[2]}

ubuntu@VM-10-194-ubuntu:/tmp$ unset a   #删除数组a所有元素
#${数组名[@或*]:起始位置:长度} 输出数组的第1-3项
ubuntu@VM-10-194-ubuntu:/tmp$ echo ${a[@]:0:3}
#${数组名[@或*]/查找字符/替换字符   替换输出,并不改变数组
ubuntu@VM-10-194-ubuntu:/tmp$ echo ${a[*]/1/6}

2.2获取数组长度

# 取得数组元素的个数
length=${#array_name[@]}
# 或者
length=${#array_name[*]}
# 取得数组单个元素的长度
lengthn=${#array_name[n]}

注意

直接对数组赋值,并不会把数组变成变量,如果之前定义了数组,现在要重新定义最好unset array

(python37) cndaqiang@mommint:~$ N=(1 2 3 4 5 6 7 8)
(python37) cndaqiang@mommint:~$ N=''
(python37) cndaqiang@mommint:~$ echo ${N[@]}
2 3 4 5 6 7 8

3 字典

示例

declare -A CarOrFrac
CarOrFrac["c"]="ScaledCartesian"
CarOrFrac["C"]="ScaledCartesian"
CarOrFrac["d"]="Fractional"
CarOrFrac["D"]="Fractional"

应用-构建key为元素符号,value为原子序数的字典

declare -A SpeciesOrder
Species=( H       He      Li      Be      B       C       N       O       F       Ne      Na      Mg      Al      Si
P       S       Cl      Ar      K       Ca      Sc      Ti      V       Cr      Mn      Fe      Co      Ni
Cu      Zn      Ga      Ge      As      Se      Br      Kr      Rb      Sr      Y       Zr      Nb      Mo
Tc      Ru      Rh      Pd      Ag      Cd      In      Sn      Sb      Te      I       Xe      Cs      Ba
La      Ce      Pr      Nd      Pm      Sm      Eu      Gd      Tb      Dy      Ho      Er      Tm      Yb
Lu      Hf      Ta      W       Re      Os      Ir      Pt      Au      Hg      Tl      Pb      Bi      Po
At      Rn      Fr      Ra      Ac      Th      Pa      U       Np      Pu      Am      Cm      Bk      Cf
Es      Fm      Md      No      Lr)
Order=1
for i in ${Species[@]}
do
SpeciesOrder[$i]=$Order
((Order++))
done

bash文件

1. 创建文件

ubuntu@VM-10-194-ubuntu:/tmp$ > test.sh
ubuntu@VM-10-194-ubuntu:/tmp$ touch test2.sh
ubuntu@VM-10-194-ubuntu:/tmp$ echo "something" >test3.sh
ubuntu@VM-10-194-ubuntu:/tmp$ cat test3.sh 
something

创建多行,使用了重定向的知识,重定向见本文后段

ubuntu@VM-10-194-ubuntu:/tmp$ cat >test4.sh<<EOF
> some
> shu wan zhi jie hui che
> zi dong tian jia >
> EOF
#EOF为任意字符串
ubuntu@VM-10-194-ubuntu:/tmp$ cat test4.sh 
some
shu wan zhi jie hui che
zi dong tian jia >

不过还是喜欢,vi file 直接输入

2. 添加执行权限

$ chmod +x test.sh 

3. 编写脚本

#!/bin/bash
echo "hello bash"
exit 0

说明: #!/bin/bash : 它是bash文件声明语句,表示是以/bin/bash程序执行该文件。它必须写在文件的第一行!

echo "hello bash" : 表示在终端输出hello bash

exit 0 : 表示返回0。在bash中,0表示执行成功,其他表示失败。

4. 执行

ubuntu@VM-10-194-ubuntu:/tmp$ ./test.sh 
hello bash

注释

  • 单行注释 以#开头的行就是注释
  • 多行注释 sh里没有多行注释,可以把这一段要注释的代码用一对花括号括起来,定义成一个函数,没有地方调用这个函数,这块代码就不会执行,达到了和注释一样的效果

zhushi()
{
zhushi注释内容
echo zhushi
}
:||{
被注释的多行内容
}

运算

1 数值运算

数值元算主要有4种实现方式:(())、let、expr、bc。 工作效率: (()) == let > expr > bc

  • (())let是bash内建命令,执行效率高,支持i++;而exprbc是系统命令,会消耗内存,执行效率低。
  • expr只支持整数计算
    expr a opr b其中opr支持+,-,\*,/和取余%
  • 只有bc支持浮点运算
  • (())内只能写整数计算的公式,写小数会报错
  • 对于一些高级的计算如sin可以使用awk,见文件处理: grep sed awk sort echo printf tail cut paste


#!/bin/bash
#(())  要加个$再赋予变量
val=$((3*(5+2)))
echo $val

#let
let "val=3*(5+2)"
echo $val

#expr 又费劲  注意是`重音符  ``输出命令结果,例如val=`ls`,val=`date`
#还得用空格隔开运算符和数字,不然显示不对
val=`expr 3 \* \( 5 + 2 \)`
echo $val

#bc 是重音符`
val=`echo "3*(5+2)"|bc`
echo $val

使用$(())let来进行整数计算,bc进行浮点数计算

$(( 3+3 ))

i++ ((val++))或者 let val++

例:bc精度计算,bc默认精度小数点后0位,使用scale设置

SE=$(echo "scale=7;(1/2*($ssenergy-$j*$Ebulk)+($sfenergy-$ssenergy))/${S[i]}" | bc)

错误示例

 $ val=$((3.00*(5+2)+$val))
-bash: 3.00*(5+2)+42:语法错误: 无效的算术运算符 (错误符号是 ".00*(5+2)+42")
 $ val=$((3*(5+2)+$val))

计算精度scale, 格式化输出的计算结果printf

cndaqiang@girl:~/scripts/structure2structure$ echo "scale=7;1.0*5" | bc 
5.0
cndaqiang@girl:~/scripts/structure2structure$ printf "%15.7f\n" $(echo "scale=7;1.0*5" | bc )
      5.0000000

2 字符串运算

2.1字符串的使用

ubuntu@VM-10-194-ubuntu:/tmp$ str="hello world"
ubuntu@VM-10-194-ubuntu:/tmp$ echo ${str}
hello world
ubuntu@VM-10-194-ubuntu:/tmp$ echo ${strq:?ERR_MSG}
-bash: strq: ERR_MSG

2.2 字符串操作

ubuntu@VM-10-194-ubuntu:/tmp$ echo ${#str}
11
ubuntu@VM-10-194-ubuntu:/tmp$ echo ${str:3:6}
lo wor
ubuntu@VM-10-194-ubuntu:/tmp$ echo ${str%ld}
hello wor
#并不影响原字符串
ubuntu@VM-10-194-ubuntu:/tmp$ echo ${str}
hello world

常用

  • 从左边第n个开始的m个字符${str:n:m},n从0开始计数
  • 从右边第n个开始的m个字符${str:0-n:m},n从1开始计数,0-是参数
    cndaqiang@mac$ str=structure.xyz
    cndaqiang@mac$ echo ${str:0:3}
    str
    cndaqiang@mac$ echo ${str:1:3}
    tru
    cndaqiang@mac$ echo ${str:0-1:3}
    z
    cndaqiang@mac$ echo ${str:0-5:3}
    e.x
    

    流程控制(判断循环)

    1 条件判断

    1.1 test

    语法

    test EXPRESSION
    

    test是关键字,表示判断; EXPRESSION是被判断的语句。

1.1 数值判断

这些-开头比较仅适用于整数,不能比较字符串,字符串看下面

参数 说明
-eq或 == 等于则为真
-ne 或!= 不等于则为真
-gt 或> 大于则为真
-ge 或>= 大于等于则为真
-lt 或< 小于则为真
-le 或<= 小于等于则为真

使用>=和<=时,两边的数值和判断算符不要有空格

[cndaqiang@localhost tmp]$ test 12 -eq 2
[cndaqiang@localhost tmp]$ echo $?
1
# $?表示上一命令的执行结果(在linux中bash中,*true返回0,false返回1*

1.2字符串测试

参数 说明
= 等于则为真
!= 不相等则为真
-z 字符串 字符串的长度为零则为真
-n 字符串 字符串的长度不为零则为真

[cndaqiang@localhost tmp]$ test "hello" != "yes"
[cndaqiang@localhost tmp]$ echo $?
0

===都可以用于判断相同

(python37) cndaqiang@mommint:~/code/TDAP-offical$ if [ ${ibasename:0:2} -eq ._ ]; then echo he; fi
-bash: [: ._: integer expression expected
(python37) cndaqiang@mommint:~/code/TDAP-offical$ if [ ${ibasename:0:2} == ._ ]; then echo he; fi
he
(python37) cndaqiang@mommint:~/code/TDAP-offical$ if [ ${ibasename:0:2} = ._ ]; then echo he; fi
he

1.3文件测试

linux文件:目录、链接、设备、文本。。。

参数 说明
-e 文件名 如果文件存在则为真
-r 文件名 如果文件存在且可读则为真
-w 文件名 如果文件存在且可写则为真
-x 文件名 如果文件存在且可执行则为真
-s 文件名 如果文件存在且至少有一个字符则为真即文件长度大于0、非空
-d 文件名 如果文件存在且为目录则为真
-f 文件名 如果文件存在且为普通文件则为真
-L 文件名 如果文件存在且为符号连接
-c 文件名 如果文件存在且为字符型特殊文件则为真
-b 文件名 如果文件存在且为块特殊文件则为真
-u 文件名 如果文件存在且有suid位设置

ubuntu@VM-10-194-ubuntu:/tmp$ test -f ./123.txt 
ubuntu@VM-10-194-ubuntu:/tmp$ echo $?
0

1.2 []条件判断

语法

[ EXPRESSION ]

中括号的左右扩弧和EXPRESSION之间都必须有空格
空格空格
关于EXPRESSION的说明,参考如下:

ubuntu@VM-10-194-ubuntu:/tmp$ [ -f test2.sh ]
ubuntu@VM-10-194-ubuntu:/tmp$ echo $?
0
ubuntu@VM-10-194-ubuntu:/tmp$ [ -f test2.s11h ]
ubuntu@VM-10-194-ubuntu:/tmp$ echo $?
1

1.3 逻辑

基本格式(上表也有)

-a : 逻辑与,操作符两边均为真,结果为真,否则为假。

-o : 逻辑或,操作符两边一边为真,结果为真,否则为假。

! : 逻辑否,条件为假,结果为真。

[ expre ] && [ expre2 ]

[ expre ] || [ expre2 ]

#判断test2.sh是否可读写
[ -r test2.sh -a -w test2.sh ]

2 if [ ] ; then cmd; elif [ ] ; then cmd ; else cmd ; fi

if 或者 elif 后面必须有then,else直接跟命令,可以没有,最后以fi结尾

if 后面的[]判断数值大小用[ 3 -gt 2 ]不能用[ 3 > 2]

-gt仅支持整数,可通过echo 0.1 | awk -F. '{ print $1 }' 取整

在bash文件中,回车,不需要分号,单行if命令需要分号

if后面[]的判断内容可为 test、[]判断,计算式(( ))等,以下涉及判断条件同理

例1

 if [ 1 -eq 2 ] 
> then echo 1==2
> elif [ 2 -eq 3 ]
> then 2==3
> elif [ 3 -eq 4 ]
> then echo 3==4
> else echo 1=1 
> fi
#或者
if [ 1 -eq 2 ] ; then echo 1==2; elif [ 2 -eq 3 ]; then 2==3; elif [ 3 -eq 4 ]; then echo 3==4; else echo 1==1; fi
1==1

例2

#!/bin/bash
# 提示用户输入一个值
echo -n "please input a number:"
# 保存用户的输入到num中
read NUM
if [ $NUM -le 5 ] ;
then echo "$NUM<5";
else echo "$NUM>5";
#分号可有可无
fi
exit 0

例3,判断字符串相等

for i in $(ls); do what=$(tail -1 $i); EF=EF;if [ "$what" != "$EF" ] ;then echo -e "$i"; fi; done

3 case

# 格式
case 值 in
模式1)
命令1
...
;;
模式2)
命令2
...
;;
esac

#!/bin/bash
echo -n "are you femail(Y/N)"
read val
case $val in
Y|y)
echo "yes"
;;    # 注意此处有两个;
N|n)
echo "no"
;;
*)
echo "incorrect input"
;;
esac      # 注意esac结束
exit 0

4 for循环

for 变量名in列表  或者 for  ((i=1;i<7;i++)),**注意两个括号**
do
命令1
命令2...
done

$ N=(1 2 3)
$ for i in ${N[@]}; do echo $i; done
1
2
3
# 判断当前目录下的文件并输出文件名
#!/bin/bash
#将ls结果保存到变量CUR,
CUR=`ls`    #注意这里是Tab键上面的`
# 大部分Unix shell以及编程语言如Perl、PHP以及Ruby等都以成对的重音符作指令替代,意思是以
某一个指令的输出结果作为另一个指令的输入项。例如上述指令
echo $CUR
for val in $CUR
do     #do不可少
if [ -f $val ] ;
then  echo "FILE: $val" ;
fi
done   #done 不可少
exit 0
#计算10以内数的和
#!/bin/bash
sum=0
for ((i=1;i<10;i++))
do
((sum=$sum+$i))
# (( ))表示数值计算
done
echo "sum=$sum"
exit 0

5 until

循环至少运行一次 当满足条件时不再执行直接结束

until 条件
do
命令1
...
done

例;输出5以内的自然数

#!/bin/bash
val=0
until (($val==5))   #或者[ "$val" -eq "5" ] 
do
echo $val
((val++))
done
exit 0

6 while

满足条件才进行循环,最少不运行

while 命令
do
命令1
命令2
...
done

例;输出5以内的自然数

#!/bin/bash
val=0
while ((val<5))
do
echo $val
((val=$val+1))
done
exit 0

7 break 与continue

break命令允许跳出循环—–结束
continue命令跳过这个循环步—–进入下个循环

例break输出5内自然数

#!/bin/bash
val=0
while true
do
if (($val==5));
then break;
fi
echo $val
((val++))
done
exit 0

例:从0开始逐步递增到10:当数值为5时,将数值递增2;否则,输出数值

#!/bin/bash
val=0
while (($val<=10))
do
if (($val==5))   #好像没有;用<Enter>也是可以的
then ((val=$val+2))
continue
else
echo $val 
((val++))
fi
done
exit 0

函数

所有函数在使用前必须定义。这意味着必须将函数放在脚本开始部分,直至shell解释器首次发现它时,才可以使用。调用函数仅使用其函数名即可

1 定义

基本格式

function 函数名()
{
...
return 返回值;
}
  • function可有可无。但建议保留,因为保留的话看起来更加直观
  • return 返回值,如果不加,将以最后一条命令运行结果,作为返回值 例
    #!/bin/bash
    function foo()
    {
    echo "hello"
    return 5;
    }
    foo
    echo $?
    #直接编写foo函数文件,在终端中运行foo“返回值为0”,没有echo结果
    

    结果

    [cndaqiang@localhost tmp]$ ./han
    hello
    5
    

2 传递参数

$n代表输入的第n个参数

foo param1 param2 param3
#参数变量出入函数内部为@ 例
echo $@
#输出结果为
param1 param2 param3

表格内容来自Shell 传递参数

参数 说明
$? 显示上一条命令的退出状态。判断退出true为0,flase为1,函数退出结果为返回值,例
[cndaqiang@localhost tmp]$ ((1+1))
[cndaqiang@localhost tmp]$ echo $?
0
$# 传递到脚本的参数个数
$n 表示传入的第n个参数
$10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数
$* 以一个单字符串显示所有向脚本传递的参数。如"$*"用「”」括起来的情况、以”$1 $2 … $n”的形式输出所有参数。
$@ $*相同,但是使用时加引号,并在引号中返回每个参数。如$@用「”」括起来的情况、以$1 $2$n 的形式输出所有参数。
$- 显示Shell使用的当前选项,与set命令功能相同。
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的ID号
$0 当前sehll名 [cndaqiang@localhost tmp]$ echo $0 结果为bash

** $*$@ 的区别**

  • $*$@ 表示传递给函数或脚本的所有参数,不被双引号(“ “)包含时,都以”$1” “$2” … “$n” 的形式输出所有参数。
  • 但是当它们被双引号(“ “)包含时,”$*” 会将所有的参数作为一个整体,以”$1 $2 … $n“的形式输出所有参数;”$@” 会将各个参数分开,以”$1” “$2” … “$n” 的形式输出所有参数。

#!/bin/bash
for val in "$*"
do echo $val
done
echo \"
for val in "$@"
do echo $val
done
exit 0
ubuntu@VM-10-194-ubuntu:/tmp$ ./can 1 2 3 4
1 2 3 4
"
1
2
3
4

内建命令

1 内建指令查看

基本格式
type cmd
格式说明
type是命令关键字,cmd表示查看的命令;若输出builtin,则该命令是bash的内建命令。
例如:
ubuntu@VM-10-194-ubuntu:/tmp$ type echo
echo is a shell builtin
除此之外,用户也可以通过man bash或者man builtins查看bash的全部内置命令

2 常用内建命令

(01)echo
命令:echo arg
功能:在屏幕上显示出由arg指定的字串
(02)read
命令格式:read变量名表
功能:从标准输入设备读入一行,分解成若干字,赋值给bash程序内部定义的变量

read -t 5 -p “倒计时5s输入:” var

(03)shift
命令:shift [N] (N为大于0的整数;当N省略时,等价与于“shift 1”)
功能:所有的参数依次向左移动N个位置,并使用$#减少N,直到$#=0为止。
(04)alias
命令:alias name='value'
功能:别名。用name替换value,value要用单引号括住。
(05)export
命令:export变量名[=变量值]
功能:export可以把bash的变量向下带入子bash(即子bash中可以使用父bash的变量),从而让子进程继承父进程中的环境变量。但子bash不能用export把它的变量向上带入父bash。
(06)readonly
命令:readonly 变量名
功能:定义只读变量。不带任何参数的readonly命令将显示出所有只读变量。
(07)exec
命令:exec 命令参数
功能:当bash执行到exec语句时,不会去创建新的子进程,而是转去执行指定的命令,当指定的命令执行完时,该进程(也就是最初的bash)就终止了,所以bash程序中exec后面的语句将不再 
被执行。
(08)"."(点)
命令:. bash程序文件名
功能:使bash读入指定的bash程序文件并依次执行文件中的所有语句。
(09)exit
命令:exit N
功能:退出Shell程序。在exit之后可有选择地指定一个数位作为返回状态。

echo

  • echo是Shell的一个内部指令,用于在屏幕上打印出指定的字符串。命令格式
echo "anything"
"可有可无
  • 1 显示转义字符 backslash escapes
    在echo命令的帮助中,写到默认禁止反斜杠功能。如果需要开起反斜杠转义功能,需要使用-e的参数.
转义字 含义
\\ 反斜杠
\a 警报,响铃
\b 退格(删除键)
\f 换页(FF),将当前位置移到下页开头
\n 换行
\r 回车
\t 水平制表符(tab键)
\v 垂直制表符

#!/bin/bash
echo -e  "hello\c"
echo "hell\c"
echo  hell
exit 0

结果

hellohell\c
hell

可看到,需要 -e 才能启用转义字符功能 对于 “ /需要// /” /$来输出,不适用上述规则

  • 2 显示变量
    echo $val
    

    当变量与其它字符相连时用{}隔开

    echo ${val}-2-3 
    
  • 3 显示结果重定向至文件(写入另一个文件)
    [cndaqiang@localhost tmp]$ echo rete >file
    [cndaqiang@localhost tmp]$ cat file
    rete
    
  • 4 原样输出字符串 单引号
    [cndaqiang@localhost tmp]$ echo '\$cal'
    \$cal
    
  • 5 输出命令结果 重音符`
    [cndaqiang@localhost tmp]$ echo `date`
    Mon Aug 7 00:23:33 PDT 2017
    

    双引号可有可无,单引号主要用在原样输出中,重音符输出命令

  • 6 输出后不自动换行
    cndaqiang@girl:~$ echo -n "hello"; echo "world"
    helloworld
    

    printf

    printf 命令用于格式化输出, 是echo命令的增强版。它是C语言printf()库函数的一个有限的变形,并且在语法上有些不同。

    注意:printf 由 POSIX 标准所定义,移植性要比 echo 好。

格式

printf  format-string  [arguments...]
  • format-string 为格式控制字符串
  • arguments 为参数列表

[cndaqiang@localhost tmp]$ printf "%d%s%c%f\n" 1 "abc" "def" "0.1"
1abcd0.100000

%d %s %c %f 格式替代符详解:
d:data 数字 – 对应位置参数必须是数字型,否则报错!
s:str 字符串 – 对应位置参数必须是字符串或者字符型,否则报错!
c:char 字符 – 对应位置参数必须是字符串或者字符型,否则报错!
f:float 浮点 – 对应位置参数必须是数字型,否则报错!
如:上面第三个参数是 “def”,%c 自动截取字符串的第一个字符作为结果输出,若第四个参数无0.1则输出0.000000

指定输出宽度
%-10s 指一个宽度为10个字符(-表示左对齐,没有则表示右对齐)
默认转义

转义字符 含义
\\ 反斜杠
\a 警报,响铃
\b 退格(删除键)
\f 换页(FF),将当前位置移到下页开头
\n 换行
\r 回车
\t 水平制表符(tab键)
\v 垂直制表符
\c 抑制(不显示)输出结果中任何结尾的换行字符(只在%b格式指示符控制下的参数字符串中有效),而且,任何留在参数里的字符、任何接下来的参数以及任何留在格式字符串中的字符,都被忽略
\ddd 表示1到3位数八进制值的字符。仅在格式字符串中有效
\0ddd 表示1到3位的八进制值字符

与C语言printf()函数的不同

  • printf 命令不用加()
  • format-string 可以没有引号,但最好加上,单引号双引号均可,不加上运行结果偶尔异常,如
    [cndaqiang@localhost tmp]$ printf "hello\n"
    hello
    [cndaqiang@localhost tmp]$ printf hello\n
    hellon[cndaqiang@localhost tmp]$
    
  • 参数多于格式控制符(%)时,format-string 可以重用,可以将所有参数都转换,即未指定格式使用前面的格式 例
    [cndaqiang@localhost tmp]$ printf "%s\n%s%s\n" 1 2 3
    1
    23
    [cndaqiang@localhost tmp]$ printf "%s\n" 1 2 3    #格式不够使,借前面的格式
    1
    2
    3
    
  • arguments 使用空格分隔,不用逗号。 例
    [cndaqiang@localhost tmp]$ printf "%s\n" 1,2 3  #逗号不分隔,空格分隔
    1,2
    3
    

调试

1 bash命令调试

bash [-nvx] scripts.sh

选项与参数:

-n :不要执行 script,仅查询语法的问题;
-v :再执行 sccript 前,先将 scripts 的内容输出到屏幕上;
-x :将使用到的 script 内容显示到屏幕上,这是很有用的参数!
例如,想要执行bash脚本,并查看bash的调用流程,可以通过以下命令:
$ bash -x test.sh

2 echo调试

echo [OPTION] STRING
-n : 输出内容之后,不换行。默认是输入内容之后,换行。
-e : 开启反斜线“\”转义功能
-E : 关闭反斜线“\”转义功能(**默认**)。
例如,输出“please input a number:”之后不换行。
$ echo -n "please input a number:"

3 printf

和echo一样,printf也能用于输出。语法格式和C语言中printf一样。
例如,输出“hello printf”之后换行。
$ printf "hello printf\n"

更新显示内容

Shell 小技巧 – 原地更新终端输出信息 & 闪动效果 & 字体高亮 会自动更新显示的内容,也不换行输出

(python37) cndaqiang@mac ~$ for i in $(seq 1 10); do printf "\r"$i ; sleep 1; done

输入输出重定向

标准输入,标准输出,标准错误输出
执行一个shell命令行时通常会自动打开三个标准文件,即

  • 标准输入文件(stdin),即shell的输入设备(linux里设备也是文件),通常对应终端的键盘;
  • 标准输出文件(stdout)为执行命令成功时将运行的结果输出到的文件
  • 标准错误输出到文件(stderr)为执行命令报错时将错误信息输出到的文件,这两个文件都对应终端的屏幕。

/dev下可以看到 stdin stdout stderr三个文件分别为标准输入,标准输出,标准错误输出,通过下面的例子可以看到 stdin stdout stderr分别连接到不同的文件

ubuntu@VM-10-194-ubuntu:/dev$ ls -all std*
lrwxrwxrwx 1 root root 15 Aug  7 09:34 stderr -> /proc/self/fd/2
lrwxrwxrwx 1 root root 15 Aug  7 09:34 stdin -> /proc/self/fd/0
lrwxrwxrwx 1 root root 15 Aug  7 09:34 stdout -> /proc/self/fd/1

输入输出重定即: 改变输入/输出数据的的读取/输出的默认文件(键盘或终端)为其他文件(普通文件,文件描述符,文件设备),而且只能是文件

常用操作符

名称 默认文件描述符 操作符
标准输入(stdin) 0 < ,«,0<或0«
0<与<等价,其他带有文件描述符的操作符意义同理,«代表追加
标准输出(stdout) 1 >, », 1> 或 1»
标准错误输出(stderr) 2 2> 或 2»

输入重定向

输入重定向是指把命令(或可执行程序)的标准输入重定向到指定的文件中。也就是说,输入可以不来自键盘,而来自一个指定的文件。所以说,输入重定向主要用于改变一个命令的输入源,特别是改变那些需要大量输入的输入源。

格式

命令 操作符 重定向文件

示例命令

格式 说明
Command < filename Command 命令以 filename 文件作为标准输入
Command < filename > filename2 Command 信不信以 filename 文件作为标准输入,以 filename 2 作为标准输出
注: 输入(>或») 与 输出(<或«)无强制顺序 即wc >test«delim与wc «delim >test一样
Command « delimiter 从标准输入中读入,以 delimiter(任意) 为结束符,祥见示例

注: 上表格中的重定向仅在当前命令生效
例 wc 统计指定文件包含的行数、单词数和字符数

#当只输入wc时,回等待用户输入,直至 ctrl+d停止,然后统计行数、单词数和字符数
ubuntu@VM-10-194-ubuntu:/tmp$ wc
123 34
23      1       3       9
# 也可将输入重定向为某个文件
ubuntu@VM-10-194-ubuntu:/tmp$ wc < file
13 13 95
ubuntu@VM-10-194-ubuntu:/tmp$ wc <<delim
> haha
> test
> lala
> delim
 3  3 15
#或者,将结果输出到test中
ubuntu@VM-10-194-ubuntu:/tmp$ wc >test<<delim
> haha
> test
> lala
> delim
ubuntu@VM-10-194-ubuntu:/tmp$ cat test
 3  3 15

输出重定向

输出重定向是指把命令(或可执行程序)的标准输出或标准错误输出重新定向到指定文件中。这样,该命令的输出就不显示在屏幕上,而是写入到指定文件中。

输出重定向比输入重定向更常用,很多情况下都可以使用这种功能。例如,如果某个命令的输出很多,在屏幕上不能完全显示,那么将输出重定向到一个文件中,然后 再用文本编辑器打开这个文件,就可以查看输出信息;如果想保存一个命令的输出,也可以使用这种方法。

格式

命令 操作符 重定向文件

注意标准输出和标准错误输出不是”一个”文件,所以有两个输出,只改一个的话,另一个不变,同时改变需要2>file 1>file(意思是标准输出到file,标准错误输出到file),或者>file 2>&1(表示标准输出到file,标准错误输出到标准输出的文件即file)或者&>file (意思是把 标准输出 和 标准错误输出 都重定向到文件file中)
»为追加输入,即在原文件内末尾添加 示例命令

格式 说明
Command > filename 把标准输出重定向到一个新文件中
Command » filename 把标准输出重定向到一个文件(追加)
Command 2> filename 把标准错误重定向到一个文件中
Command 2» filename 把标准错误重定向到一个文件中(追加)
Command > filename 2>&1 把标准输出和错误一起重定向到一个文件中
Command » filename 2>&1 把标准输出和错误一起重定向到一个文件中(追加)

注: 上表格中的重定向仅在当前命令生效

ubuntu@VM-10-194-ubuntu:/tmp$ ls vv >file
ubuntu@VM-10-194-ubuntu:/tmp$ ls vv >>file
ubuntu@VM-10-194-ubuntu:/tmp$ cat file
vv
vv

绑定重定向

前两种重定向仅生效一次,绑定重定向即永久重定向输入输出文件,所有命令的输入输出都都来自重定向后的文件
格式

exec 操作符 重定向文件

操作符 ` < 0< «  > 1> ….` 与之前意义相同 例

# 将输出绑定重定向到/tmp/test后命令的结果不会输出到终端
ubuntu@VM-10-194-ubuntu:/tmp$ exec >/tmp/test
ubuntu@VM-10-194-ubuntu:/tmp$ ls
ubuntu@VM-10-194-ubuntu:/tmp$ ls -11
# 将输出重定向回/dev/tty(注意必须写完整路径)则终端又输出命令运行结果
ubuntu@VM-10-194-ubuntu:/tmp$ exec >/dev/tty
ubuntu@VM-10-194-ubuntu:/tmp$ ls
!  1  cal.sh  cancan  cvm_init.log  ff    net_affinity.log  sagent.pid  test  vv
0  2  can     chon    ec            file  pin               setRps.log  tty

文件描述符

文件描述符,可以理解为文件的一个数字标记

  • &0代表标准输入文件的标记为0
  • &1 代表标准输出文件标记为1
  • &2代表标准错误输出
  • &-代表关闭与它绑定的描述符

&n 代表是已经存在的文件描述符,在重定向时与文件地位相同,即重定向的文件也可以用文件描述符来表示

格式

#指定m描述的文件
命令 m>file
#一般为一次性重定向,绑定重定向命令为exec,
# file也可以为&n,这样m->n->n描述的文件

示例命令

格式 说明
Command >&n 把标准输出重定向到文件描述符 n 中,如 ls >&1 ,若要实现绑定重定向则Command为exec如 exec >&n
Command m>&n 把往文件描述符 m 的输出重定向到文件描述符 n 上,2>&1,若要实现绑定重定向则 exec m>&n
Command <&- 关闭标准输入同样绑定则Command为exec
Command 2>&- 关闭标准错误输出,和 2>/dev/null 有类似功效,同样绑定则Command为exec
Command n>&- 关闭输出文件描述符n,和 2>/dev/null 有类似功效,同样绑定则Command为exec

ubuntu@VM-10-194-ubuntu:/tmp$ exec 6>&1
#原理: 6 -> 1 -> stdout -> /dev/tty
ubuntu@VM-10-194-ubuntu:/tmp$ exec 1>test.txt
#原理: 1->test.txt 同时 stdout -> test.txt
ubuntu@VM-10-194-ubuntu:/tmp$ ls
#原理: ls结果输出到/tmp/test.txt了
ubuntu@VM-10-194-ubuntu:/tmp$ exec 1>&6
#原理: 1-> 6 -> /dev/tty 同时 stdout -> /dev/tty,也可直接exec 1>/dev/tty或者 exec 1>&2
ubuntu@VM-10-194-ubuntu:/tmp$ ls
cvm_init.log  net_affinity.log  sagent.pid  setRps.log  test.txt
#原理: ls结果输出到/dev/tty即终端
ubuntu@VM-10-194-ubuntu:/tmp$ ls /dev/fd
0  1  2  3  6
#添加文件描述符后在/dev/fd里可以看到
ubuntu@VM-10-194-ubuntu:/tmp$ exec 6>&-
#
ubuntu@VM-10-194-ubuntu:/tmp$ ls /dev/fd
0  1  2  3

备注

1. > < n>等操作符可以同时出现在一条命令里,而且无顺序要求,如

ubuntu@VM-10-194-ubuntu:/tmp$ exec 1>test 2>/dev/tty

2. 命令行前面这一条ubuntu@VM-10-194-ubuntu:/tmp$以及输入的命令在键盘上的显示好像属于标准错误输出

3. 命令 < 文件 中的命令应该能接受标准输入 ,命令 > 文件中的命令应该有标准输出

4. 在类Unix系统中,/dev/null,或称空设备,是一个特殊的设备文件,它丢弃一切写入其中的数据(但报告写入操作成功),读取它则会立即得到一个EOF,可以重定向输出到/dev/null

其他传递

管道

将一个程序或命令的输出作为另一个程序或命令的输入,有两种方法,一种是通过一个临时文件将两个命令或程序结合在一起,例如

ubuntu@VM-10-194-ubuntu:/tmp$ ls > test
ubuntu@VM-10-194-ubuntu:/tmp$ cat test
cvm_init.log
net_affinity.log
sagent.pid
setRps.log
test

另一种是Linux所提供的管道功能。

ubuntu@VM-10-194-ubuntu:/tmp$ ls | cat
cvm_init.log
net_affinity.log
sagent.pid
setRps.log
test

管道工作流程


command1正确输出,作为command2的输入 然后comand2的输出作为,comand3的输入 ,comand3输出就会直接显示在屏幕上面了。
通过管道之后:comand1,comand2的正确输出不显示在屏幕上面
注意:

  1. 管道命令只处理前一个命令正确输出,不处理错误输出
  2. 管道命令右边命令,必须能够接收标准输入流命令才行

管道与重定向的区别

  1. 管道
    左边的命令应该有标准输出 | 右边的命令应该接受标准输入
    重定向
    左边的命令应该有标准输出 > 右边只能是文件
    左边的命令应该需要标准输入 < 右边只能是文件
  2. 管道触发两个子进程执行|两边的程序;而重定向是在一个进程内执行
    • 重定向的优先级大于管道的优先级


来源:linux shell 管道命令(pipe)使用及与shell重定向区别
例中的grep为正则表达时搜索,以及sed,awk命令以后有时间,继续学习做笔记

#可以相互转换情况
#输入重定向
 
[chengmo@centos5 shell]$ cat test.sh| grep -n 'echo'
5:    echo "very good!";
7:    echo "good!";
9:    echo "pass!";
11:    echo "no pass!";
#"|"管道两边都必须是shell命令
 
 
[chengmo@centos5 shell]$ grep -n 'echo' <test.sh    
5:    echo "very good!";
7:    echo "good!";
9:    echo "pass!";
11:    echo "no pass!";
#"重定向"符号,右边只能是文件(普通文件,文件描述符,文件设备)
 
 
[chengmo@centos5 shell]$ mail -s 'test' 8292669@qq.com <test.sh
[chengmo@centos5 shell]$ cat test.sh|mail -s 'test' 8292669@qq.com
#以上2个也相同,将test.sh内容发送到指定邮箱。
 
 
[chengmo@centos5 shell]$ (sed -n '1,$p'|grep -n 'echo')<test.sh 
5:    echo "very good!";
7:    echo "good!";
9:    echo "pass!";
11:    echo "no pass!";
#这个脚本比较有意思了。由于前面是管道,后面需要把test.sh内容重定向到 sed ,然后sed输出通过管道,输入给grep.需要将前面用"()"运算符括起来。在单括号内的命令,可以把它们看作一个象一个命令样。如果不加括号test.sh就是grep 的输入了。
 
 
#上面一个等同于这个
[chengmo@centos5 shell]$ sed -n '1,$p'<test.sh | grep -n 'echo'
5:    echo "very good!";
7:    echo "good!";
9:    echo "pass!";
11:    echo "no pass!";
 
#重定向运算符,在shell命令解析前,首先检查的(一个命令,执行前一定检查好它的输入,输出,也就是0,1,2 设备是否准备好),所以优先级会最高
 
 
[chengmo@centos5 shell]$ sed -n '1,10p'<test.sh | grep -n 'echo' <testsh.sh
10:echo $total;
18:echo $total;
21:     echo "ok";
#哈哈,这个grep又接受管道输入,又有testsh.sh输入,那是不是2个都接收呢。刚才说了"<"运算符会优先,管道还没有发送数据前,grep绑定了testsh.sh输入,这样sed命令输出就被抛弃了。这里一定要小心使用
 
#输出重定向
 
[chengmo@centos5 shell]$ cat test.sh>test.txt
[chengmo@centos5 shell] cat test.sh|tee test.txt &>/dev/null
#通过管道实现将结果存入文件,还需要借助命令tee,它会把管道过来标准输入写入文件test.txt ,然后将标准输入复制到标准输出(stdout),所以重定向到/dev/null 不显示输出
#">"输出重定向,往往在命令最右边,接收左边命令的,输出结果,重定向到指定文件。也可以用到命令中间。
 
 
[chengmo@centos5 shell]$ ls test.sh test1.sh testsh.sh 2>err.txt | grep 'test'
test.sh
testsh.sh
#目录下面有:test,testsh文件,test1.sh不存在,因此将ls 命令错误输出输入到err.txt 正确输出,还会通过管道发送到grep命令。
[chengmo@centos5 shell]$ ls test.sh test1.sh testsh.sh &>err.txt | grep 'test'
#这次打印结果是空,&代表正确与错误输出 都输入给err.txt,通过管道继续往下面传递数据为空,所以没有什么显示的
 
#同样">"输出重定向符,优先级也是先解析,当一个命令有这个字符,它就会与左边命令标准输出绑定。准备好了这些,就等待命令执行输出数据,它就开始接收

命令替换(重音符`)

命令替换和重定向有些相似,但区别在于命令替换是将一个命令的输出作为另外一个命令的参数

重定向必须为一个文件,而重音符``是将命令的结果作为参数传递例如

ubuntu@VM-10-194-ubuntu:/tmp$ echo 192.168.1.1 > file
# ping需要参数,不是文件,即不能接受标准输入,可以接受重音符运行的参数,不能接受重定向的输入文件
ubuntu@VM-10-194-ubuntu:/tmp$ ping `cat file`
PING 192.168.1.1 (192.168.1.1) 56(84) bytes of data.
^Z
[8]+  Stopped                 ping `cat file`
ubuntu@VM-10-194-ubuntu:/tmp$ echo 192.168.1.1 >file
ubuntu@VM-10-194-ubuntu:/tmp$ ping < file
Usage: ping [-aAbBdDfhLnOqrRUvV]...省略
##cat 可以接收文件,也可以接受参数,即可以接受标准输入
ubuntu@VM-10-194-ubuntu:/tmp$ cat file
192.168.1.1
ubuntu@VM-10-194-ubuntu:/tmp$ cat < file
192.168.1.1
ubuntu@VM-10-194-ubuntu:/tmp$ cat `echo file`
192.168.1.1

有趣

多线程 & wait

无关联多进程

参考该链接已失效

#!/bin/bash
##################################
# Author : cndaqiang             #
# Update : 2019-09-02            #
# Build  : 2019-04-11            #
# What   : 并行:从代码中找特定字符函数/变量  #
##################################
for i in $(ls)
do
        grep -rni $1 $i &                                                                                
done
#这里可以添加一个实时检测后台剩余进度的命令
while [ $(jobs | wc -l) -gt 0 ]; do printf "\r"$(jobs | wc -l); sleep 1; printf "\r"$(jobs | wc -l).; sleep 1; printf "\r"$(jobs | wc -l)..; sleep 1; printf "\r"$(jobs | wc -l)...; done
wait

阻塞多进程

./sleep.sh 10 &
echo --10--
./sleep.sh 8 &
echo --8--
./sleep.sh 2 &
echo --2--
wait
echo over

也可以等待具体的pid,并判断是否有错误 注意&仅对前面的一个语句有作用,A;B&即执行完A后,再在后台执行B

./sleep.sh 10 &
echo --10--
pids="$pids $!"
./sleep.sh 8 &
echo --8--
pids="$pids $!"
./sleep.sh 2 &
echo --2--
pids="$pids $!"

for pid in $pids;do
        wait $pid || let "RES=1"
done

echo over
if [[ "$RES" -ne 0 ]]; then
        exit $RES
fi

CPU split的多线程wannier脚本,适合同时运行多个程序

sleep 1 &
pid0=$!
pid1=${pid0}
pid2=${pid0}

while [ 1 ]
do
if [ ! $( ps  | awk '{print $1}' | grep ^"${pid0}"$ ) ]
then
    echo sleep 4
    sleep 4 &
    pid0=$!
fi

if [ ! $( ps  | awk '{print $1}' | grep ^"${pid1}"$ ) ]
then
    echo sleep 1
    sleep 1 &
    pid1=$!
fi
done

结果

cndaqiang@mac tmp$ bash test.sh
sleep 4
sleep 1
sleep 1
sleep 1
sleep 1

判断字符串是否是数字

Shell中判断字符串是否为数字的6种方法分享

  • expr只支持整数计算

expr $1 "+" 10 &> /dev/null
if [ $? -eq 0 ];then
  echo "$1 is number"
else
  echo "$1 not number"
fi

去重

先排序再去重

ls simulation.pos__* | awk -Fbond  '{ print $2 }' | sort | uniq

多行结果变成同一行

linux shell 将多行文件转换为一行

ls | awk BEGIN{RS=EOF}'{gsub(/\n/," ");print}'
#或ls | xargs

删除文件名中的空格

``` #for file in *获得的file是包含空格的, for i in $(ls) 是不包含空格的 for file in *; do mv “$file” $file ; done #以及 rename ‘s/ //g’ *


本文首发于我的博客@cndaqiang.
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!


目录

访客数据