脚本常用命令

exec

在 Shell 脚本中,exec 是一个内置命令,用于执行外部命令或程序。exec 命令的作用是用指定的命令替换当前的 Shell 进程,从而使新的命令成为新的进程的唯一进程。换句话说,exec 命令会替换当前的 Shell 进程,而不是创建一个新的子进程来执行命令。

exec 命令的语法如下:

exec command [arguments]

其中,command 是要执行的外部命令或程序,arguments 是传递给该命令的参数。

以下是一些 exec 命令的示例:

  • 执行外部命令:
exec ls -l

上述命令将执行 ls -l 命令,并将当前的 Shell 进程替换为该命令的输出。

  • 执行脚本:
exec ./script.sh

上述命令将执行名为 script.sh 的脚本,并用该脚本的进程替换当前的 Shell 进程。

  • 执行 Shell 内部命令:
exec echo "Hello, world!"

上述命令将执行内部命令 echo,并将当前的 Shell 进程替换为该命令的输出。

请注意,当使用 exec 命令时,原始 Shell 进程将被完全替换,因此任何位于 exec 命令后面的代码都不会被执行。这是因为 exec 命令会直接加载新的命令或程序,取代当前进程的代码和环境。

在使用 exec 命令时要特别小心,确保您知道您正在执行的命令及其参数,以免产生意外的结果。

exec “$@”

在Shell脚本中,exec "$@" 是一种常见的用法,用于将脚本接收到的命令行参数传递给另一个命令或程序。

"$@" 是一个特殊的变量,用于展开所有的命令行参数。当脚本接收到命令行参数时,"$@" 将展开为一个参数列表,每个参数都是独立的字符串。

exec "$@" 的作用是将脚本的当前进程替换为命令行参数所表示的命令或程序,并将这些参数传递给该命令或程序。这样做的效果是,脚本自身的进程被替换,而不是在子进程中执行命令。

以下是一个示例,展示了 exec "$@" 的用法:

#!/bin/bash

## 脚本接收命令行参数并将其传递给另一个命令
exec echo "$@"

假设上述脚本命名为 script.sh。执行该脚本时,命令行参数将传递给 echo 命令,并且脚本自身的进程将被 echo 命令替换。

例如,执行以下命令:

./script.sh Hello World

将会执行 echo Hello World,并输出 Hello World

这种用法在需要将脚本的命令行参数传递给其他命令或程序时非常有用。它允许脚本在不创建额外子进程的情况下直接执行其他命令,并继承脚本的输入输出流和环境。

set

set 命令是用于设置和修改 shell 环境变量的命令。它具有多种用法和选项,常用于 shell 脚本中进行参数设置和控制。

下面是一些 set 命令的常见用法和选项:

  1. 设置变量:set VAR=value 可以将变量 VAR 设置为指定的值 value。这样可以在脚本中定义和使用变量。
  2. 显示变量:set 命令单独使用时,可以显示当前的所有环境变量。
  3. 设置选项:set -option 可以打开或关闭 shell 的选项。一些常用的选项包括:
    • set -eset -o errexit:当命令返回非零状态码时,立即退出脚本。
    • set -uset -o nounset:在使用未定义的变量时,产生错误并退出脚本。
    • set -xset -o xtrace:显示脚本中每个命令执行的详细信息,用于调试。
  4. 解析参数:set -- arg1 arg2 arg3 可以将后续的参数作为位置参数供脚本使用。-- 用于标识后续参数是位置参数,即使参数中包含特殊字符。
  5. 位置参数:在 shell 脚本中,可以通过 $1$2$3 等来引用位置参数,其中 $1 表示第一个参数,$2 表示第二个参数,以此类推。
  6. 脚本退出状态码:可以通过 set 命令设置脚本的退出状态码,使用 set -e 可以让脚本在遇到错误时立即退出,并返回一个非零的状态码。

这些只是 set 命令的一些常见用法和选项,还有其他用法和选项可供探索。你可以使用 man set 命令来查看更详细的文档。

set 命令可以在一定程度上增加 shell 脚本的安全性,但它并不能完全解决所有安全问题。以下是一些使用 set 命令可以提高脚本安全性的方面:

  1. 错误处理:通过使用 set -eset -o errexit 选项,脚本在任何命令返回非零状态码时会立即退出。这样可以防止错误继续执行导致潜在的安全风险。
  2. 未定义变量检测:通过使用 set -uset -o nounset 选项,脚本在使用未定义的变量时会产生错误并退出。这有助于避免未经预料的行为,例如误用或未初始化的变量。
  3. 调试信息:通过使用 set -xset -o xtrace 选项,脚本会显示每个命令的详细执行信息。这对于调试脚本非常有用,可以帮助发现潜在的安全问题和逻辑错误。
  4. 参数处理:通过使用 set -- 命令,可以确保后续参数被视为位置参数,即使参数中包含特殊字符。这有助于防止参数注入攻击。

虽然 set 命令提供了一些安全增强功能,但它并不能完全解决所有安全问题。编写安全的 shell 脚本还需要遵循其他最佳实践,如输入验证、避免代码注入、使用安全的文件权限等。此外,还应定期更新和维护脚本,以及关注操作系统和 shell 的安全补丁和更新。

expect

  • 实现面交互执行,来自expect包

范例:镶嵌到shell脚本中执行

范例1

[root@8 ~]# cat push_ssh_key_expect.sh
#!/bin/bash
## #********************************************************************
#Author:            xiangzheng
#QQ:                767483070
#Date:              2022-04-20
#FileName:         push_ssh_key_expect.sh
#URL:               https://www.xiangzheng.vip
#Email:             rootroot25@163.com
#Description:      批量推送公钥到远程主机以实现ssh免密登录
#Copyright (C):     2022 All rights reserved
#********************************************************************

#远程主机IP
IP_LIST="
10.0.0.100
10.0.0.101
10.0.0.102
"

#远程主机密码
PASS="123"

#dpkg -L expect &> /dev/null || apt -y install expect &> /dev/null
rpm -q expect &> /dev/null || yum -y install expect &> /dev/null

if [ -f ~/.ssh/id_rsa.pub ];then
    #公钥存在则退出
    echo "ssh key alreadey exist exit" && exit 3
else
    #生成公钥私钥对,默认不为私钥创建密码,需要创建密码的话在-P后面的""中指定密码
    ssh-keygen -f /root/.ssh/id_rsa -t rsa -P "" && echo "ssh key created succeed"
fi

#使用 for 以IP变量的方式推送到远程主机组
for IP in ${IP_LIST};do
expect <<EOF #嵌入shell脚本中中的EOF来进行面交互执行操作
set timeout 20 #设置超时时间为6秒,下面的代码需在6秒钟内完成,如果超过,则退出。用来防止ssh远程主机网络不可达时卡住及在远程主机执行命令宕住
spawn ssh-copy-id -i /root/.ssh/id_rsa.pub root@${IP} #spawn后面跟需要执行的命令
expect { 
    "*yes/no*" { send "yes\n";exp_continue } #exp_continue 继续 下面还有需要执行的
    "*password*" { send "${PASS}\n" }
}
expect eof #spawn进程结束后会向expect发送eof,接收到eof代表该进程结束
EOF
echo ${IP} push succeed
done

范例2

#!/bin/bash
...
expect<<- EOF
spawn ./easyrsa gen-req ${NAME} 
expect {
    "Enter*" { send "${PASS}\r"; exp_continue }
    "Verifying*" { send "${PASS}\r"; exp_continue }
    "Common" { send "\r"; }
}
expect eof
EOF
...

范例:纯expect格式执行

  • 注意:要加执行权限后写绝对路径执行,不能使用bash执行
#!/usr/bin/expect
spawn ssh root@10.0.0.101
expect { 
    "*yes/no*" { send "yes\r"; exp_continue }
    "*password:" { send "123\r" } 
}
expect eof
interact

小技巧

## 获取脚本当前所在的目录
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

这段代码的目的是获取当前脚本所在的目录路径。它使用了一些 Bash 命令和参数扩展来实现这一点。让我们逐步详细解释一下每个部分的作用:

  1. $( ... )

    • 这是命令替换,意味着括号中的命令将被执行,结果将替换这个命令。换句话说,整个 $( ... ) 部分将被它包含的命令的输出结果替换。
  2. ${BASH_SOURCE[0]}

    • 这是一个 Bash 特殊变量数组 BASH_SOURCE 的第一个元素(索引为0)。BASH_SOURCE 数组包含当前脚本或被包含脚本的路径。
    • 在脚本运行时,${BASH_SOURCE[0]} 表示当前脚本的文件名(包括路径)。
  3. dirname "${BASH_SOURCE[0]}"

    • dirname 是一个命令,它去掉文件路径中的文件名部分,只返回目录路径。例如,如果 ${BASH_SOURCE[0]}/home/user/script.sh,那么 dirname "${BASH_SOURCE[0]}" 会返回 /home/user
    • 这个命令的作用是获取当前脚本文件所在的目录路径。
  4. cd "$( dirname "${BASH_SOURCE[0]}" )"

    • 这条命令使用 cd 命令(改变目录)进入当前脚本所在的目录。dirname "${BASH_SOURCE[0]}" 返回的结果被命令替换机制替换,成为 cd /home/user 这样的形式。
    • cd 命令将当前工作目录改变为脚本所在的目录。
  5. && pwd

    • && 是一个逻辑与运算符,它的作用是前一个命令成功时才执行后一个命令。
    • pwd 是一个命令,它返回当前工作目录的绝对路径。
    • 结合 cdpwd,这部分的作用是首先进入脚本所在的目录,然后获取并返回这个目录的绝对路径。
  6. DIR="..."

    • 将整个命令替换的结果(即脚本所在的绝对路径)赋值给变量 DIR

综上所述,这段代码的作用是获取当前脚本所在的绝对路径,并将其赋值给变量 DIR。具体步骤如下:

  1. 获取当前脚本的路径。
  2. 去掉脚本文件名,得到其所在的目录路径。
  3. 切换到这个目录。
  4. 获取并返回这个目录的绝对路径。
  5. 将路径赋值给 DIR 变量。

这样,无论你从哪里运行这个脚本,DIR 变量总是包含该脚本所在目录的绝对路径。