我正在尝试编写一个简单的替代脚本来将文件上传到传输文件服务。网站上的一个示例提到了一种在单个“会话”中上传多个文件的方法:
$ curl -i -F filedata=@/tmp/hello.txt \
-F filedata=@/tmp/hello2.txt https://transfer.sh/
我正在尝试创建一个函数,它可以接受任意数量的参数(文件)并以这种方式将它们传递给 cURL。函数如下:
transfer() {
build() {
for file in $@
do
printf "-F filedata=@%q " $file
done
}
curl --progress-bar -i \
$(build $@) https://transfer.sh | grep https
}
只要文件名中没有空格,该函数就会按预期工作。的输出printf "-f filedata=@%q " "hello 1.txt"
是-F filedata=@test\ 1.txt
,所以我期望特殊字符能够正确转义。但是,当使用包含空格的文件名调用该函数时:
$ transfer hello\ 1.txt
cURL 似乎无法解释转义并报告错误:
curl: (26) couldn't open file "test\"
我还尝试引用命令的部分内容,例如printf "-F filedata=@\"%s\" " "test 1.txt"
,它会生成-F filedata=@"test 1.txt"
.在这种情况下,cURL 返回相同的错误:curl: (26) couldn't open file ""test"
。看起来它根本不关心引号。
我不太确定是什么导致了这种行为。如何修复该函数以使其也可以处理包含空格的文件名?
编辑/解决方案
正如 @meuh 所解释的,可以通过使用数组来解决该问题。一个同时适用于 和 的解决方案bash
是zsh
:
transfer () {
if [[ "$#" -eq 0 ]]; then
echo "No arguments specified."
return 1
fi
local -a args
args=()
for file in "$@"
do
args+=("-F filedata=@$file")
done
curl --progress-bar -i "${args[@]}" https://transfer.sh | grep https
}
zsh
和的输出bash
为:
$ ls
test space.txt test'special.txt
$ transfer test\ space.txt test\'special.txt
######################################################################## 100.0%
https://transfer.sh/z5R7y/test-space.txt
https://transfer.sh/z5R7y/testspecial.txt
$ transfer *
######################################################################## 100.0%
https://transfer.sh/4GDQC/test-space.txt
https://transfer.sh/4GDQC/testspecial.txt
在 Linux 和OS X 上使用xsel --clipboard
或将函数的输出通过管道传输到剪贴板可能是个好主意。xclip
pbcopy
@Jay 提供的解决方案也非常有效:
transfer() {
printf -- "-F filedata=@%s\0" "$@" \
| xargs -0 sh -c \
'curl --progress-bar -i "$@" https://transfer.sh | grep -i https' zerop
}
答案1
尝试一下这个标准且安全的解决方案:
transfer() {
printf -- "-F filedata=@%s\0" "$@" | xargs -0 sh -c 'curl --progress-bar -i "$@" https://transfer.sh | grep -i https' zerop
}
"$@"
加双引号将使 shell 保持原来的选项不变。看2.5.2 特殊参数部分来自The Open Group 基本规范第 6 期:
@
扩展到位置参数,从 1 开始。当发生膨胀时双引号内,以及字段分割(参见场分裂)执行时,每个位置参数应扩展为单独的字段,前提是第一个参数的扩展仍应与原始单词的开头部分连接(假设扩展的参数嵌入在单词中),并且最后一个参数的扩展仍应与原始单词的最后部分连接。如果没有位置参数,则“@”的扩展将生成零字段,即使“@”被双引号引起来也是如此。
使用printf
这种方式是处理选项的标准方式。您可以测试它,并看到结果将生成-F filedata=@
与选项数量一样多的字符串。
使用名称中包含特殊字符的 2 个文件进行测试:
$ ls -b
f\ rst s'cond
$ transfer() { printf -- "-F filedata=@%s\0" "$@" | xargs -0 sh -c 'curl --progress-bar -i "$@" https://transfer.sh | grep -i https' zerop ;}
$ transfer *
######################################################################## 100.0%
https://transfer.sh/fnzuh/f-rst
https://transfer.sh/fnzuh/scond
答案2
避免 bash 分词的一种方法是使用数组来携带每个参数,而不需要转义:
push(){ args[${#args[*]}]="$1"; }
build() {
args=()
for file
do push "-F"
push "filedata=@$file"
done
}
build "$@"
curl --progress-bar -i "${args[@]}" https://transfer.sh | grep https
该build
函数创建一个数组args
,并将push
一个新值添加到数组末尾。简单curl
地使用数组。
第一部分可以简化,也push
可以简单地写为args+=("$1")
,所以我们可以将其删除并更改build
为
build() {
args=()
for file
do args+=("-F" "filedata=@$file")
done
}