Shell脚本实用技巧:文件重命名、Expect自动化与网络扫描
从FTP服务器下载文件
以下脚本演示如何无交互方式从FTP服务器下载指定文件:
#!/bin/bash
[ $# -ne 1 ] && { echo "用法:$0 文件路径"; exit 1; }
dir=$(dirname "$1")
fname=$(basename "$1")
ftp -n -v << EOF
open 192.168.1.10
user admin password
binary
cd "$dir"
get "$fname"
EOF
动态变量赋值方案
场景一:将字符串"4 5 6"映射为变量a4、a5、a6:
for val in 4 5 6; do
eval "var_$val=$val"
done
echo $var_4 $var_5 $var_6
场景二:解析参数如"192.168.1.1{1,2}"为独立变量:
num=0
for ip in $(eval echo "$@"); do
((num++))
eval "host_$num=$ip"
done
echo $host_1 $host_2
场景三:使用数组提取元素:
vals=(4 5 6)
var1=${vals[0]}
var2=${vals[1]}
var3=${vals[2]}
批量重命名HTML文件
现有文件:article_1.html、article_2.html、article_3.html → 改为bbs_*.html
方式一(基础for循环):
for file in *.html; do
newname="bbs_${file#*_}"
mv "$file" "$newname"
done
方式二(find查找):
find . -maxdepth 1 -name "*.html" -exec sh -c '
for f; do
mv "$f" "bbs_${f#*_}"
done
' _ {} +
方式三(rename命令):
rename 's/article_/bbs_/' *.html
统计HTML文件总大小
方法一(find + awk):
find . -name "*.html" -exec du -k {} + | awk '{s+=$1} END {print s}'
方法二(ls + for循环):
sum=0
for sz in $(ls -l *.html | awk '{print $5}'); do
((sum += sz))
done
echo $sum
Expect自动化SSH交互
Expect工具常用于自动应答交互式程序(如SSH),需提前安装:
方式一(内联Expect脚本):
user=root
passwd=123.com
host=192.168.1.120
expect << EOF
set timeout 30
spawn ssh $user@$host
expect {
"(yes/no)" { send "yes\r"; exp_continue }
"password:" { send "$passwd\r" }
}
expect "*@*" { send "$1\r"; exp_continue }
expect "*@*" { send "exit\r" }
expect eof
EOF
方式二(expect -c单行调用):
expect -c "
spawn ssh root@192.168.1.120
expect {
\"(yes/no)\" {send \"yes\\r\"; exp_continue}
\"password:\" {send \"123.com\\r\"; exp_continue}
\"root@*\" {send \"df -h\\rexit\\r\"; exp_continue}
}"
方式三(独立Expect脚本):自动化分发SSH密钥(Ed25519算法)
#!/bin/bash
PASSWD="xxxxxx"
SSH_USER="root"
install_expect() {
yum -y install expect >/dev/null 2>&1 || apt-get install -y expect >/dev/null 2>&1
}
gen_key() {
expect -c "
set timeout -1
spawn ssh-keygen -t ed25519
expect {
*\"Enter file*\" { send \"\\r\"; exp_continue }
*\"Enter passphrase*\" { send \"\\r\"; exp_continue }
*\"repeat*\" { send \"\\r\"; exp_continue }
eof { exit 0 }
}"
ssh-add
}
copy_key() {
expect -c "
set timeout -1
spawn ssh-copy-id -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $1
expect {
*\"password:\" { send \"$2\\r\"; exp_continue }
eof { exit 0 }
}"
}
install_expect
gen_key
for h in $(grep -v '^127.0.0\|^::' /etc/hosts | awk '{print $1}'); do
copy_key $h $PASSWD
done
批量更新服务器密钥
原始凭证文件(old_pass.txt)格式:IP 用户名 旧密码 端口(每行一项):
192.168.18.217 root 123456 22
192.168.18.218 root 123456 22
自动修改密码脚本:
OLD_INFO="old_pass.txt"
NEW_INFO="new_pass.txt"
> "$NEW_INFO"
while read ip user passwd port; do
[ -z "$ip" ] && continue
newpass=$(mkpasswd -l 8)
echo "$ip $user $newpass $port" >> "$NEW_INFO"
expect -c "
spawn ssh -p $port $user@$ip
set timeout 2
expect {
\"(yes/no)\" { send \"yes\\r\"; exp_continue }
\"password:\" { send \"$passwd\\r\"; exp_continue }
\"$user@*\" { send \"echo '$newpass' | passwd --stdin $user\\rexit\\r\"; exp_continue }
}"
done < "$OLD_INFO"
getopts解析命令行参数
示例:指定文件和行号打印内容:
#!/bin/bash
while getopts :f:n: opt; do
case $opt in
f) FILE="$OPTARG"; [ ! -f "$FILE" ] && { echo "文件不存在"; exit 1; };;
n) sed -n "${OPTARG}p" "$FILE";;
?) echo "用法:$0 -f 文件 -n 行号"; exit 1;;
esac
done
并发测试192.168.4.0/24网段存活主机
多进程方案(后台并行ping):
check_host() {
ping -c 2 -i 0.3 -W 1 "$1" &>/dev/null
echo "$1 is $([ $? -eq 0 ] && echo up || echo down)"
}
for i in {1..254}; do
check_host "192.168.4.$i" &
done
wait
结果输出到文件(含去重处理):
success_file="/root/online-hosts.txt"
failure_file="/root/offline-hosts.txt"
> "$success_file"; > "$failure_file"
for ip in 10.1.1.{1..254}; do
{
if ping -c 1 "$ip" &>/dev/null; then
echo "$ip is online" >> "$success_file"
else
echo "$ip is offline" >> "$failure_file"
fi
} &
done
wait
while循环串行版本:
i=1
while [ $i -le 254 ]; do
ping -c 2 -i 0.3 -W 1 "192.168.4.$i" &>/dev/null
if [ $? -eq 0 ]; then
echo "192.168.4.$i is up"
else
echo "192.168.4.$i is down"
fi
((i++))
done