shell基础

常见的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环境

重定向

python hello.py > output.txt   # 标准输出到(文件)
python hello.py >> output.txt  # 标准输出到(文件),追加
python hello.py 2> error.log   # 标准错误到(文件)
python hello.py 2>&1           # 标准错误到标准输出
python hello.py 2>/dev/null    # 标准错误到(空null)
python hello.py &>/dev/null    # 标准输出和标准错误到(空null)
python hello.py < foo.txt      # 将 foo.txt 提供给 python 的标准输入

系统环境变量

#Shell常见的系统环境变量
PATH    命令所示路径,以冒号为分割;
HOME    打印用户家目录;
SHELL   显示当前Shell类型;
USER    打印当前用户名;
ID      打印当前用户id信息;
PWD     显示当前所在路径;
TERM    打印当前终端类型;
HOSTNAME    显示当前主机名;
PS1         定义主机命令提示符的;
HISTSIZE    历史命令大小,可通过 HISTTIMEFORMAT 变量设置命令执行时间;
RANDOM      随机生成一个 0 至 32767 的整数;

颜色输出

字体颜色数字

#字体颜色31-37 默认=0,黑色=30,红色=31,绿色=32,黄色=33,蓝色=34,紫色=35,天蓝色=36,白色=3
#背景颜色41-47
echo -e "\e[31m红色文字\e[0m"
echo -e "\e[32m绿色文字\e[0m"
各参数解释说明:
-e  启用反斜杠转义的解释
\e 转义起始符,定义一个转义序列, 可以使用\033代替 可以定义变量RED='\033[31m'; GREEN='\033[32m';  NC='\033[0m'
[ 表示开始定义颜色
m 转义终止符,表示颜色定义完毕
再次使用 \e[ ,表示再次开启颜色定义,0表示使用默认的颜色,m表示颜色定义结束,所以最后的 \e[0m 的作用是恢复之前的配色方案
\e[0m也可以写成\033[0m

字体样式

#举例说明
echo -e "\e[1;33;41m test content \e[0m"
1表示高亮,33表示字体颜色为黄色,45表示背景色为红色

# 显示方式1-8
0	普通字符(复位或正常)	关闭所有属性
1	粗体字	
2	弱化(降低强度)	未广泛支持
3	斜体	未广泛支持有时为反相显示
4	下划线	
5	缓慢闪烁	
6	快速闪烁	
7	反显	
8	隐藏	未广泛支持。
9	划除	

语法检查调试

Shell本身提供一些调试方法选项:

-n,读一遍脚本中的命令但不执行,用于检查脚本中的语法错误。
-v,一边执行脚本,一边将执行过的脚本命令打印到标准输出。
-x,提供跟踪执行信息,将执行的每一条命令和结果依次打印出来。
使用这些选项有三种方法(注意:避免几种调试选项混用)

1.在命令行提供参数:sh -x script.sh 或者 bash -n script.sh
2.脚本开头提供参数:#!/bin/sh -x 或者 #!/bin/bash -x
3.在脚本中用set命令启用或者禁用参数,其中set -x表示启用,set +x表示禁用

流程控制

if

语法格式

if [ 条件测试 ]; then
    # 条件为真时执行的命令
fi

if [ -e "/etc/passwd" ]; then
    echo "文件 /etc/passwd 存在"
fi

字符串判断

if [[ str1 = str2 ]]   # 当字符串 str1 和 str2 有相同内容、长度时为真
if [[ str1 != str2 ]]  # 当字符串 str1 和 str2 不等时为真
if [[ -n "str1" ]]     # 当字符串 str1 的长度大于 0(非空)时为真 
if [[ -z "str1" ]]     # 当字符串 str1 的长度为 0(空)时为真 
if [[ str1 ]]          # 当字符串 str1 为非空时为真

文件判断

文件存在且可读

if [ -e "$FILE" ] && [ -r "$FILE" ]; then
    echo "文件存在且可读"
fi


[[ -h FILE ]]	  符号链接
[[ -s FILE ]]	  大小 > 0 字节
[[ -r FILE ]]	  可读
[[ -w FILE ]]	  可写
[[ -x FILE ]]	  可执行文件
[[ f1 -nt f2 ]]	  f1 比 f2 新
[[ f1 -ot f2 ]]	  f2 比 f1 新
[[ f1 -ef f2 ]]	  相同的文件

目录判断

if [ -d dir ];  判断目录是否存在 

数字判断

-eq  等于,应用于整型比较 equal;
-ne  不等于,应用于整型比较 not equal;
-lt  小于,应用于整型比较 letter;
-gt  大于,应用于整型比较 greater;
-le  小于或等于,应用于整型比较;
-ge  大于或等于,应用于整型比较;
-a  双方都成立(and) 逻辑表达式 –a 逻辑表达式;
-o  单方成立(or) 逻辑表达式 –o 逻辑表达式;
-z  空字符串;
-x      是否具有可执行权限
||      单方成立;

判断用户是否存在

if id -u "nginx" >/dev/null 2>&1; then
    echo "nginx用户存在"
fi

if-else语句

语法

if [ 条件测试1 ]; then
    # 条件1为真时执行的命令
elif [ 条件测试2 ]; then
    # 条件2为真时执行的命令
else
    # 所有条件都为假时执行的命令
fi

案例:分数比较

read -p "请输入分数: " score
if [ $score -ge 90 ]; then
    echo "优秀"
elif [ $score -ge 80 ]; then
    echo "良好"
elif [ $score -ge 60 ]; then
    echo "及格"
else
    echo "不及格"
fi

for 循环

语法

for 变量名 in 取值列表; do
    # 执行的操作
done

遍历数字范围

for i in {1..5}; do
    echo $i                # 依次打印1 2 3 4 5
done
或
for ((  i=1; i<5; ++i)); do # 使用条件表达式
    echo $i
done

遍历数组

arr=("apple" "banana" "orange" "grape")
for i in "${arr[@]}"; do
    echo $i
done
结果:
apple
banana
orange
grape

arr=("apple" "banana" "orange" "grape")
for i in "${arr[*]}"; do
    echo $i
done
结果:
apple banana orange grape

无限循环特定输入退出

# 用户输入 "exit" 才退出循环
for (( ; ; )); do
    read -p "输入命令 (输入 exit 退出): " cmd
    if [ "$cmd" = "exit" ]; then
        echo "退出循环"
        break
    else
        echo "执行: $cmd"
    fi
done

while 循环

语法

while [ 条件测试 ]; do
  # 要执行的命令
done

数字循环

count=1
while [ $count -le 100 ]; do
    echo $count
    count=$((count+1))
done

或
count=1
while ((count <= 100)); do   # 使用条件表达式
    echo $count
    count=$((count+1))
done

1-100 的总和

#求 1-100 的总和
j=0
i=1
while ((i&lt;=100))
do
     j=`expr $i + $j`
     ((i++))
done
echo $j

无限循环特定输入退出

#只有输入CTRL+C才会退出循环
while true; do
    echo "Press [CTRL+C] to stop.."
    sleep 1
done

break 和 continue 语句

break 和 continue 语句
  break 是终止循环。
  continue 是跳出当前循环。
#示例 1:在死循环中,满足条件终止循环
while true; do
  let N++
  if [ $N -eq 5 ]; then
    break
fi
  echo $N
done
输出:1 2 3 4
#示例 2:举例子说明 continue 用法
N=0
while [ $N -lt 5 ]; do
  let N++
if [ $N -eq 3 ]; then
  continue
fi
  echo $N
done
输出:1 2 4
# 打印 1-100 数字
i=0
while ((i&lt;=100))
do
        echo  $i
        i=`expr $i + 1`
done

case 选择语句

语法

case 变量 in
    模式1)
        命令1
        ;;
    模式2)
        命令2
        ;;
    *)
        默认命令
        ;;
esac

输入字符判断

#!/bin/bash

echo "请输入一个字符:"
read -n1 char  # 只读取一个字符,无需按回车
echo          # 换行,美观起见

case $char in
    [a-z])
        echo "小写字母"
        ;;
    [A-Z])
        echo "大写字母"
        ;;
    [0-9])
        echo "数字"
        ;;
    *)
        echo "特殊字符"
        ;;
esac

菜单创建

内置变量详解

$REPLY 变量的作用

$REPLY 是 read 命令的一个特殊变量。当使用 read 命令且没有明确指定变量名时,用户输入的内容会自动存储到 $REPLY 变量中。示例如下:

echo "请输入任意内容:"
read    # 未指定变量名
echo "你输入的内容是:$REPLY"

运行结果:

请输入任意内容:
2
你输入的内容是:2

PS3:交互式菜单的提示符

  • 作用PS3 是 shell 的一个特殊环境变量,专门用于 select 命令的交互式菜单。当使用 select 命令创建菜单时,PS3 的值会作为菜单的提示符显示出来,提示用户进行选择。
  • 默认值:如果没有对 PS3 进行设置,它的默认值是 #? 
PS3="请选择操作(输入数字):"
select item in "查看文件" "创建文件" "退出"; do
    case $REPLY in  # $REPLY 存储用户输入的数字
        1) ls ;;
        2) touch new_file.txt ;;
        3) exit ;;
        *) echo "无效选择" ;;
    esac
done

运行结果:

1) 查看文件
2) 创建文件
3) 退出
请选择操作(输入数字):

select 选择语句菜单创建

优点

  • 内置菜单:无需手动编写编号,自动生成数字选项(1, 2, 3…)。
  • 简洁易用:只需定义选项和处理逻辑,无需额外循环。
  • 自动处理输入:用户输入会自动存储在 $REPLY 中,无需额外变量。

缺点

  • 样式固定:菜单格式固定(数字 + 选项),难以自定义样式。
  • 只能输入数字:用户必须输入数字选项,输入非数字会提示错误。
  • 无限循环:不输入有效选项会一直循环,需手动退出(如 exit)。

适用场景:简单的交互式菜单,无需复杂样式或输入验证。

# 选择mysql 版本
#!/bin/bash
PS3="Select a number: "
while true; do
select mysql_version in 5.1 5.6 quit;
 do
  case $mysql_version in
  5.1)
    echo "mysql 5.1"
      break
      ;;
  5.6)
    echo "mysql 5.6"
       break
       ;;
  quit)
    exit
    ;;
  *)
    echo "Input error, Please enter again!"
      break
esac
 done
done

for循环+数组菜单创建

优点

  • 动态生成菜单:通过数组管理选项,便于添加或删除菜单项。
  • 自定义样式:可自由控制菜单格式(如添加前缀、颜色)。
  • 代码简洁:选项较多时比 echo 更易维护。

缺点

  • 手动计算索引:数组索引从 0 开始,需用 $((i+1)) 转换为 1-based 编号。
  • 输入验证复杂:需额外代码处理非数字输入或越界选择。
  • 缺乏内置循环:如需重复显示菜单,需手动添加循环。

适用场景:选项动态变化或需要批量生成菜单的场景。

#!/bin/bash
echo "欢迎使用菜单选项"
echo "请选择你要执行的操作:"
options=("选项1" "选项2" "选项3" "退出")
# 循环显示菜单选项
for option in "${options[@]}"
do
    echo "$option"
done
# 读取用户的选择
read -p "请选择一个选项 (输入选项编号): " choice
# 判断用户的选择
if [[ $choice -eq 1 ]]; then
    echo "你选择了选项1"
    # 在这里添加选项1的逻辑代码
elif [[ $choice -eq 2 ]]; then
    echo "你选择了选项2"
    # 在这里添加选项2的逻辑代码
elif [[ $choice -eq 3 ]]; then
    echo "你选择了选项3"
    # 在这里添加选项3的逻辑代码
elif [[ $choice -eq 4 ]]; then
    echo "你选择了退出"
    # 在这里添加退出的逻辑代码
else
    echo "无效的选择,请重新输入"
fi

结果如下:

欢迎使用菜单选项
请选择你要执行的操作:
选项1
选项2
选项3
退出
请选择一个选项 (输入选项编号): 

使用 echo 命令和 read 命令

优点

  • 高度自定义:这种方式通常结合case语句来处理用户的输入,可以清晰地列出所有选项,并且每个选项后都可以跟随特定的命令。例如,可以通过read命令读取用户的输入,然后使用case语句判断用户输入的值,并执行相应的命令,可以自由控制菜单样式(如添加颜色、图标)。
  • 灵活输入:可结合 read 选项(如 -s 隐藏输入、-n1 限制单字符)。
  • 单次交互:执行一次后退出,无需手动终止循环。

缺点

  • 手动管理编号:需自行维护选项编号与逻辑的对应关系。
  • 代码冗余:选项较多时,echo 和 case 会变得冗长。
  • 缺乏循环:如需重复显示菜单,需手动添加循环结构。

适用场景:需要自定义菜单样式或复杂输入验证的场景。

#!/bin/sh
while true; do
    echo "1. 目录内容"
    echo "2. 切换目录"
    echo "3. 创建文件"
    echo "4. 编辑文件"
    echo "5. 删除文件"
    echo "6. 退出菜单"
    
    read -p "请输入选择(1-6): " input
    case $input in
        1) ls ;;
        2) echo "Enter target directory: "
           read dir
           cd $dir ;;
        3) echo "Enter a file name: "
           read file
           touch $file ;;
        4) echo "Enter a file name: "
           read file
           vi $file ;;
        5) echo "Enter a file name: "
           read file
           rm $file ;;
        6) break ;;
        *) echo "错误选择!" ;;
    esac
done

几种方式对比结果

特性selectecho + readfor + 数组
代码复杂度低(内置循环和编号)中(手动编写编号和循环)中(需处理数组索引)
样式自定义差(固定格式)好(完全自定义)好(可结合数组动态生成)
输入验证简单(只能输入数字)灵活(可结合 read 选项)灵活(需手动处理)
选项动态性差(需修改代码)差(需逐个修改 echo好(通过数组管理)
循环控制自动循环(需手动退出)需手动添加循环需手动添加循环
适用场景简单菜单(如脚本内部选项)复杂样式或单次交互选项动态变化的场景

函数数组

函数

Shell 函数很简单,函数名后跟双括号,再跟双大括号。通过函数名直接调用,不加小括号 # 函数语法 func() { command1 command1 …… } func # 直接调用函数名 #实例 #!/bin/bash func() { VAR=$((1+1)) return $VAR echo “This is a function.” } func echo $?

数组

数组是相同类型的元素按一定顺序排列的集合。 格式:array=(元素 1 元素 2 元素 3 …) 用小括号初始化数组,元素之间用空格分隔。 定义方法 1:初始化数组 array=(a b c) 定义方法 2:新建数组并添加元素 array[下标]=元素 定义方法 3:将命令输出作为数组元素array=($(command))

遍历数组

#方法 1:
#!/bin/bash
IP=(10.0.0.1 10.0.0.2 10.0.0.3)
for ((i=0;i&lt;${#IP[*]};i++)); do
echo ${IP[$i]}
done
# bash test.sh
10.0.0.1
10.0.0.2
10.0.0.3
#方法 2:
#!/bin/bash
IP=(10.0.0.1 10.0.0.2 10.0.0.3)
for   IP   in ${IP[*]}; do
echo $IP
done</code></pre>

© 版权声明
THE END
点赞9 分享