26、Bash脚本实用示例与操作指南

Bash脚本实用示例与操作指南

1. 权威DNS查询脚本

1.1 脚本概述

权威DNS查询脚本借助 dig 命令开展DNS查询,能够绕过本地DNS缓存服务器。该脚本的独特之处在于,它依据自身名称来明确要查询的DNS记录类型。例如,若脚本名为 a ,则查询DNS A记录;若名为 soa ,则查询DNS SOA记录。特殊的 ptr 名称可将IPv4地址转换为合适的 in-addr.arpa 形式进行实际查询。

1.2 脚本代码

#!/bin/bash
#----------------------------------------------------------------
# Copyright © 2006 - Philip Howard - All rights reserved
#
# script  a, aaaa, cname, mx, ns, ptr, soa, txt
#
# purpose Perform direct DNS lookups for authoritative DNS
#         data. This lookup bypasses the local DNS cache
#         server.
#
# syntax  a       [ names ... ]
#         aaaa    [ names ... ]
#         any     [ names ... ]
#         cname   [ names ... ]
#         mx      [ names ... ]
#         ns      [ names ... ]
#         ptr     [ names ... ]
#         soa     [ names ... ]
#         txt     [ names ... ]
#
# author  Philip Howard
#----------------------------------------------------------------
# For use with ptr query.
function inaddr {
    awk -F. '{print $4 "." $3 "." $2 "." $1 ".in-addr.arpa.";}'
}

query_type=$( exec basename "${0}" )
# Get and query for each host.
for hostname in "$@" ; do
    if [[ "${query_type}" == ptr ]] ; then
    # A typical scripting trick: when a case can begin
    # with a numeral, place a dummy character such as x in
    # front because the case syntax expects an alphanumeric
    # character.
    case "x${hostname}y" in
        ( x[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*y )
        hostname=$( echo "${hostname}" | inaddr )
        ;;
        ( * )
        ;;
    esac
    fi
    # Execute the query.
    dig +trace +noall +answer "${query_type}" "${hostname}" | \
        egrep "^${hostname}"
done
exit

1.3 操作步骤

  1. 复制上述代码到一个文件中,例如 dns_lookup.sh
  2. 根据需要查询的DNS记录类型,创建该脚本的副本或硬链接、符号链接,如 ln -s dns_lookup.sh a 创建查询A记录的链接。
  3. 执行脚本,传入要查询的主机名,如 ./a example.com

2. 跨Shell会话文件传输脚本

2.1 脚本概述

此脚本可借助每个系统上的shell会话,将文件或包含所有子目录的文件目录从一个系统传至另一个系统。它通过在前台创建 rsync 守护进程来发送指定的文件或目录,并展示几种不同形式的 rsync 命令,用于在接收系统上接收文件或目录。

2.2 脚本代码

#!/bin/bash
#----------------------------------------------------------------
# Copyright © 2006 - Philip Howard - All rights reserved
#
# script  rsend
#
# purpose To start an rsync daemon in the shell foreground
#         to send a specified directory or file when
#         retrieved using one of the rsync command lines
#         shown, by pasting it in a shell session on another
#         host.
#
# usage   rsend  [options]  directory | file
#
# options -c include checksum in the rsync command lines
#         -d change daemon to the specified directory
#         -n include dryrun in the rsync command lines
#         -p use the specified port number, else random
#         -s include sparse in the rsync command lines
#         -u user to run as, if started as root
#         -v show extra information
#
# author  Philip Howard
#----------------------------------------------------------------
umask 022
hostname=$( exec hostname -f )
whoami=$( exec whoami )
uid="${whoami}"
#----------------------------------------------------------------
# Set defaults.
#----------------------------------------------------------------
checksum=""
delete=""
delmsg=""
dryrun=""
padding="-------"
port=""
sparse=""
verbose=""
bar1="--------------------------"
bar1="#${bar1}${bar1}${bar1}"
bar2="##########################"
bar2="#${bar2}${bar2}${bar2}"
#----------------------------------------------------------------
# Include paths for ifconfig.
#----------------------------------------------------------------
export PATH="${PATH}:/usr/sbin:/sbin"
#----------------------------------------------------------------
# Scan options.
#----------------------------------------------------------------
while [[ $# -gt 0 && "x${1:0:1}" = "x-" ]]; do
    case "x${1}" in
    ( x-c | x--checksum )
    checksum="c"
    ;;
    ( x--delete )
    delete=" --delete"
    delmsg="/delete"
    padding=""
    ;;
    ( x-d | x--directory )
    shift
    cd "${1}" || exit 1
    ;;
    ( x--directory=* )
    cd "${1:12}" || exit 1
    ;;
    ( x-n | x--dry-run )
    dryrun="n"
    ;;
    ( x-p | x--port )
    shift
    port="${1}"
    ;;
    ( x--port=* )
    port="${1:7}"
    ;;
    ( x-s | x--sparse )
    sparse="S"
    ;;
    ( x-u | x--user )
    shift
    uid="${1}"
    ;;
    ( x--user=* )
    uid="${1:7}"
    ;;
    ( x-v | x--verbose )
    verbose=1
    ;;
    esac
    shift
done
#----------------------------------------------------------------
# Get a random number for a port.
#----------------------------------------------------------------
if [[ -z "${port}" || "${port}" = 0 || "${port}" = . ]]; then
    port=$( dd if=/dev/urandom ibs=2 obs=2 count=1 2>/dev/null \
       | od -An -tu2 | tr -d ' ' )
    port=$[ $port % 16384 ]
    port=$[ $port + 12288 ]
fi
#----------------------------------------------------------------
# Make up names for temporary files to be used.
#----------------------------------------------------------------
conffile="/tmp/rsync-${whoami}-${port}-$$.conf"
lockfile="/tmp/rsync-${whoami}-${port}-$$.lock"
#----------------------------------------------------------------
# This function adds quotes to strings that need them.
# Add single quotes if it has one of these: space $ " `
# Add double quotes if it has one of these: '
# Note: not all combinations will work.
#----------------------------------------------------------------
function strquote {
    local str
    str=$( echo "${1}" | tr -d ' $"`' )
    if [[ "${str}" != "${1}" ]]; then
    echo "'${1}'"
    return
    fi
    str=$( echo "${1}" | tr -d "'" )
    if [[ "${str}" != "${1}" ]]; then
    echo '"'"${1}"'"'
    return
    fi
    echo "${1}"
    return 0
}
#----------------------------------------------------------------
# Only one name can be handled.
#----------------------------------------------------------------
if [[ $# -gt 1 ]]; then
    echo "Only one name (directory or file)" 1>&2
    exit 1
elif [[ $# -eq 1 ]]; then
    name="${1}"
else
    name=$( exec pwd )
fi
#----------------------------------------------------------------
# Set up a temporary config file.
#
# Arguments:
#    $1    Directory transferred, or where transfer is starting
#    $2    Not used (AO: Should be removed)
#    $3    File transferred (if single file specified)
#----------------------------------------------------------------
function configout {
    echo "lock file = ${lockfile}"
    echo "log file = /dev/stderr"
    echo "use chroot = false"
    echo "max connections = 32"
    echo "socket options = SO_KEEPALIVE"
    echo "list = yes"
    echo "[.]"
    echo "path = ${1}"
    echo "read only = yes"
    echo "uid = ${uid}"
    echo "comment = ${2}"
    if [[ -n "${3}" ]]; then
    echo "include = **/${3}"
    echo "exclude = **"
    fi
}
#----------------------------------------------------------------
# Get directory and file.
#----------------------------------------------------------------
if [[ ! -e "${name}" ]]; then
    echo "does not exist:" $( strquote "${name}" ) 1>&2
    exit 1
elif [[ -d "${name}" ]]; then
    p=$( exec dirname "${name}" )
    b=$( exec basename "${name}" )
    d="${name}"
    f=""
    r=$( cd "${name}" && exec pwd )
    announce="${d}"
    rsyncopt="-a${checksum}${dryrun}H${sparse}vz${delete}"
    configout "${d}/." "directory:${d}/" >"${conffile}"
elif [[ -f "${name}" ]]; then
    p=$( exec dirname "${name}" )
    b=$( exec basename "${name}" )
    d="${p}"
    f="${b}"
    r=$( cd "${p}" && exec pwd )
    r="${r}/${b}"
    announce="${d}/${f}"
    rsyncopt="-a${checksum}${dryrun}${sparse}vz"
    configout "${d}/." "file:${d}/${f}" >"${conffile}"
elif [[ -L "${name}" ]]; then
    p=$( exec dirname "${name}" )
    b=$( exec basename "${name}" )
    d="${p}"
    f="${b}"
    r=$( cd "${p}" && exec pwd )
    r="${r}/${b}"
    announce="${d}/${f}"
    rsyncopt="-a${checksum}v"
    configout "${d}/." "symlink:${d}/${f}" "${f}" >"${conffile}"
fi
#----------------------------------------------------------------
# Show config file if verbose is requested.
#----------------------------------------------------------------
if [[ -n "${verbose}" ]]; then
    echo "${bar2}"
    ls -ld "${conffile}"
    echo "${bar2}"
    cat "${conffile}"
fi
#----------------------------------------------------------------
# This function outputs example receive commands.
#----------------------------------------------------------------
function showrsync {
    echo -n "rsync ${rsyncopt} "
    if [[ -n "${oldfmt}" ]]; then
    echo "--port=${port}" $( strquote "${1}::${2}" ) $( strquote "${3}" )
    else
    echo $( strquote "rsync://${1}:${port}/${2}" ) $( strquote "${3}" )
    fi
    return
}
#----------------------------------------------------------------
# These functions show rsync commands for hostname and IP address.
#----------------------------------------------------------------
function getip {
    case $( exec uname -s ) in
    ( SunOS )
    netstat -i -n | awk '{print $4;}'
    ;;
    ( Linux )
    ifconfig -a | awk '{if($1=="inet")print substr($2,6);}'
    ;;
    ( * )
    netstat -i -n | awk '{print $4;}'
    ;;
    esac
    return
}
function ipaddr {
    getip                    \
    | egrep '^[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*$'    \
    | egrep -v '^0\.|^127\.'            \
    | head -2                    \
    | while read ipv4 more ; do
    showrsync "${ipv4}" "$@"
    done
    return
}
function showcmd {
    ipaddr "${2}" "${3}"
    showrsync "${1}" "${2}" "${3}"
    return
}
#----------------------------------------------------------------
# Announce the shell commands to receive this data.
#----------------------------------------------------------------
echo "${bar2}"
echo "# sending ${announce}"
echo "# paste ONE of these commands in a remote shell to receive"
if [[ -d "${name}" ]]; then
    echo "${bar1}"
    showcmd "${hostname}" . .
    echo "${bar1}"
    showcmd "${hostname}" . "${b}"
    if [[ "${d}" != "${b}" && "${d}" != "${r}" ]]; then
    echo "${bar1}"
    showcmd "${hostname}" . "${d}"
    fi
    echo "${bar1}"
    showcmd "${hostname}" . "${r}"
else
    echo "${bar1}"
    showcmd "${hostname}" "./${f}" "${b}"
    s=$( exec basename "${d}" )
    s="${s}/${f}"
    if [[ "${s}" != "${b}" ]]; then
    echo "${bar1}"
    showcmd "${hostname}" "./${f}" "${s}"
    fi
    if [[ "${name}" != "${b}" \
       && "${name}" != "${s}" \
       && "${name}" != "${r}" ]]; then
    echo "${bar1}"
    showcmd "${hostname}" "./${f}" "${name}"
    fi
    echo "${bar1}"
    showcmd "${hostname}" "./${f}" "${r}"
fi
echo "${bar1}"
echo "# press ^C here when done"
echo "${bar2}"
#----------------------------------------------------------------
# Start rsync in daemon mode.
#----------------------------------------------------------------
s="DONE"
trap 's="SIGINT ... DONE"' INT
trap 's="SIGTERM ... DONE"' TERM
rsync --daemon --no-detach "--config=${conffile}" "--port=${port}"
rm -f "${conffile}" "${lockfile}"
echo "${s}"

2.3 操作步骤

  1. 复制上述代码到一个文件中,例如 rsend.sh
  2. 在发送系统上运行脚本,可指定选项和要发送的文件或目录,如 ./rsend.sh -p 12345 /path/to/file
  3. 脚本会输出一些示例的 rsync 命令,根据能访问发送系统的IP地址或主机名,以及接收系统上文件或目录的存储目标位置,选择合适的命令。
  4. 将选中的命令复制到接收系统的shell中执行,完成文件或目录的接收。
  5. 传输完成后,在发送系统的shell窗口中按 Ctrl - C 停止守护进程。

2.4 注意事项

  • 该脚本无安全机制,任何能访问其监听地址和端口号的人都可获取传输的数据,因此请勿用于传输机密或敏感数据,可尝试使用 scp sftp
  • 确保发送系统为其使用的端口号开放网络访问,可使用 -p 选项指定端口号。若防火墙规则仅允许连接一个或几个端口,必须使用这些端口号。

3. SSH与Screen集成脚本

3.1 脚本概述

此脚本可通过一个命令建立SSH连接并启动一个命名的 screen 会话,在处理多个服务器时,能实现更快速的连接和断开操作。它的使用方式与 ssh 命令类似,但扩展了指定远程会话用户名和主机名的语法,还可包含会话名。

3.2 脚本代码

#!/usr/bin/env bash
#----------------------------------------------------------------
# Copyright © 2006 - Philip Howard - All rights reserved
#
# command ss (secure screen)
#
# purpose Establish a screen based background shell session
#         via secure shell communications.
#
# syntax  ss  [options]  session/username@hostname
#         ss  [options]  session@username@hostname
#         ss  [options]  username@hostname/session
#         ss  [options]  username@hostname  session
#
# options -h hostname
#         -h=hostname
#         -i identity
#         -i=identity
#         -l loginuser
#         -l=loginuser
#         -m Multi-display mode
#         -p portnum
#         -p=portnum
#         -s session
#         -s=session
#         -t Use tty allocation (default)
#         -T Do NOT use tty allocation
#         -4 Use IPv4 (default)
#         -6 Use IPv6
#         -46 | -64 Use either IPv6 or IPv4
#
# requirements The local system must have the OpenSSH package
#         installed. The remote system must have the
#         OpenSSH package installed and have the sshd
#         daemon running. It must also have the screen(1)
#         program installed. Configuring a .screenrc
#         file on each system is recommended.
#
# note    The environment variable SESSION_NAME will be set
#         in the session created under the screen command
#         for potential use by other scripts.
#
# author  Philip Howard
#----------------------------------------------------------------
whoami=$( exec whoami )
hostname=$( exec hostname )
h=""
i=( )
m=""
p=( )
s=''
t=( -t )
u="${whoami}"
v=( -4 )
#----------------------------------------------------------------
# Parse options and arguments.
#----------------------------------------------------------------
while [[ $# -gt 0 ]]; do
    case "x${1}" in
    ( x*/*@* )
    # Example: session1/lisa@centrhub
    u=$( echo "x${1}" | cut -d @ -f 1 )
    u="${u:1}"
    s=$( echo "x${u}" | cut -d / -f 2 )
    u=$( echo "x${u}" | cut -d / -f 1 )
    u="${u:1}"
    h=$( echo "x${1}" | cut -d @ -f 2 )
    shift
    break
    ;;
    ( x*@*/* )
    # Example: lisa@centrhub/session1
    u=$( echo "x${1}" | cut -d @ -f 1 )
    u="${u:1}"
    h=$( echo "x${1}" | cut -d @ -f 2 )
    s=$( echo "x${h}" | cut -d / -f 2 )
    h=$( echo "x${h}" | cut -d / -f 1 )
    h="${h:1}"
    shift
    break
    ;;
    ( x*@*@* )
    # Example: session1@lisa@centrhub
    s=$( echo "x${1}" | cut -d @ -f 1 )
    s="${s:1}"
    u=$( echo "x${1}" | cut -d @ -f 2 )
    h=$( echo "x${1}" | cut -d @ -f 3 )
    shift
    break
    ;;
    ( x*@* )
    # Example: lisa@centrhub
    u=$( echo "x${1}" | cut -d @ -f 1 )
    u="${u:1}"
    h=$( echo "x${1}" | cut -d @ -f 2 )
    # Next argument should be session name.
    shift
    if [[ $# -gt 0 ]]; then
        s="${1}"
        shift
    fi
    break
    ;;
    ( x-h=* )
    h="${1:3}"
    ;;
    ( x-h )
    shift
    h="${1}"
    ;;
    ( x-i=* )
    i="${1:3}"
    if [[ -z "${i}" ]]; then
        i=( )
    else
        i=( -i "${1:3}" )
    fi
    ;;
    ( x-i )
    shift
    i=( -i "${1}" )
    ;;
    ( x-l=* | x-u=* )
    u="${1:3}"
    ;;
    ( x-l | x-u )
    shift
    u="${1}"
    ;;
    ( x-m | x--multi )
    m=1
    ;;
    ( x-p=* )
    p="${1:3}"
    if [[ -z "${p}" ]]; then
        p=( )
    else
        p=( -p "${1:3}" )
    fi
    ;;
    ( x-p )
    shift
    p=( -p "${1}" )
    ;;
    ( x-s=* )
    s="${1:3}"
    ;;
    ( x-s )
    shift
    s="${1}"
    ;;
    ( x-t )
    t=( -t )
    ;;
    ( x-T )
    t=( )
    ;;
    ( x-4 )
    v=( -4 )
    ;;
    ( x-6 )
    v=( -6 )
    ;;
    ( x-46 | x-64 )
    v=( )
    ;;
    ( x-* )
    echo "Invalid option: '${1}'"
    die=1
    ;;
    ( * )
    echo "Invalid argument: '${1}'"
    die=1
    ;;
    esac
    shift
done
#----------------------------------------------------------------
# Make sure essential information is present.
#----------------------------------------------------------------
if [[ -z "${u}" ]]; then
    echo "User name is missing"
    die=1
fi
if [[ -z "${h}" ]]; then
    echo "Host name is missing"
    die=1
fi
[[ -z "${die}" ]] || exit 1
#----------------------------------------------------------------
# Run screen on the remote only if a session name is given.
#----------------------------------------------------------------
c=( ssh "${v[@]}" "${i[@]}" "${p[@]}" "${t[@]}" "${u}@${h}" )
if [[ -n "${s}" ]]; then
    o="-DR"
    [[ -n "${m}" ]] && o="-x"
    x="exec /usr/bin/env SESSION_NAME='${s}' screen ${o} '${s}'"
    c=( "${c[@]}" "${x}" )
fi
exec "${c[@]}"

3.3 操作步骤

  1. 复制上述代码到一个文件中,例如 ss.sh
  2. 执行脚本,根据需要指定选项、会话名、用户名和主机名,如 ./ss.sh session1/lisa@centrhub
  3. 若指定了会话名,脚本会在远程系统上启动一个 screen 会话;若未指定会话名,则以正常方式运行 ssh 命令。

3.4 注意事项

  • 本地系统必须安装OpenSSH包,远程系统必须安装OpenSSH包并运行 sshd 守护进程,同时还需安装 screen 程序,建议在每个系统上配置 .screenrc 文件。
  • 环境变量 SESSION_NAME 会在 screen 命令创建的会话中设置,可供其他脚本使用。

4. 总结

本文介绍了三个实用的Bash脚本,分别用于权威DNS查询、跨Shell会话文件传输以及SSH与Screen的集成。这些脚本能帮助用户更高效地完成系统管理任务,提高工作效率。在使用过程中,需注意各脚本的特点和注意事项,确保操作的安全性和正确性。

通过合理运用这些脚本,用户可以在日常系统管理中节省大量时间和精力,同时避免一些常见的错误和问题。希望这些脚本能为你的工作带来便利。

5. 脚本使用流程对比

脚本名称 复制代码到文件 运行脚本 后续操作 停止操作 注意事项
权威DNS查询脚本 复制代码到 dns_lookup.sh 根据需要创建副本或链接,传入主机名执行,如 ./a example.com
跨Shell会话文件传输脚本 复制代码到 rsend.sh 指定选项和文件或目录运行,如 ./rsend.sh -p 12345 /path/to/file 选择合适命令复制到接收系统执行 Ctrl - C 停止守护进程 无安全机制,注意端口开放
SSH与Screen集成脚本 复制代码到 ss.sh 指定选项、会话名、用户名和主机名执行,如 ./ss.sh session1/lisa@centrhub 确保系统安装相关程序,配置 .screenrc 文件

6. 脚本使用场景分析

6.1 权威DNS查询脚本

  • 场景 :当需要进行权威的DNS查询,绕过本地DNS缓存服务器获取准确的DNS记录时使用。
  • 优势 :可以根据脚本名称灵活查询不同类型的DNS记录,方便快捷。

6.2 跨Shell会话文件传输脚本

  • 场景 :需要在不同系统之间快速传输文件或目录,尤其是需要传输大量文件或包含子目录的情况。
  • 优势 :通过 rsync 守护进程实现高效传输,且无需在接收系统安装该脚本。

6.3 SSH与Screen集成脚本

  • 场景 :在管理多个服务器时,需要频繁建立SSH连接并启动 screen 会话,以保持会话的持续性。
  • 优势 :通过一个命令完成连接和会话启动,提高操作效率。

7. 脚本执行流程图

graph LR
    classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
    classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;

    A([开始]):::startend --> B{选择脚本类型}:::decision
    B -->|权威DNS查询脚本| C(复制代码到文件):::process
    B -->|跨Shell会话文件传输脚本| D(复制代码到文件):::process
    B -->|SSH与Screen集成脚本| E(复制代码到文件):::process
    C --> F(创建副本或链接):::process
    F --> G(传入主机名执行脚本):::process
    D --> H(指定选项和文件或目录运行脚本):::process
    H --> I(选择合适命令复制到接收系统执行):::process
    I --> J(按`Ctrl - C`停止守护进程):::process
    E --> K(指定选项、会话名、用户名和主机名执行脚本):::process
    G --> L([结束]):::startend
    J --> L
    K --> L

8. 进一步优化建议

8.1 权威DNS查询脚本

  • 可以添加错误处理机制,当查询失败时给出明确的错误提示。
  • 支持批量查询多个主机名,提高查询效率。

8.2 跨Shell会话文件传输脚本

  • 增加加密功能,提高传输数据的安全性。
  • 实现断点续传功能,避免因网络问题导致传输中断后需要重新开始。

8.3 SSH与Screen集成脚本

  • 支持更多的 screen 选项,满足不同用户的需求。
  • 提供自动重连功能,当SSH连接中断时自动重新连接。

9. 总结与展望

本文详细介绍了三个实用的Bash脚本,包括权威DNS查询脚本、跨Shell会话文件传输脚本以及SSH与Screen集成脚本。通过对脚本的概述、代码展示、操作步骤说明和注意事项提醒,帮助读者了解如何使用这些脚本来提高系统管理的效率。

在实际应用中,读者可以根据具体需求对脚本进行适当的修改和优化,以满足不同的场景。同时,随着技术的不断发展,这些脚本也可以进一步扩展功能,为系统管理工作带来更多的便利。

希望本文能为读者在系统管理方面提供有价值的参考,让大家在日常工作中能够更加轻松地应对各种挑战。未来,我们可以继续探索更多实用的脚本和技术,不断提升系统管理的水平。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值