Java教程

shell编程

本文主要是介绍shell编程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Shell编程入坑

Shell简介

Shell是一个用C语言编写的程序,它是用户使用linux的桥梁,Shell既是一种命令语言,又是一种程序设计语言。

Shell语法与php类似,容易上手

感觉这个语言蛮有意思的,在linux上写一些自动化脚本挺不错的。结合了菜鸟教程和网上视频简单学了学。

第一个Shell脚本

#! /bin/bash
echo "this is my first shell program!"

result:this is my first shell program!

  • #!用来告诉系统这个脚本需要什么解释器来执行,即使用哪一种shell,如果是python脚本,开头可以写上#! /usr/bin/python,即表示用python解释器来执行
  • echo命令用于向窗口输出文本

运行Shell脚本的方法:

  • 作为可执行程序

将上述代码保存为hello.sh(文本后缀对linux来说没有太大意义,其实该后缀可以任意修改,仍然可以正常运行),通过命令行cd到相应的目录,为该脚本赋予可执行权限后,通过./hello.sh的方式即可执行脚本

chmod +x hello.sh
./hello.sh

需要注意的是:

  1. 只能通过./test.sh去运行,而不能通过文件名test.sh去运行。直接写 test.sh,linux 系统会去 PATH 里寻找有没有叫 test.sh 的,而只有 /bin, /sbin, /usr/bin,/usr/sbin 等在 PATH 里,当前的目录通常不会在PATH里,所以直接输入test.sh 会显示找不到命令,要用./test.sh告诉系统,就在当前目录找。
  2. 该方法会打开一个子shell读取并执行hello.sh中的命令

直接输入hello.sh的结果:

┌──(root㉿kali)-[/home/kali/桌面]
└─# hello.sh
hello.sh: command not found

当然如果要让系统找到该文件,也可以直接输入绝对路径,这样也能成功执行脚本,例如:

┌──(root㉿kali)-[/home/kali/桌面]
└─# /home/kali/桌面/hello.sh                                                 
this is my first shell program!
  • 作为解释器参数

    直接运行解释器,作用的参数为shell脚本名,文件可以无执行权限。例如:

    /bin/sh hello.sh  # 运行shell脚本
    /usr/bin/python hello.py  # 运行python脚本
    

​ 该方法会打开一个子shell来读取并执行hello.sh中的命令

  • 其他方式

​ 使用source hello.sh或者. hello.sh(bash特有)方式,其作用是在当前shell环境下读取并执行hello.sh脚本中的命令,文件可以无执行权限。

BASH

cat /etc/shells,可以查看当前系统可以使用哪些shell

echo $SHELL 可以查看自己当前使用的是什么shell

以下将使用kali中的bash命令行作为示例(kali默认使用的shell是zsh,在命令行中输入bash即可进入使用bash的子shell进程中

以下内容仅适用于适用bash的命令行

  • bash是linux其中一种命令处理器,运行在文本窗口中,并能执行用户直接输入的命令
  • bash还能从文件中读取linux命令,称之为脚本
  • bash支持通配符、管道、命令替换、条件判断等逻辑控制语句

命令历史

Shell会保留其会话中用户提交执行的命令

history 

-c 清空内存中命令历史

-r 从文件中恢复历史命令

数字  :显示最近n条命令  例如: history 10

 
echo $HISTSIZE    # 显示保留历史的条数

 
# 存放用户执行的历史命令,写入文件 
┌──(root㉿kali)-[/home/kali]
└─# echo $HISTFILE
/root/.bash_history 
# kali使用的是zsh ,所以保存路径为 /root/.zsh_history


# 调用历史记录
![历史id]    可以快速执行历史命令 
!! 执行上次历史命令 

bash特性汇总

  • 文件路径、命令tab键补全
  • 快捷键 ctrl +a,e,u,k,l
  • 通配符
  • 命令历史
  • 命令别名
  • 命令行展开

变量

变量基本内容

  • 变量定义与赋值,注意变量与值之间不得有空格

以下赋值方式都是可以的

name="123"
name=123
name='123'

shell默认把所有变量都认为是字符串
shell变量是弱类型,无需事先声明类型,是将声明和赋值同时进行变量替换/引用

┌──(root㉿kali)-[/home/kali]
└─# name="123"                                                               

┌──(root㉿kali)-[/home/kali]
└─# echo $name   # 可以在变量名前加$符号                                                            
123

┌──(root㉿kali)-[/home/kali]
└─# echo ${name}  # 比较完整的是前面加$,变量名再用{}包裹,加花括号是为了帮助解释器识别变量的边界                                                          
123

'x',"x",x,`x`

  • 单引号:所见即所得,是强引用
  • 双引号:输出引号里所有的内容,识别特殊符号,弱引用
  • 无引号:连续的符号可以不加引号,有空格则有歧义,最好使用双引号
  • 反引号,引用命令执行结果,等于$()用法
┌──(kali㉿kali)-[~/桌面]
└─$ num1=1                                                                   

┌──(kali㉿kali)-[~/桌面]
└─$ num2=2                                                                   

┌──(kali㉿kali)-[~/桌面]
└─$ num3="$num1"

┌──(kali㉿kali)-[~/桌面]
└─$ echo $num3                                                         
1

┌──(kali㉿kali)-[~/桌面]
└─$ num4='$num2'

┌──(kali㉿kali)-[~/桌面]
└─$ echo $num4
$num2

`linux命令`  # echo 这个值会直接运行该linux命令

┌──(root㉿kali)-[/home/kali/桌面]
└─# name=`whoami`

┌──(root㉿kali)-[/home/kali/桌面]
└─# echo $name                                                               
root

  • 变量名规则

  • 不得引用保留关键字(help查看关键字)

  • 只能包含数字、字母、下划线

  • 不能以数字开头

  • 不能用标点符号

  • 变量名严格区分大小写

  • 变量作用域

    image-20220828203355577

    可以发现在当前shell中定义的变量在子shell(在当前shell中输入想要使用的shell名字,比如zsh,bash,sh,即可进入子shell中无法引用,同样的,子shell中定义的变量也无法在当前shell中引用,因为作用域的缘故。

    • 环境变量,也称为全局变量,针对当前shell及其任意子进程,环境变量也分 '自定义'、'内置环境变量',如$PATH就是内置环境变量
    • 局部变量,针对在shell函数或是shell脚本中定义
    • 位置参数变量:用于shell脚本中传递的参数
    • 特殊变量:shell内置的特殊功效变量,如:
      • $? #作用:判断上一行命令执行是否成功
        • 0:成功
        • 1-255:错误码
    • 自定义 变量

父子shell

  • 每次调用bash/sh解释器执行脚本,都会开启一个子shell,因此不保留脚本运行过程中的shell变量

  • 通过pstree命令可以检查进程树,查看当前使用的shell的父子情况

  • 调用source或者. 等方式执行脚本是在当前shell环境加载脚本,因此保留变量

环境变量设置

  1. 环境变量一般指的是用export内置命令导出的变量,用于定义shell的运行环境、保证shell命令的正确执行。shell通过环境变量确定登录的用户名,PATH路径、文件系统等各种应用。

  2. 环境变量可以在命令行中临时创建,但是用户退出shell终端,变量即丢失,如要永久生效,需要修改环境变量配置文件

    • 用户个人配置文件 ~/.bash_profile~/.bashrc 远程登录用户特有文件
  • 全局配置文件/etc/profile/etc/bashrc,且系统建议最好创建在/etc/profile.d/,而非直接修改主文件,修改全局配置文件,影响所有登录系统的用户
  1. 每个用户都有自己的环境变量配置文件,~/.bash_profile.bashrc,每个用户中的环境变量只在自己的shell终端中生效,每次写入新的环境变量后,需重新登录才能加载新写入的环境变量
  2. 当需要让所有用户都可以使用某个变量,可以将其写入全局环境变量配置文件,即/etc/profile

检查系统环境变量的命令

命令 作用
set 输出所有变量,包括全局变量、局部变量
env 只显示全局变量
declare 输出所有的变量,如同set
export 显示和设置环境变量值

撤销环境变量

  • unset变量名,删除变量或函数

设置只读变量

  • readonly,只有shell结束,只读变量失效
┌──(root㉿kali)-[/home/kali/桌面]
└─# readonly passwd=123

┌──(root㉿kali)-[/home/kali/桌面]
└─# echo $passwd
123

┌──(root㉿kali)-[/home/kali/桌面]
└─# passwd=234
bash: passwd:只读变量

系统保留环境变量关键字

  • bash内嵌了诸多环境变量,用于定义bash的工作环境

    通过下面命令可以查看:

    export |awk -F '[ :=]' '{print $3}' >env
    cat env
    

result:

COLORTERM
COMMAND_NOT_FOUND_INSTALL_PROMPT
DISPLAY
DOTNET_CLI_TELEMETRY_OPTOUT
HOME
LANG
LANGUAGE
LESS_TERMCAP_mb
LESS_TERMCAP_md
LESS_TERMCAP_me
LESS_TERMCAP_se
LESS_TERMCAP_so
LESS_TERMCAP_ue
LESS_TERMCAP_us
LOGNAME
LS_COLORS
MAIL
OLDPWD
PATH
POWERSHELL_TELEMETRY_OPTOUT
POWERSHELL_UPDATECHECK
PWD
SHELL
SHLVL
SUDO_COMMAND
SUDO_GID
SUDO_UID
SUDO_USER
TERM
USER
XAUTHORITY

环境变量加载顺序

  1. ssh登录Linux后,系统启动一个bash,bash会读取若干个系统文件环境文件,检查环境变量设置
  2. /etc/profile:全局环境变量文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行,并从/etc/profile.d目录的配置文件中搜集shell的设置。
  3. 然后读取/etc/profile.d目录下的脚本,有系统诸多脚本,也放入自定义需要登录加载的脚本,便于用于登录后立即运行脚本
  4. 运行$HOME/.bash_profile(用户环境变量文件)
  5. 运行$HOME/.bashrc
  6. 最终运行/etc/bashrc

特殊变量

在命令行中输入man sh,查找Special Parameters,即可看到部分特殊变量

参数处理 说明
$0 获取shell脚本文件名,以及脚本路径
$n 获取shell脚本传递的第n个参数,n在1-9之间,如$1、$2、$9,大于则需要写,${10},参数空格隔开
$# 传递到脚本的参数个数
$* 以一个单字符串显示所有向脚本传递的参数。 如"$*"用「"」括起来的情况、以"$1 $2 … $n"的形式输出所有参数。
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的ID号
$@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。 如"$@"用「"」括起来的情况、以"$1" "$2" … "$n" 的形式输出所有参数。
$_ 在此之前执行的命令的最后一个参数
$- 显示Shell使用的当前选项,与set命令功能相同。
$? 显示最后命令的退出状态。0表示没有错误,其他任何值(1-255)表明有错误。

运行如下特殊变量.sh文件:

┌──(root㉿kali)-[/home/kali/桌面]
└─# cat 特殊变量.sh 
#! /bin/bash
echo -e "\n" # 使用-e参数可以输出转义字符
echo '$n'
echo "文件名为:$0"
echo "第一个参数:$1"第二个参数:$2"第三个参数:$3"
echo -e "\n"
echo '$#'
echo "参数个数为:$#"
echo -e "\n"
echo '$$'
echo "该文件运行的PID为$$"
echo -e "\n"
echo '$*'
echo "所有参数为:$*"
echo -e "\n"
echo '$@'
echo "所有参数为:$@"

结果:

┌──(root㉿kali)-[/home/kali/桌面]
└─# ./特殊变量.sh a b c


$n
文件名为:./参数传递.sh
第一个参数:a第二个参数:b第三个参数:c


$#
参数个数为:3


$$
该文件运行的PID为40241


$*
所有参数为:a b c


$@
所有参数为:a b c

$_

运行上述脚本后,输出$_

┌──(root㉿kali)-[/home/kali/桌面]
└─# echo $_
c

$!

┌──(root㉿kali)-[/home/kali/桌面]
└─# nohup ping www.baidu.com & 1>/dev/hull
[1] 49488

┌──(root㉿kali)-[/home/kali/桌面]
└─# nohup: 忽略输入并把输出追加到'nohup.out'
echo $!
49488 

┌────(root㉿kali)-[/home/kali/桌面]
└─# kill -9 $!  

┌────(root㉿kali)-[/home/kali/桌面]
└─# [1]+  已杀死               nohup ping www.baidu.com

$*与$@的区别

当 $* 和 $@ 不被双引号 " " 包围时,它们之间没有任何区别,都是将接收到的每个参数看做一份数据,彼此之间以空格来分隔。

但是当它们被双引号" "包含时,就会有区别了:

  • "$*"会将所有的参数从整体上看做一份数据,而不是把每个参数都看做一份数据;
  • "$@"仍然将每个参数都看作一份数据,彼此之间是独立的。

比如传递了 5 个参数,那么对于 $* 来说,这 5 个参数会合并到一起形成一份数据,它们之间是无法分割的;而对于 $@ 来说,这 5 个参数是相互独立的,它们是 5 份数据。

如果使用 echo 直接输出 $* 和 $@ 做对比,是看不出区别的;但如果使用 for 循环来逐个输出数据,立即就能看出区别来。

运行如下test.sh文件

┌──(root㉿kali)-[/home/kali/桌面]
└─# cat test.sh
#! /bin/bash

echo "params from $* "

for _ in "$*";do
        echo $_
done


echo "params from $@ "

for _ in "$@";do
        echo $_
done

结果:

┌──(root㉿kali)-[/home/kali/桌面]
└─# ./test.sh a b c
params from a b c 
a b c
params from a b c 
a
b
c
这篇关于shell编程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!