Bash脚本中scp批量上传:传入*作为参数失效问题咨询
为什么脚本里用
$1传*给scp会失效? 这是个非常典型的Shell路径名展开(globbing)时机问题,我给你一步步拆解清楚:
核心原因:*的展开时机不同
当你直接在命令行执行scp * username2@destination_host:directory2/filename2时,Bash会先帮你把*展开成当前目录下的所有非隐藏文件名,然后把这些文件名作为独立参数传给scp。比如当前目录有file1.txt、file2.jpg,scp实际收到的命令是scp file1.txt file2.jpg username2@host:dir,自然能正常工作。
但当你把*作为参数传给脚本时,会出现两种错误场景:
- 没加引号传
*:比如运行./your_script * user@host dir,这时候Bash还是会先在脚本外部把*展开成所有文件名,导致$1变成第一个文件名(比如file1.txt),$2变成第二个文件名(file2.jpg),$3才是user@host——这时候你的脚本执行scp $1 $2:$3就变成了scp file1.txt file2.jpg:user@host,完全不符合预期,肯定报错。 - 加引号传
*:比如运行./your_script "*" user@host dir,这时候Bash不会展开*,$1就是字符串*。scp收到这个参数后,会去本地找名为*的文件,而不是展开成所有文件,自然找不到对应的文件,执行失败。
解决办法
根据你的需求,有两种靠谱的解决方案:
方案1:让脚本接收所有文件参数(推荐)
如果你的需求是上传任意数量的文件(包括用*通配的情况),可以让脚本把除最后两个参数外的所有内容作为要上传的文件,最后两个参数分别是目标主机和目录:
#!/bin/bash # 提取目标信息:倒数第二个参数是用户@主机,最后一个是目标目录 target_host="${@: -2:1}" target_dir="${@: -1}" # 提取要上传的文件:所有参数除了最后两个 files=("${@:1: $#-2}") # 执行scp,注意用双引号包裹数组,保留带空格的文件名 scp "${files[@]}" "$target_host:$target_dir"
使用方式和直接敲scp一样:
./your_script * username2@destination_host directory2
这时候Bash会先展开*成所有文件,脚本会把这些文件全部传给scp,和直接执行scp命令效果完全一致,还能支持带空格的文件名。
方案2:在脚本内部展开通配符
如果你需要脚本接收通配符字符串(比如用户想上传*.txt这类特定模式的文件),可以在脚本内部手动展开通配符(注意:这种方式要避免处理不可信的参数,防止安全问题):
#!/bin/bash # 手动展开通配符成文件数组 eval "files=($1)" # 执行scp scp "${files[@]}" "$2:$3"
使用时需要给通配符加引号:
./your_script "*.txt" username2@destination_host directory2
内容的提问来源于stack exchange,提问作者Zach
相关产品推荐
相关产品推荐

