Shell 是一种命令行解释器,它接受用户输入的命令,并将其转换为操作系统可以执行的指令。它不仅仅是一个简单的命令行界面,还是一个功能强大的脚本编程环境,可以编写自动化任务和复杂的工作流程。主流的shell 环境有 Bash Csh Tcsh , makefile中默认使用的是bash shell。我们可以通过 输入 echo $SHELL查看当前shell类型,使用chsh -s 新shell 路径 更改用户登录的默认shell。
强列建议所有的变量都要加上“”双引号,见详细介绍连接。 防止空值导致错误,或者包含空格以及其他特殊字符的字符串;比如[ $a = “HLL” ] 如果 $a 为空或者未定义,则出现[ = "HLL"] 左边是空的语法错误;当处理字符串 HH HLL时, 会被shell 解释器理解为[ HH HLL="HLL"] 同样出现了语法错误。
1. 变量的定义与赋值
标量变量
变量可以分为普通变量,环境变量,变量名称可以由字母、数字和下划线组成,但不能以数字开头。BASH 和CSH定义变量方式有些许不同这里分别介绍;
BASH shell
普通变量:
a=10, 但是注意“=”左右一定不可以有空格;
set 显示系统中已经存在的shell变量,以及设置的新变量值
环境变量:
export MY_VAR=value
env 在BASH下打印所有环境命令。
CSH shell
普通变量:
set a=10 TCSH SHELL
set 会打印所有的通变量
环境变量:
setenv MY_VAR value TCSH
printenv env 在TCSH下打印所有环境命令。
- 变量的引用
使用变量的值只需要在前面增加 "$" 符号就可以了,这里需要注意如果 使用 “ " ” 双引号会保持变量的引用(会用变量的值替换), 如果使用 “ ' ” 单引号则不会对其中的变量进行替换,保持原样输出。
my_var =10
echo $my_var
当在一个字符串替换变量时候,为了避免模糊或混淆, 需要将{} 限定变量的位置。
echo "${my_var}is niupu" 输出10is niupu
如果使用 ''
echo '${my_var}is niupu' 输出 ${my_var}is niupu
- 变量替换
“变量置换(variable substitution)”是指将变量名替换为其对应值的过程,“变量置换(variable substitution)”是指将变量名替换为其对应值的过程。我们可以通过变量置换将一段字符串以命令的方式运行并获取其返回值。
- BASH 字符串内容的替换
通过 $( cmd ) 或者 ` cmd ` 获取c命令的输出并用于后续操作,a=$(date +%D_%T), 当变量名后接其他字符时,需要使用${var}将变量隔开。
${var//hello/HELLO} 将var变量中hello字段替换为HELLO (所有)
${var/hello/HELLO} 将var变量中hello字段替换为HELLO (仅替换一次)
注意var变量和"/"不允许出现空格,否则会语法错误;此外替换的新字符串与"}"之间的空格会认为是
新字符串的一部分。
- CSHELL 字符串内容替换
只能通过 `` 完成命令的替换, 捕获命令的输出并用于后续操作,当百年来后接其他字符时,需要使用需要使用${var}将变量隔开。
set sr_arr="hello world"
set dt_arr=($sr_arr:as/o/#/) 将o字符替换为#字符
note 注意如果源字符串包含/字符,可以使用其他字符比如%替换/分隔符
数组变量
在某些 Shell(如 Bash)中,还支持数组变量的定义和操作,使得能够更方便地处理多个数据项。
对于普通·数组可以通过如下方式定义:
普通数组 : declare -a ARR1 或者 ARR=(10,20,"apple",“banana")
通过${ARR1[0]} 即可访问其中的变量。
对于关联数组可以通过如下方式定义:
关联数组 : declare -A ARR2 它是一种类似于字典的数据形式;
通过${ARR2[key]}来找到与之”键key“对应的value; ${ARR2{@}}获取所有值。
FRUITS=("Apple" "Orange" "Banana" "Grapes")
echo ${FRUITS[0]} # 输出数组 FRUITS 的第一个元素 "Apple"
echo ${VEGETABLES[2]} # 输出数组 VEGETABLES 的第三个元素 "Tomato"
##获取所有元素
echo ${FRUIT[@}}
echo ${FRUIT[*}}
##获取数组长度
echo ${#FRUIT[@}}
echo ${#FRUIT[*}}
# 使用 for 循环遍历 FRUITS 数组
for fruit in "${FRUITS[@]}"
do
echo "Fruit: $fruit"
done
shell 内建变量
- $0:脚本本身的名称。
- $1, $2, …, $n:分别是传递给脚本的第一个、第二个直到第 n 个参数。
- $@:表示所有的参数列表,每个参数被视为一个单独的字符串。
- $#:传递给脚本的参数个数
- $? 在bash中表示上个指令的返回值 0:成功, 1:失败;在cshell中表示变量是否存在。
2. shell 基本运算符
在 Shell 编程中,使用基本运算符可以进行数学运算、逻辑判断和字符串处理等操作。本文将介绍 Shell 中常用的基本运算符及其使用方法。
算数运算符 | + - * / |
逻辑运算符 | -eq 等于 -ne 不等于 -gt 大于 -lt 小于 -ge 大于等于 -le 小于等于 |
字符串运算符 | = 字符串相等 != 字符串不相等 -z 字符串为空 -n 字符串非空 |
与或非 | ! || && 或者 or and |
四则运算表达式 | BASH: $[1+3] or $(( 1 + 2 ))or let a=a+1 CSH : @ a=$a + 1 or $(1+2) 注意运算符之间左右都有空格, 注意@只支持整数运算, |
Shell test命令 for bash
在 Shell 脚本编程中,test
命令是一个非常重要的工具,用于检查文件类型和比较值。本文将介绍 test
命令的基本用法、常见选项以及一些实际应用场景,它通常会与if 搭配一起使用。
test 命令的基本用法:
1. test 条件
if test -d file
2. 使用方括号 [] (测试表达式) 表示test 命令。
if [ -d file ]
[ -d sim ] && ( cmd1;cmd2 ) 其中cmd1 和cmd2 分别在不同自shell中进行。
[ -d sim ] && { cmd1; cmd2;} 其中cmd1 和cmnd2实在最开始创建的同一个子shell中运行。
注意点:
1. 在使用 [ ]
时,操作符 -d 和参数之间需要有空格,否则会出现语法错误; 内部的与或非需要使用 -a -o !。
2. 字符串比较注意引号:为了避免字符串比较时出现意外的行为,建议使用双引号将变量括起来。
3. [[ ]] 支持模式匹配和正则表达式,更加灵活。内部的比较就可以使用 > , && , ==(字符串)直接进行比较。
- bc 命令使用
bc支持多种数学函数,包括但不限于平方根、指数、对数等。为了使用这些功能,需要在启动 bc
时加入 -l
选项,这将载入数学库。
精度转换: echo "scale=2;1.233" | bc -l 输出1.23
进制转换: echo "obase=16;ibase=10;12 | bc 输出 C
拓展: 如果想要在shell环境中执行多个命令可以使用 {} 或者 () 运算符。{}代码块会在当前shell执行,{ cmd1;cmd2;cmd3 } , "{" 和命令之间需要又空格, "}"之前需要回车或者分号, 命令之间使用 ”;"分隔, 它可以共享当前shell中设置的变量; (cmd1;cmd2;cmd3) 代码块会在子shell中执行,不能共享当前shell中的变量。
对比 ( )
会创建子Shell
3. 分支结构
条件判断在 Shell 脚本中通过 if...else
结构实现,用来根据条件的真假来执行不同的命令或代码块。csh 的condtion 与 then之间不存在 ” ;", bash中的"elif" 在csh中通过”else if"实现。
- BASH 分支语句
单条件判断:
if condition1 ; then
expression1
fi
多条件判断:
if condition1 ; then
expression1
elif condition2
expression2
fi
case分支语句
case $var in
"apple") ;;
"banana") ;;
*) ;;
esac
;; 指示当前分支执行结束后,不再执行后续分支。
- Csh 分支结构
if (condition) then
expression
else if (condition2) then
expression2
endif
类似c语言 switch case 结构
switch ()
case A : cmd breaksw
case B : cmd breaksw
default: cmd breeaksw
endsw
while ($# > 0)
switch
case "hello" :
echo "hello"
breaksw
case "...." :
echo "....."
breaksw
default :
echo "this is default"
breaksw
endsw
end
4. 循环结构
Shell 脚本中的循环结构包括 for
、while
和 until
循环,用于重复执行一段代码,直到条件不再满足为止。
- BASH 循环结构
1. for循环
for i in "$*" ; do expressions 表达式中可以存在continue 跳回下一次循环, break跳出本次循环。 done for (( i=0; i<10; i++)) ; do ## C语言风格循环 done 拓展:对于$*当不用“”引起来时,它时将命令行参数用数组形式存储;否则表示一个命令行参数组成的字符串(含有空格)。
2.while 循环
count=1 while [ $count -le 5 ] do echo "Count is $count" ((count++)) done
用例通过while 读取文本文件: 这里的IFS用于定义文件读取时以哪种分隔符进行分隔 while IFS='seq' read -r line ; do expression done < "src.txt" 在makefile中注意循环结构中都补充;
-
Csh函数
foeach item (数组)
end
while (condition)
end
5. shell函数
在 Shell 编程中,函数是一种重要的组织代码的方式,它可以帮助我们将复杂的任务分解为更小的模块,并且提高代码的重用性和可维护性。本文将介绍 Shell 函数的基本语法、参数传递、返回值处理以及一些实际应用场景。
shell 定义函数基本语法:
function_name() {
# 函数体(命令序列)
}
或者
function_name {
# 函数体(命令序列)
}
使用:
function_name argv1 argv2
6. record
- source为再当前shell 执行脚本, bash 和 ../是在新开的子shell中运行。详解shell中source、sh、 bash、./执行脚本的区别_ITPUB博客.
- 字符串的切片 ,{1..0} 这种用法目前只有bash 环境支持,csh tcsh是不支持的。
string="Hello, World!"
# 正确的参数扩展
substring=${string:7:5}
echo $substring # 输出 "World"
# 错误示例:offset不是数字
# substring=${string:word:5} # 这将报错
- 查看当前目录下一级层次内所有的.v文件,并执行相应的命令
find ./ -name "*.v" -exec cmd {} \;
- shell获取当前执行文件路径的表达式:(放在文件中使用)
$(cd $(dirname ${BASH_SOURCE[0]}) && pwd ) #仅仅bash shell支持
- 不同硬盘间的快速赋值,可以使用exclude 排除其内部的目录,原始目录后不加 “ /"
表示仅仅将其下的文件而不是整个目录赋值过来。
rsync -avzp - /原始目录/ /path/
rsync -avzp --exclude="a" /原始目录/ /path/ 排除a目录
- 查看限定层级下磁盘使用情况
du -h --max-depth==1 ./ # max-depth 决定展示的层级深度
- 使用find 获取当前目录下特定的文件的绝对路径,
find ./ -name "*.v" -print0 | xargs -0 readlink -f >> a.fl
这里使用了 -print0 选项来确保 find 命令输出的路径名以 null 字符(\0)分隔,而不是换行符。xargs -0 选项告诉 xargs 以 null 字符作为输入项的分隔符。这样做可以正确处理包含空格、换行符等特殊字符的文件名
- csh 中插入bash代码段
#!usr/bin/csh
xxx
xxx
#bash code field
bash <<-'EOF"
xxx
xxx
'EOF'