如何将SCP成功传输的文件分配到Bash数组的独立位置?
问题:Bash脚本中成功传输的文件名无法按单个元素存入数组
我写了一段Bash脚本,通过scp从远程服务器拉取文件,原本打算删除已成功获取的远程文件,现在希望把这些成功传输的文件名存入数组(每个文件占数组的独立位置),后续通过ssh用这个数组删除对应远程文件。但现在所有成功传输的文件名都被塞进了数组的索引0里,相关脚本、运行情况如下:
脚本代码
#!/bin/bash # #------------------------------------------------[ Variables ]------------------------------------- #need to put the full path so that oracle knows about it export STARTTIME=$(/usr/bin/date +%H:%M:%S) export PROGNAME=$(basename $0) export FILEDATESTAMP=$(/usr/bin/date +%Y%m%d_%H%M) . /usr/local/bondscape/sbin/profile #------------------------------------------------[ Constants ]------------------------------------- # Configuration SCRIPT="$(basename $(readlink --canonicalize --no-newline $0))" SCRIPT_DIR="$(dirname $(readlink --canonicalize --no-newline $0))"t with te SCRIPT_FILENAME="${SCRIPT%.*}" SCRIPT_FILEEXT="${SCRIPT##*.}" SCRIPT_FILEPATH="${SCRIPT_DIR}" LOGDIR="/tmp" LOG=${LOGDIR}/GetFile-${FILEDATESTAMP}.log.$$ TRX_LOG=/tmp/GetFile.$$ if [[ $# -lt 4 ]] ; then # Or count gt 0 and then process, or as original $4 is NULL/empty echo "Usage: $0 [source path] [destination path] [scp server] [file pattern]" >&2 exit 1 fi IFS=',' read -ra FILEMASKS <<< "$4" #sending to tmp log file echo "-----------------------------------------------------" >> $LOG echo "StartTime ${STARTTIME}" >> $LOG echo "Push Files to ${3}" >> $LOG echo "Filemasks are ${FILEMASKS[@]}" >> $LOG echo "Source Path is ${1}" >> $LOG echo "Dest Path is ${2}" >> $LOG echo "SCP Server is ${3}" >> $LOG echo "-----------------------------------------------------" >> $LOG #SCP to get the file USER=oracle for FILE in "${FILEMASKS[@]}"; do script -q -c "scp ${USER}@${3}:${1}/${FILE}* ${2}" >> ${TRX_LOG} done FILEMASK=${4} awk -v searchterm="${FILEMASK}" '$0 ~ searchterm {IGNORECASE=1 ; print $1 }' ${TRX_LOG} >> $LOG echo "-----------------------------------------------------" >> /tmp/GetFile.$$ echo " Transfer From ASH: " echo "-----------------------------------------------------" echo "Show the array -" SUCCESSXFER=() SUCCESSXFER=$( (grep 100% /tmp/tmpGetFile | awk '{print $1}' /tmp/tmpGetFile) ) for item in "${SUCCESSXFER[@]}"; do #testing the items in the array echo "Array item is - $item" SUCCESSXFER=() SUCCESSXFER=$( (grep 100% /tmp/tmpGetFile | awk '{print $1}' /tmp/tmpGetFile) ) for item in "${SUCCESSXFER[@]}"; do #testing the items in the array echo "Array item is - $item" done exit 0
运行输出
/GetFilev6 /home/oracle/ /tmp/ vbnexfer01.housing.qld.gov.au ten*,TEN* Transfer From ASH: ----------------------------------------------------- Show the array - Array item is - tenant.7366 tenant.7367 tenant.7369 tentt1 tentt2 TENANT TENANT12 TENANT.7366 TENANT.7367 TENANT.7369
手动测试结果
# echo ${SUCCESSXFER[0]} tenant.7366 tenant.7367 tenant.7369 tentt1 tentt2 TENANT TENANT12 TENANT.7366 TENANT.7367 TENANT.7369
所有文件名都被放在了数组的索引0位置,请问怎么修改脚本让每个文件对应数组的独立元素?
解决方案
问题根源
你当前用SUCCESSXFER=$(...)的方式给数组赋值,这种写法会把命令的全部输出当成单个字符串存入数组的第一个元素,不会自动拆分每行或每个文件名到独立的数组索引。
正确的数组赋值方式
下面提供两种可靠的方法,把每个成功传输的文件名存入数组的独立位置:
方法1:用mapfile(推荐,Bash 4.0+支持)
mapfile可以直接把命令输出的每一行读取为数组的一个元素,加上-t参数会去掉每行末尾的换行符:
# 清空数组 SUCCESSXFER=() # 把grep+awk的输出逐行存入数组 mapfile -t SUCCESSXFER < <(grep 100% /tmp/tmpGetFile | awk '{print $1}')
方法2:用while循环兼容旧版Bash
如果你的系统是旧版Bash(低于4.0),可以用while read循环逐行读取输出并添加到数组:
# 清空数组 SUCCESSXFER=() # 逐行读取命令输出,添加到数组 while IFS= read -r filename; do SUCCESSXFER+=("$filename") done < <(grep 100% /tmp/tmpGetFile | awk '{print $1}')
修复脚本的额外注意事项
- 你的脚本里重复写了两遍数组赋值和循环代码,需要删掉重复的部分,只保留一组即可。
- 验证数组是否正确拆分:
for item in "${SUCCESSXFER[@]}"; do echo "Array item is - $item" done
运行后每个文件名会单独输出,对应数组的独立索引。
后续用ssh删除远程文件的示例
如果要通过ssh批量删除远程服务器上的这些文件,为了避免文件名含空格、特殊字符导致出错,可以用Bash的@Q特性(Bash 4.4+支持)自动转义每个文件名:
# 拼接远程文件的完整路径,并用@Q转义每个文件名 ssh "${USER}@${3}" 'rm -f '"${1}/"'${SUCCESSXFER[@]@Q}'
内容的提问来源于stack exchange,提问作者goced
相关产品推荐
相关产品推荐

