这篇文章之前写在简书上的,简书和这个博客的md语法有些差异,调节起来太麻烦了,以后复习bash时再重新整理
参考
Linux bash总结(一) 基础部分(适合初学者学习和非初学者参考)
Linux 输入输出重定向, &>file, 2>&1, 1>&2 等
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++
;而expr
和bc
是系统命令,会消耗内存,执行效率低。- 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的正确输出不显示在屏幕上面
注意:
- 管道命令只处理前一个命令正确输出,不处理错误输出
- 管道命令右边命令,必须能够接收标准输入流命令才行
管道与重定向的区别
- 管道
左边的命令应该有标准输出 | 右边的命令应该接受标准输入
重定向
左边的命令应该有标准输出 > 右边只能是文件
左边的命令应该需要标准输入 < 右边只能是文件- 管道触发两个子进程执行|两边的程序;而重定向是在一个进程内执行
- 重定向的优先级大于管道的优先级
例
来源: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
判断字符串是否是数字
- 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
多行结果变成同一行
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 协议 ,转载请注明出处!