Shell 学习记录

发布于 2023-04-03  450 次阅读


Shell 学习

因为希望做一个 stable-diffusion 一键安装脚本,所以开始学习 shell ,记录下自己学习过程。同时发现想在服务器上实现一次跑完所有文件,用 bash 也是最方便的。今天先学了最简单的操作,不追求鲁棒性的话,基本上已经够用了(这里顺便吐槽下鲁棒性,是哪个大帅逼发明的翻译啊,老老实实翻译稳健性它不好吗。。。)

第一个 Shell 脚本

#!/bin/bash
echo "hello world!"

#! 是一个约定标记,告诉系统这个脚本要什么解释器来执行,即用哪一种 Shell。

echo 用于向窗口输出文本。

Shell 变量

  • 定义变量时,变量名不加 $ ( 这和 PHP 不一样,PHP 中变量需要 )。命名规则和其他语言相同。
your_name="Nobody"

注意:变量名和等号之间不能有空格

  • 除了显示赋值以外,还可以用语句给变量赋值,如:

    for file in 'ls /etc'
    #or use this
    for file in $(ls /etc)
  • 使用变量

    your_name="nobody"
    echo $your_name
    echo ${your_name}

    以上两种方法都可以,但个人比较喜欢第二种加花括号的写法,首先这更符合 {这是一个变量} 的直觉,其次是适用性更广,比如:

    for skill in Ada Coffe Action Java; do
    echo "I am good at ${skill}Script"
    done

    在上面那种场景下就必须要加花括号了。

  • 设置变量为只读(相当于设置 const 变量)

    #!/bin/bash
    
    myUrl="https://jerryhouse.cn"
    readonly myUrl
    myUrl="https://www.jerryhouse.cn"

    运行上述脚本会报错

    /home/ljy/shell_learn/hello_world.sh: 行 24: myUrl: 只读变量
  • 删除变量

    unset variable_name
  • Shell 字符串

    字符串是shell编程中最常用最有用的数据类型(除了数字和字符串,也没啥其它类型好用了),字符串可以用单引号,也可以用双引号,也可以不用引号。

    1. 单引号
    str='this is a string'

    单引号字符串的限制:

    • 单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的;

    • 单引号字串中不能出现单独一个的单引号(对单引号使用转义符后也不行),但可成对出现,作为字符串拼接使用。

    1. 双引号

      your_name="nobody"
      str="Hello, I know you are \"$your_name\"! \n"
      echo -e $str # -e 开启转义 

      双引号的优势:

      • 双引号里可以有变量
      • 双引号里可以出现转义字符
    2. 字符串拼接

      your_name="nobody"
      # 双引号拼接
      greeting="hello, "$your_name"! "
      greeting_1="hello, ${your_name}! "
      echo ${greeting} ${greeting_1}
      
      # 单引号拼接
      greeting_2='hello, '$your_name'! '
      greeting_3='hello, ${your_name}! '
      echo $greeting_2 $greeting_3
    3. 获取字符串长度

      string="abcd"
      echo ${#string}   # 输出 4

      变量为数组时,${#string} 等价于 ${#string[0]}:

      string="abcd"
      echo ${#string[0]}   # 输出 4
    4. 提取子字符串

      以下实例从字符串第 3 个字符开始截取 4 个字符:

      string="I'm nobody"
      echo ${string:2:4} # 输出 m no
    5. 查找子字符串

      查找字符 io 的位置(哪个字母先出现就计算哪个):

      string="I'm nobody"
      echo expr index "$string" io # 输出 6

      注意: 以上脚本中 ` 是反引号,而不是单引号 ',不要看错了哦。

    6. Shell 数组

      经过测试,这个数组支持 map<string,string> 类型

      bash支持一维数组(不支持多维数组),并且没有限定数组的大小。类似于 C 语言,数组元素的下标由 0 开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于 0

      在 Shell 中,用括号来表示数组,数组元素用"空格"符号分割开。定义数组的一般形式为:

      数组名=(值1 值2 ... 值n)

      例如:

      array_name=(value0 value1 value2 value3)

      array_name=(
      value0
      value1
      value2
      value3
      )

      还可以单独定义数组的各个分量:

      array_name[0]=value0
      array_name[1]=value1
      array_name[n]=valuen

      可以不使用连续的下标,而且下标的范围没有限制。

    7. 读取数组

      读取数组元素值的一般格式:

      ${数组名[下标]}

      例如:

      valuen=${array_name[n]}

      使用 @ 符号可以获取数组中的所有元素,例如:

      echo ${array_name[@]}
    8. 获取数组的长度

      # 取得数组元素的个数
      length=${#array_name[@]}
      # 或者
      length=${#array_name[*]}
      # 取得数组单个元素的长度
      lengthn=${#array_name[n]}
    9. Shell 注释

      以 # 开头的行就是注释,会被解释器忽略。

      通过每一行加一个 # 号设置多行注释,像这样:

      #--------------------------------------------
      # 这是一个注释
      # author:ljy
      # site:www.jerryhouse.com
      # moto:总是追求不平凡,又怎能享受平凡的快乐呢
      #--------------------------------------------
      ##### 用户配置区 开始 #####
      #
      #
      # 这里可以添加脚本描述信息
      #
      #
      ##### 用户配置区 结束  #####

      多行注释:

      :<

Shell 传递参数

向脚本传递三个参数,并分别输出,其中 $0 为执行的文件名(包含文件路径):

#!/bin/bash
echo "Shell 传递参数实例!";
echo "执行的文件名:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";

运行 ./hello_world.sh first second third 输出:

Shell 传递参数实例!
执行的文件名:./hello_world.sh
第一个参数为:first
第二个参数为:second
第三个参数为:third

运行 ~/文档/shell_learn/hello_world.sh first second third 输出:

Shell 传递参数实例!
执行的文件名:/home/ljy/文档/shell_learn/hello_world.sh
第一个参数为:first
第二个参数为:second
第三个参数为:third

PS: 学到这里,我想起来了我的右键菜单里的 Open in vscode 脚本,里面的内容如下:

#!/bin/bash
code ./$1

明白了作用就是把选中的文件或文件夹作为参数传给这个命令。 $0 的作用就是返回正在执行的脚本文件的文件名(包含路径)。例如我在终端里直接输入命令

echo $0

得到的输出为:

bash

因为终端里用的环境变量 bash 在执行这条语句。
于是我仿照这段代码写了 Open in typora :

#!/bin/bash
typora $1

事实上,./ 可加可不加,因为这条命令会默认从当前工作目录去寻找这个文件,当你右键点击要打开的文件一定是在这个目录里的。

一些处理参数的特殊字符:

参数处理 说明
$# 传递到脚本的参数个数
$* 以一个单字符串显示所有向脚本传递的参数。如"\$*"用「"」括起来的情况、以"\$1 $2 … $n"的形式输出所有参数。
$$ 脚本运行的进程号
$! 后台运行的最后一个进程号(暂时没搞懂是啥意思,也许是这个脚本会后台运行程序,返回最后一个程序后台运行时的进程号)
$@ 与\$*相同,但是使用时加引号,并在引号中返回每个参数。
如"\$@"用「"」括起来的情况、以"\$1" "\$2" … "\$n" 的形式输出所有参数。
$- 显示Shell使用的当前选项,与set命令功能相同(暂时没搞懂,终端中使用后输出为 himBHs
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

\$* 与 \$@ 区别:

  • 相同点:都是引用所有参数。
  • 不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,,则 " * " 等价于 "1 2 3"(传递了一个参数),而 "@" 等价于 "1" "2" "3"(传递了三个参数)。
#!/bin/bash

echo "-- \$* 演示 ---"
for i in "$*"; do
    echo $i
done

echo "-- \$@ 演示 ---"
for i in "$@"; do
    echo i
done

执行 :

./hello_world.sh first second third four five

输出:

-- $* 演示 ---
first second third four five
-- $@ 演示 ---
first
second
third
four
five

整天不想事儿,就想着干饭