各位看官,从今天开始,我们进入新的专栏Shell学习,Shell 是操作系统的命令行界面,它允许用户通过输入命令与操作系统交互。常见的 Shell 有 Bash 和 Zsh,它们可以执行用户输入的命令或运行脚本文件。Shell 广泛应用于系统管理、开发环境自动化、数据处理、网络管理和文件操作等场景,因其灵活性和强大的脚本编写能力而备受开发者和系统管理员的青睐。这与我们以后的开发工作也是密切相关的,我们应该认真对待,为以后工作打好基础,做好铺垫!
目录
一、Shell介绍
1.1 编译型语言和解释型语言
对于计算机硬件而言,它们只能识别某些特定的二进制指令(机器码),而无法解读和直接执行我们编写的源代码。因此,在程序真正运行之前必须将源代码转换成二进制指令。而因为不同语言转换的时机不同,但总体上可分为两类,因而将高级编程语言分为了编译型语言和解释型语言。
1.2 二者的区别
两者在以下方面存在一定的区别(但不仅限于以下方面)
区别1、
编译型语言在程序在执行之前需要一个专门的编译过程,通过编译器把程序编译成为可执行文件,再由机器运行这个文件,运行时不需要重新翻译,直接使用编译的结果就行了。运行效率高,开发效率低。
解释型语言是一边执行一边转换的,其不会由源代码生成可执行文件,而是先翻译成中间代码,再由解释器对中间代码进行解释运行,每执行一次都要翻译一次。运行效率低,开发效率高。
编译型语言的大致运行流程:
解释型语言的大致运行流程:
区别2、
编译型语言可以实现一次编译,无限次运行,只要在首次执行时编译生成相应的可执行文件,在以后的每次运行就只需要直接运行这个可执行文件,因此其运行效率高于解释型语言,但因为不同平台的可执行文件不同(同时不同平台支持的函数、类型、变量等都可能不同),因此编译型语言难以实现在不同操作系统间随意切换,可移植性较差。
对于解释型语言,其实现的是一次编写,到处运行,每次执行都得重新转换源代码,因此其在效率上天生就低于编译型语言,但也因为其每次运行都会重新转换源代码,因此只需要解释器根据操作系统的不同将源代码转换成不同的机器码,相同的源代码,就可以实现在不同的平台上运行,因此其更灵活。
1.3 什么是shell
网上有很多shell 的概念介绍,其实都很官方化,如果你对linux 命令很熟悉,那么编写shell 就不是一个难事,shell 本质上是 linux 命令,一条一条命令组合在一起,实现某一个目的,就变成了shell脚本。它从一定程度上减轻了工作量,提高了工作效率。简单来说Shell就是一个用户跟操作系统之间的一个命令解释器,它为用户提供了一个向Linux内核发送请求以便运行程序的界面系统级程序,用户可以用Shell来启动、挂起、停止甚至是编写一些程序。
常见的shell 有哪些?
Bourne Shell(/usr/bin/sh或/bin/sh)
Bourne Again Shell(/bin/bash)
C Shell(/usr/bin/csh)
K Shell(/usr/bin/ksh)
Shell for Root(/sbin/sh)
最常用的shell是Bash,也就是Bourne Again Shell。Bash由于易用和免费,在日常工作中被广泛使用,也是大多数Linux操作系统默认的Shell环境。
1.4 第一个helloworld
运行脚本文件有两种方式:
- 直接使用bash命令执行;
- 给文件加上执行权限,执行文件 ;
二、常用语法
2.1 变量
2.1.1 本地变量
在Shell编程中,本地变量是指在脚本或函数中声明并使用的变量,这些变量只在其定义的范围内有效。一旦程序执行离开这个范围,这些变量就会失效或被销毁。
在shell里,使用变量之前通常并不需要指定变量的类型。在默认情况下,所有变量都被看作字符串并以字符串来存储,即使它们被赋值为数值时也是如此。shell和一些工具程序会在需要时把数值型字符串转换为对应的数值以对它们进行操作。Linux是 一个区分大小写的系统 ,在shell中,你可以通过在变量名前加一个$符号来访问它的内容。想要获取变量内容,必须在它前面加一个$字符。当你为变量赋值时,你只需要使用变量名,该变量会根据需要被自动创建。一种获取变量内容的简单方式就是在变量名前加一个$符号,再用echo命令将它的内容输出到终端上。
#!/usr/bin/bash 指定使用bash命令解释器
#shell中的变量主要分为3种:本地变量、环境变量、参数变量
/*************************1、本地变量的定义和使用*******************************/
a=10 这里的a可以看作是整型,也可以看作是字符串,具体要看bash怎么操作数据
b=hello 单个单词可以不加双引号,也可以加双引号
b="hello"
b="hello world" 中间有空格表示一个整体,必须要加双引号!
d=$a 变量之间的赋值
a="10" 定义变量时,加双引号、不加双引号、单引号都可以没有区别!
b='hello' 在变量之间赋值的时候,双引号和单引号是有区别的,单引号代表原样输出
d="$a" 这种表示取a的值赋值给d
d1='$a' 这种表示原样输出$a
echo "a=$a" $表示取变量a的值
echo "b=$b"
echo "d=$d"
exit 0
/***************************2、从键盘获取值给变量赋值*******************************/
#!/usr/bin/bash 指定使用bash命令解释器
echo "input"
read a 从键盘读取数据赋值给变量a
echo "a=$a"
#基本算术运算:a++ a-- a+=3
let "a+=1" 变量自增
let "a-=1" 变量自减
let "a+=3"
exit 0
/***********************************************************************************/
#开头代表注释。 你可以使用read命令将用户的输入赋值给一个变量。 这个命令需要一个参数,即准备读入用户输入数据的变量名,然后它会等待用户输入数据。通常情况下,在用户按下回车键时,read命令结束。
注意事项:
- 脚本的第一行需要指定使用的命令解释器;
- 定义本地变量的格式为:变量名=值 ,不需要指定变量类型(根据对数据的操作,解释器自动进行转换),语句结束没有分号;
- 等号的两边不能有空格;
- 如果字符串里包含空格,就必须用双引号把它们括起来;
- $变量名:表示取变量的值;
- 定义变量时,不使用引号、使用双引号、使用单引号没有区别,在定义含有空格的变量时必须加上双引号,此外,使用一个变量给另一个变量进行赋值时,此时必须使用双引号,因为单引号代表原样输出!
- 在赋值和使用变量时,建议使用引号,特别是当变量的值包含空格或特殊字符时;
2.1.2 环境变量
在shell编程中,环境变量是非常重要的概念。环境变量是操作系统用来存储配置信息和控制运行环境的键值对。它们可以影响运行进程的行为,提供了一种在不同进程间传递信息的机制。
当一个shel脚本程序开始执行时,一些变量会根据环境设置中的值进行初始化。这些变量通常用大写字母做名字,以便把它们和用户在脚本程序里定义的变量区分开来,具体创建的变量取决于个人配置。表2-2列出了其中一些主要的变量。
/*********************设置环境变量**********************************/
1、临时设置环境变量,可以在命令行中临时设置一个环境变量,这种设置仅在当前会话有效:
export MY_VAR="Hello World"
echo $MY_VAR # 输出 "Hello World"
2、永久设置环境变量,要永久设置环境变量,可以将它们添加到shell的配置文件中,如 .bashrc, .bash_profile, .zshrc 等。例如,在 .bashrc 文件中添加:
export MY_VAR="Hello World"
然后使其生效:source ~/.bashrc
/***********************使用环境变量*********************************/
在shell脚本中,可以通过 $ 符号来引用环境变量:
echo "My variable is: $MY_VAR"
/**********************修改环境变量********************************/
可以使用 export 命令来修改已有的环境变量:
export PATH="$PATH:/new/path"
这条命令将新的路径 /new/path 添加到现有的PATH变量中。
/*********************删除环境变量*******************************/
可以使用 unset 命令删除环境变量:
unset MY_VAR
/******************************************************************/
2.1.3 参数变量
在shell编程中,参数变量是指传递给脚本或函数的参数。参数变量使得脚本更加灵活和通用,可以根据不同的输入执行不同的操作。
如果脚本程序在调用时带有参数,一些额外的变量就会被创建。即使没有传递任何参数,环境变量$#也依然存在,只不过它的值是0罢了。
假设我们有一个脚本 example.sh
,其内容如下:
#!/bin/bash
echo "Script name: $0"
echo "First parameter: $1"
echo "Second parameter: $2"
echo "Number of parameters: $#"
echo "All parameters (individually quoted): $@"
echo "All parameters (as a single string): $*"
运行该脚本并传递一些参数:
./example.sh arg1 arg2 arg3
输出将会是:
Script name: ./example.sh
First parameter: arg1
Second parameter: arg2
Number of parameters: 3
All parameters (individually quoted): arg1 arg2 arg3
All parameters (as a single string): arg1 arg2 arg3
2.2 条件
所有程序设计语言的基础是对条件进行测试判断,并根据测试结果采取不同行动的能力。在讨论它之前,我们先来看看在shell脚本程序里可以使用的条件结构,然后再来看看使用这些条件的控制结构。一个shell脚本能够对任何可以从命令行上调用的命令的退出码进行测试,其中也包括你自己编写的脚本程序。这也就是为什么要在所有自己编写的脚本程序的结尾包括一条返回值的exit命令的重要原因。
test或 [ 命令
在实际工作中,大多数脚本程序都会广泛使用shell的布尔判断命令 [ 或test。在一些系统上,这两个命令的作用是一样的,只是为了增强可读性,当使用 [ 命令时,我们还使用符号 ] 来结尾。把 [ 符号当作一条命令多少有点奇怪,但它在代码中确实会使命令的语法看起来更简单、更明确、更像其他的程序设计语言。我们以一个最简单的条件为例来介绍test命令的用法:检查一个文件是否存在。用于实现这一操作的命令是test -f <filename>, 所以在脚本程序里,你可以写出如下所示的代码:
if test -f fred.c
then
.... //条件满足执行的语句
fi
你还可以写成下面这样,这种方式更常用,注意[ ]两边要有空格:
if [ -f fred.c ]
then
... //条件满足执行的语句
fi
test命令的退出码(表明条件是否被满足)决定是否需要执行后面的条件代码。test命令可以使用的条件类型可以归为3类:字符串比较、算术比较和与文件有关的条件测试,表2-4、表2-5和表2-6描述了这3种条件类型。
/***************************条件判断的基本使用***********************/
/***************************单分支*****************************************/
echo "input"
read line
if [ "$line" = "hello" ]
then
echo "=="
fi
/***************************************************************************/
echo "input"
read line
if [ "$line" -ge 0 ] && [ "$line" -lt 10 ]
then
echo "0<=$line<10"
fi
/***************************双分支********************************************/
echo "input"
read line
if [ "$line" -ge 100 ]
then
echo "大于100"
else
echo "小于100"
fi
/***************************多分支****************************************/
echo "input"
read line
if [ -d "$line" ]
then
echo "$line is a dirent"
elif [ -f "$line" ]
then
echo "$line is a file"
else
echo "$line not exist"
fi
/************************************************************************/
2.3 控制结构
2.3.1 for语句
我们可以用for结构来循环处理一组值, 这组值可以是任意字符串的集合。它们可以在程序里被
列出,更常见的做法是使用shell的文件名扩展结果。
它的语法很简单:
for 变量 in 循环值
do
..... //执行的语句块
done
/****************************************************************************/
for i in 1 2 3
do
echo "i=$i"
done
/****************************************************************************/
for i in hello abc 123
do
echo "i=$i"
done
/****************************************************************************/
for i in "hello abc 123" //这里会将他们当成一个整体
do
echo "i=$i"
done
/*********************如何在一行语句中执行命令******************************/
//这种方式并不会将ls当作命令执行,而是一个普通的字符串
for i in ls
do
echo "i=$i"
done
/*****************************解决办法一*************************************/
下面的方式会将ls当作命令去执行,将结果赋值给i
values=$(ls)
for i in $(ls) $符号告诉解释器这是一个命令,需要去执行
do
echo "i=$i"
done
/*****************************解决办法二*************************************/
for i in `ls` `符号告诉解释器这是一个命令,需要去执行
do
echo "i=$i"
done
/***************************************************************************/
i=1
let "i+=1"
i=`expr $i + 1`
i=`expr $i - 3`
i=`expr $i * 3`
i=`expr $i \ 3`
echo "i=$i"
/*****************************************************************************/
可以将在终端中需要执行的一系列命令放在脚本中,然后执行脚本,他会将脚本中的命令全部执行一遍,这样更加方便,只需要执行脚本就可以了。
2.3.2 while语句
因为在默认情况下,所有的shell变量值都被认为是字符串,所以for循环特别适合于对一系列字符串进行循环处理,但如果你事先并不知道循环要执行的次数,那么它就显得不是那么有用了。
如果需要重复执行一个命令序列,但事先又不知道这个命令序列应该执行的次数,你通常会使用
一个while循环,它的语法如下所示:
while 条件
do
.... 需要执行的语句
done
/*******************************死循环写法********************************/
while true
do
echo "hello"
done
while [ "hello" = "hello"]
do
echo "hello"
done
/************************************************************************/
i=0
while [ "$i" -lt 10]
do
echo "i=$i"
i=`expr $i + 1`
done
/************************************************************************/
i=0
while true
do
if [ "$i" -ge 10]
then
break
fi
echo "i=$i"
i=`expr $i + 1`
done
/*************************************************************************/
i=0
while [ "$i" -lt 10]
do
if [ "$i" -eq 5 ]
then
i=`expr $i + 1`
continue
fi
echo "i=$i"
i=`expr $i + 1`
done
/*************************************************************************/
2.3.3 until语句
它与while循环很相似,只是把条件测试反过来了。换句话说,循环将反复执行直到条件为真,而不是在条件为真时反复执行。
如果需要循环至少执行一次,那么就使用While循环;如果可能根本都不需要执行循环,就使用until循环。
until语句的语法如下所示:
until 条件
do
.... 需要执行的语句
done
/**********************************************************/
until [ -f "a.txt" ]
do
echo "not find a.txt"
done
2.3.4 case语句
因为case结构具备匹配多个模式然后执行多条相关语句的能力,这使得它非常适合于处理用户的输入。
2.4 函数
你可以在shell中定义函数。如果你想编写大型的shell脚本程序,你会想到用它们来构造自己的代码。要定义一个shell函数,你只需写出它的名字,然后是一对空括号,再把函数中的语句放在一对花括号中,没有返回值和参数列表,但是他可以传参!也没有函数声明,没有主函数的说法。
如下所示:
函数名()
{
//函数语句
}
/**************************基本使用******************************************************/
//函数定义
fun()
{
echo "fun run"
}
fun 调用函数不需要括号,直接写函数名
/**************************函数传参****************************************************/
//函数定义
fun()
{
echo "fun run"
echo "第一个参数:$1"
echo "第二个参数:$2"
}
fun hello abc 调用函数传入两个参数,直接写在函数名的后面
/************************************************************************************/
//函数定义
fun()
{
echo "fun run"
echo "第一个参数:$1"
echo "第二个参数:$2"
}
fun2()
{
echo "fun run"
echo "第一个参数:$1"
echo "第二个参数:$2"
}
fun hello abc 调用函数传入两个参数,直接写在函数名的后面
fun2 hello abc 调用函数传入两个参数,直接写在函数名的后面
/***************************脚本传参*********************************************/
echo "my.sh的第一个参数:$1"
echo "my.sh的第二个参数:$2"
username=$1
passwd=$2
echo "username=$username"
echo "passwd=$passwd"
//运行脚本的时候:./my.sh hello world
/*************************返回值如何获取**************************************/
my_add()
{
if [ $# -ne 2 ]
then
echo "err"
return 0
fi
res =`expr $1 + $2 `
return $res //这一行可以省略
}
my_add 2 3
echo "res=$res"
//这里可不是C语言中的局部变量销毁,外界无法使用!这里外界是可以使用的!
/***************************************************************************/
必须在调用一个函数之前先对它进行定义,这有点像Pascal语言里函数必须先于调用而被定义
的概念,只是在shell中不存在前向声明。但这并不会成为什么问题,因为所有脚本程序都是从顶部开始执行,所以只要把所有函数定义都放在任何一个函数调用之前,就可以保证所有的函数在被调用之前就被定义了。
2.5 脚本调用
脚本之间是可以相互调用的,在a脚本的内容中可以直接调用b脚本。
//a.sh
#!/usr/bin/bash
echo "helloworld"
./b.sh //调用b.sh
//b.sh
#!/usr/bin/bash
echo "receice"
上面这种是直接在一个脚本中直接调用另一个脚本,没有传递参数,那么,如果我们在调用另一个脚本的时候,想要传递参数,该怎么办呢?
从上面的结果我们可以看到,我们在a脚本中定义变量,并且调用b脚本,b脚本是没有办法获取a脚本中的变量的,这是因为,我们运行一个脚本就会有一个解释器,这是两个不同的解释器分别运行!解决的办法就是要用到:脚本调用传参!
方式一:调用b脚本的时候传给b脚本参数。
方式二:将参数定义为环境变量,因为:子进程会继承父进程的环境变量,他就可以直接访问。
方式三:以点命令方式执行
2.6 C语言调用脚本
我们想在C程序中调用脚本又该如何做呢?
产生子进程,然后用脚本替换子进程即可!
三、2个常用的命令
3.1 awk命令
3.1.1 概念
AWK 是一种用于处理文本和数据的编程语言,特别适合于从文本文件或数据流中提取和操作数据。它的名字来自于三位创始人的姓氏:Alfred Aho、Peter Weinberger 和 Brian Kernighan。
3.1.2 使用
AWK 的基本语法如下
awk 'pattern { action }' filename
- pattern:用于匹配输入文本的模式。如果模式匹配,执行对应的动作。
- action:在模式匹配时执行的操作。
常用功能和用法
1. 打印文件的所有行
awk '{ print }' filename
2.打印第一列:
awk '{ print $1 }' data.txt
3.2 sed命令
3.2.1 概念
sed
(Stream Editor)是一个用于文本处理的强大工具,主要用于根据指定的规则编辑文本文件。sed
命令可以进行插入、删除、替换等操作,常用于处理数据流或文本文件中的内容。
基本语法:
sed [options] 'command' file
options
:用于指定sed
的行为,例如是否进行全局替换等。command
:sed
命令,指定要执行的文本处理操作。file
:要处理的文件。
这一篇博客只是对shell的简单了解,如需深入学习,点赞加关注!