Mac端 Shell基础

495 阅读2分钟

一、运行环境

H2O

y=x2

通过test.sh文件执行

# **** 以下Shell代码

#!/bin/bash
# 此参数可以在运行时设置
set -xv # x:在执行命令前打印命令及其参数,v:在读取输入行之后打印它们

echo "hello shell"

# **** 以上Shell代码

# 可以用以下方式运行脚本
方式1:bash test.sh # 运行脚本设置参数:bash -xv test.sh
方式2:sh test.sh # # 运行脚本设置参数:sh -xv test.sh
方式3:./test.sh

终端

# 方式一:直接在终端输入如下代码
echo "hello shell"

# 方式二:bash子进程执行
终端输入bash 回车,然后输入代码

变量

变量命名规则:跟其编程语言一样,变量名只能用字母或下划线开头,如下
#!/bin/bash
# 变量申明和设置,
name="noob"
_name="rookie"
firstName="eric"
lastName="zhou"
fullName=$firstName$lastName # 中间不能有空格
fullName="$firstName $lastName" # 有空格需要双引号包裹起来
func="fanyi."
url="https://${fanyi}baidu.com" # 变量和其他字母连续,使用${}

# 错误示范1:0name="lily"
# 错误示范2:name= "lilei" # 注意“=”前后不能有空格

# 删除变量
unset name

# 环境变量
export name=18 # 可以理解为全局变量

# 交互式变量
read name
read -p "请输入姓名" name

# 单双引号
# 示例:
url="http://baidu.com"
website1='单引号字符串:${url}'
website2="双引号字符串:${url}"
echo "$website1"
echo "$website2"
:<<EOF

以上代码运行结果如下:
单引号字符串:${url}
双引号字符串:http://baidu.com

总结:
以单引号`' '`包围变量的值时,单引号里面是什么就输出什么,即使内容中有变量和命令(命令需要反引起来)也会把它们原样输出。这种方式比较适合定义显示纯字符串的情况,即不希望解析变量、命令等的场景。  
  
以双引号`" "`包围变量的值时,输出时会先解析里面的变量和命令,而不是把双引号中的变量名和命令原样输出。这种方式比较适合字符串中附带有变量和命令并且想将其解析后再输出的变量定义
EOF

# ************ set拓展 ************
# **语法:** set [+-abCdefhHklmnpPtuvx]
:<<EOF
**参数说明**:

● -a  标示已修改的变量,以供输出至环境变量。
● -b  使被中止的后台程序立刻回报执行状态。
● -C  转向所产生的文件无法覆盖已存在的文件。
● -d  Shell预设会用杂凑表记忆使用过的指令,以加速指令的执行。使用-d参数可取消。
● -e  若指令传回值不等于0,则立即退出shell。
● -f   取消使用通配符。
● -h  自动记录函数的所在位置。
● -H Shell  可利用"!"加<指令编号>的方式来执行history中记录的指令。
● -k  指令所给的参数都会被视为此指令的环境变量。
● -l  记录for循环的变量名称。
● -m  使用监视模式。
● -n  只读取指令,而不实际执行。
● -p  启动优先顺序模式。
● -P  启动-P参数后,执行指令时,会以实际的文件或目录来取代符号连接。
● -t  执行完随后的指令,即退出shell。
● -u  当执行时使用到未定义过的变量,则显示错误信息。
● -v  显示shell所读取的输入值。
● -x  执行指令后,会先显示该指令及所下的参数。
● +<参数>  取消某个set曾启动的参数。
EOF

# 常用命令
# 没有参数:查看所以变量
set

# 查看包含“GOBIN”的结果
set | grep GOBIN

#打开调试模式,详细模式,遇见错误退出
set -xve

#关闭调试模式,详细模式,遇见错误退出
set +xve
小练习
#!/bin/bash
# 查看某个IP是否是通的
ip="39.156.66.10" # 通过ping baidu.com获取的IP
ping -c1 39.156.66.10 &>/dev/null && echo "alive" || echo "offline"
# 命令解读
# ping:-   这是一个用于测试网络连接的命令,它可以发送ICMP(Internet Control Message Protocol)回声请求到指定的主机,并等待回声应答。
# -c1:发送一次,c后面的代码发送次数
# &>:同时重定向标准输出(stdout)和标准错误(stderr)到指定的位置
# dev/null:这是一个特殊的设备文件,代表一个空设备。任何重定向到`/dev/null`的数据都会被丢弃,相当于一个黑洞。
# && ||:实现类似三元表达式,如果IP能通,输出:alive,否则输出:offline

# ************ 重定向拓展 ************
# >  重定向
:<<EOF
  例如:
  command 2> stdout.txt# 重定向标准错误到文件
  command 1> stderr.txt# 重定向输出到文件
  command &> file.txt# 重定向输出到文件(&>/dev/null表示将标准输出和错误输出丢弃)
EOF


# >> 追加
:<<EOF
  例如:
  command 2>> stderr.txt  # 追加标准错误到文件
EOF
# 


:<<EOF
故事会开场白:万里边关千重险,一方水土养百样人,重洋远渡梦依旧,长城内外是故乡!

对暗号
“先生是赣州人?”
“不,我是江西于都人。”
“于都,我去过,那是十二年前,于都有个南屏茶叶铺,掌柜的姓马。”
“恐怕你那是老黄历了,马掌柜的盘了茶叶铺,如今的掌柜姓金,专售大红袍。”
EOF
# 

二、整数运算

:<<EOF
运算符:+、-、*、/、%、^、-
EOF

read -p "请输入语文分数:" num1 # 不接收浮点数,只接收整数
read -p "请输入数学分数:" num2
read -p "请输入英语分数:" num3
# 加-n表示不自动换行
echo -n "方式一【expr 表达式】总分数:"
expr $num1 + $num2 + $num3

# 方式二、三:$(())
echo "方式二【$ + 双括号】总分数:$(($num1 + $num2 + $num3))"
echo "方式三【$ + 方括号】总分数:$[$num1 + $num2 + $num3]"

# let后面的表达式不能有空格,不然会报错:
# let: +: syntax error: operand expected (error token is "+")
let sum=$num1+$num2+$num3; echo "方式四【let】总分数:$sum"
let i++; echo "i=$i"

#startTime=$(date +%s.%3N)  # 获取时间戳(秒+毫秒)MacOS不支持.%3N
#startTime=$(date +"%Y%m%d%H%M%S") # 年月日时间戳
startTime=$(date +%s) # 时间戳(兼容所有系统)
endTime=$(date +%s)
packageTime=$(( endTime - startTime ))
packageTime1=`expr $endTime - $startTime`
packageTime2=$(expr $endTime - $startTime)
echo "packageTime=$packageTime, packageTime1=$packageTime1, packageTime2=$packageTime2"

三、小数运算

# 使用bc运算小数,scale=2指保留2位小数

echo "scale=2; 1.111 + 10/3" | bc #这里的结果是:4.441,并不是预期的4.44
# ************************ 此处插眼,以后弄明白再补充 ************************

四、条件比对

:<<EOF
==:-eq [equal],如:if [ $a -eq $b ]
!=:-ne [non equal]
> :-gt [greater than]
>=:-ge [greater than or equal]
< :-lt [less than]
<=:-le [less than or equal]
EOF
示例:
[ 1 -eq 2 ]; echo $? # 输出0表示为真,非0为假
((1 -eq 2)); echo $?

五、文件测试

:<<EOF
-e 文件是否存在 exist
-d 目录是否存在 directory
-f 常规文件
-r 文件是否可读
-w 文件是否可写
-x 文件是否可有执行
-s 文件长度是否为0,即文件不能未空
-h 文件是否是软链接【软链不是MacOS里的替身,替身是个文件类型,隐藏文件和$HOME路径下的基本都是软链接】
EOF

六、字符串

:<<EOF
字符串长度:#变量,如name="lilei"; echo ${#name},输出结果为5
=   等于
!=  不等于
-z  判断字符串长度为0返回true
-n  判断字符串长度不为0返回true
$   字符串是否为空
EOF

示例:
[ "yes" = "yees" ]; echo $?

# 字符串包含判断
str="hello world"
[[ $str =~ "llo" ]] && echo "contain" || echo "not contain" # 输出结果:contain
# 取非
[[ ! $str =~ "llo" ]] && echo "contain" || echo "not contain" # 输出结果:not contain

# ************************ 字符串大小写 ************************
echo ${test1^}    # 每个单词首字母转大写
echo ${test1^^}   # 全部转大写
echo ${test2,}    # 首字母转小写   
echo ${test2,,}   # 全部转小写
# 以上Linux环境中有效,MacOS上都报:bad substitution的错误

# 大小写转换
str="hello world"
echo "${(C)str}" # 每个单词首字母大写
str="HELLO WORLD"
echo "${(L)str}" # 所有字母转小写
echo "$str" | tr '[:lower:]' '[:upper:]' # 所有字母转大写
echo "$str" | tr '[:upper:]' '[:lower:]' # 所有字母转小写

# ************************ 字符串截取 ************************
url="c.biancheng.net"
# 1:${string: start :length}
# string 是要截取的字符串,start 是起始位置(从左边开始,从 0 开始计数),length 是要截取的长度(省略的话表示直到字符串的末尾)
echo ${url: 2: 9} # 结果为:biancheng

# 2:${string: start} 
echo ${url: 2} # 省略length,截取到字符串末尾,结果为:biancheng.net

# 3:${string: 0-start :length} 
# 从右边开始计数
echo ${url: 0-13: 9} # 结果为`biancheng`。从右边数,`b`是第 13 个字符
# 这里需要强调两点:
# 3.1:从左边开始计数时,起始数字是 0(这符合程序员思维);从右边开始计数时,起始数字是 1(这符合常人思维)。计数方向不同,起始数字也不同
# 3.2.:不管从哪边开始计数,截取方向都是从左到右

# 4:${string: 0-start}
echo ${url: 0-13} #省略 length,直接截取到字符串末尾,结果为`biancheng.net`

# 5:${string#*chars}
# 使用`#`号可以截取指定字符(或者子字符串)右边的所有字符,其中,string 表示要截取的字符,chars 是指定的字符(或者子字符串),`*`是通配符的一种,表示任意长度的字符串。`*chars`连起来使用的意思是:忽略左边的所有字符,直到遇见 chars(chars 不会被截取)。
url="http://c.biancheng.net/index.html"
echo ${url#*:} # 结果为:`//c.biancheng.net/index.html`

# 以下写法也可以得到同样的结果:
echo ${url#*p:}
echo ${url#*ttp:}

# 有多个`/`匹配
echo ${url#*/} # 结果为`/c.biancheng.net/index.html`。url 字符串中有三个`/`,输出结果表明,Shell 遇到第一个`/`就匹配结束了

# 6:${string##*chars}
# 如果希望直到最后一个指定字符(子字符串)再匹配结束,那么可以使用`##`
echo ${url#*/} # 结果为 /c.biancheng.net/index.html
echo ${url##*/} # 结果为 index.html
str="---aa+++aa@@@"
echo ${str#*aa} # 结果为 +++aa@@@
echo ${str##*aa} # 结果为 @@@

# 7:${string%*chars}
# 8:${string%%*chars}
# 使用`%`号可以第一个截取指定字符(或者子字符串)左边的所有字符。`%%`从左边最后一个匹配开始截取
# 请注意`*`的位置,因为要截取 chars 左边的字符,而忽略 chars 右边的字符,所以`*`应该位于 chars 的右侧。其他方面`%`和`#`的用法相同,这里不再赘述。
url="http://c.biancheng.net/index.html"
echo ${url%/*} #结果为 http://c.biancheng.net
echo ${url%%/*} #结果为 http:
str="---aa+++aa@@@"
echo ${str%aa*} #结果为 ---aa+++
echo ${str%%aa*} #结果为 ---

Shell字符串截取

格式说明
1、${string: start :length}从 string 字符串的左边第 start 个字符开始,向右截取 length 个字符。
2、 ${string: start}从 string 字符串的左边第 start 个字符开始截取,直到最后。
3、${string: 0-start :length}从 string 字符串的右边第 start 个字符开始,向右截取 length 个字符。
4、${string: 0-start}从 string 字符串的右边第 start 个字符开始截取,直到最后。
5、${string#*chars}从 string 字符串第一次出现 *chars 的位置开始,截取 *chars 右边的所有字符。
6、${string##*chars}从 string 字符串最后一次出现 *chars 的位置开始,截取 *chars 右边的所有字符。
7、${string%*chars}从 string 字符串第一次出现 *chars 的位置开始,截取 *chars 左边的所有字符。
8、${string%%*chars}从 string 字符串最后一次出现 *chars 的位置开始,截取 *chars 左边的所有字符。

七、条件测试

# if语法
:<<EOF
  # 单分支
  if [];then
    #条件为 真 执行此处代码
  fi

  # 双分支
  if [];then
    #条件为 真 执行此处代码
  else 
    #条件为 假 执行此处代码
  fi

  # 多分支
  if [];then
    #条件为 真 执行此处代码
  elif [ ]; then
    #条件为 真 执行此处代码
  else 
    #条件为 假 执行此处代码
  fi
EOF

# 多条件测试
:<<EOF
与条件有多种表达方式:-a &&
[ 1 -lt 5 -a 4 -gt 2 ]; echo "与 -a写法 $?"
[[ 1 -lt 5 && 4 -gt 2 ]]; echo "与 &&写法 $?"
[ 1 -lt 5 ] && [ 4 -gt 2 ]; echo "与 多[]写法 $?"

或条件有多种表达方式:-o ||
[ 1 -lt 5 -o 4 -gt 5 ]; echo "与 -o写法 $?"
[[ 1 -lt 5 || 4 -gt 5 ]]; echo "与 ||写法 $?"
[ 1 -lt 5 ] || [ 4 -gt 5 ]; echo "与 多[]写法 $?"
EOF


# case用法
:<<EOF
  case 5 in
  1)
    #执行此处代码
  ;;
  *)
  esac
EOF

# 示例:
case 5 in 
1 | 2 | 3)
  #执行此处代码
;;
4)
  #执行此处代码
;;
*)
  #执行此处代码
;;
esac

八、数组和遍历

files=("1.txt" "2.txt" "3.txt")
# 通过下标获取
file1=${files[0]}

# 遍历
for f in ${files[@]} # 获取所有元素files[@],files[*]
  do 
    echo "f=$f"
  done

九、函数

# 函数带入参
function multiParams() {
  local arr=("$*")
  echo "所有入参:$*"
  echo "第0个参数=$0" # 脚本文件名
  echo "第1个参数=$1" # 第一个入参
  echo "第2个参数=$2" # 第二个入参
}
# 函数调用,调用必须在函数申请后面
multiParams "text" "mp3" "video"

# 数组入参
function iterateArray() {
  local arr=("$*")
  #local arr=("$@")
  for f in ${arr[@]} 
  do 
    echo "f1=$f"
  done
}

files=("1.txt" "2.txt" "3.txt")
iterateArray ${files[@]}

其他常用命令

echo $$  # 显示当前进程

id # 查看用户uid等信息

# 多行输出到控制台
cat <<EOF
多行输出1
多行输出2
多行输出3
EOF

# 脚本DEBUG,会把所有执行的代码打印出来
:<<EOF
方式一:bash -vx test.sh
方式二:在test.sh中设置,如在脚本最开始添加:set -xv
EOF


:<<EOF
zsh常用插件和安装
zsh-autosuggestions:这个插件会根据你的命令历史自动提供建议,提高你的效率
git clone https://github.com/zsh-users/zsh-autosuggestions $HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions

zsh-syntax-highlighting:一款语法高亮插件,实时高亮你的命令行输入,帮助你更好地理解正在键入的命令
git clone https://github.com/zsh-users/zsh-syntax-highlighting $HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting

zsh-history-substring-search:允许你通过子字符串搜索命令历史,意味着你可以只记住命令的一部分,然后让这个插件帮你找到完整的命令
git clone https://github.com/zsh-users/zsh-history-substring-search $HOME/.oh-my-zsh/custom/plugins/zsh-history-substring-search
EOF

简单作业

一、编写一个脚本使我们在写一个脚本时自动生成”#!/bin/bash”这一行和注释信息。

方式1
#!/bin/bash
read -p "请输入脚本文件名:" name
fileName=$name.sh
touch $fileName
chmod +x $fileName

> $fileName # 清空文件
# cat > $fileName < /dev/null # 清空文件,< /dev/null是可选的

cat << EOF > $fileName
#!/bin/bash
# Author: insert your name
# Date & Time: `date +"%F %T"`
# Description:
EOF

# 将字符串追加到文案 
#echo $appandText >> $fileName
方式2
#!/bin/bash
fileName=""
if [[ -z $1 ]];then
  fileName="newscript_`date +%m%d_%S`.sh"
else
  fileName=$1
fi

if  ! grep "^#!" $fileName &>/dev/null; then
cat >> $fileName << EOF
#!/bin/bash
# Author: Inert Your Name here.
# Date & Time: `date +"%F %T"`
# Description: Please Edit here.
EOF
fi

二、任意三个整数,判断最大数。

#!/bin/bash
read -p "请输入第一个数:" num1
read -p "请输入第二个数:" num2
read -p "请输入第三个数:" num3
maxNum=$num1
if [[ $num2 -gt $num1 ]];then
  maxNum=$num2
fi
if [[ $num3 -gt $maxNum ]];then
  maxNum=$num3
fi
echo "最大的数是:$maxNum"

三、求 100 以内偶数的和

#!/bin/bash
# 方式1:
sum=0
for i in {1..50}; do
  sum=$(($sum+2*$i))
done
echo "the sum is $sum"

# 方式2:
let SUM=0
for i in $(seq 1 100); do
  if [ $[$i%2] == 0 ]; then
    let SUM+=$i
  fi
done
echo "the sum is $SUM."

四、提示输入一个用户名,判断用户是否存在,如果存在,显示一下用户默认的 shell。

#!/bin/bash
read -p "请输入用户名:" usrName
[[ -n `id | grep "$usrName"` ]] && echo "$usrName exist" || echo "$usrName not exist"
Linux Shell脚本学习指南

graph TD

A("用户<br />用户在计算机钱使用文<br />字或图形界面进行作业") --> B(Shell,应用程序,KDE)

B --> A

B --> C(硬件:CPu,显卡,内存等)

C --> B


@implementation GeneratedPluginRegistrant

+ (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry {

[AcBasePackagePlugin registerWithRegistrar:[registry registrarForPlugin:@"AcBasePackagePlugin"]];

@end

awk工作原理 awk工作原理.png