Shell-Script

在腳本中使用 cURL 發布數據

  • May 10, 2016

我正在嘗試編寫一個簡單的替代腳本來將文件上傳到transfer.sh服務。網站上的一個範例提到了一種在單個“會話”中上傳多個文件的方法:

$ 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"is的輸出-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 所解釋的,可以通過使用數組來解決這個問題。一種適用於兩者的解決方案bashzsh

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
}

兩者的輸出zshbash

$ 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

xsel --clipboard使用或xclip在 Linux 和pbcopyOS X上將函式的輸出通過管道傳輸到剪貼板可能是一個好主意。


@Jay 提供的解決方案也非常好用:

transfer() {
 printf -- "-F filedata=@%s\0" "$@" \
   | xargs -0 sh -c \ 
   'curl --progress-bar -i "$@" https://transfer.sh | grep -i https' zerop
}

避免 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
}

試試這個標準和安全的解決方案:

transfer() {
 printf -- "-F filedata=@%s\0" "$@" | xargs -0 sh -c 'curl --progress-bar -i "$@" https://transfer.sh | grep -i https' zerop
}

"$@"使用雙引號將使 shell 保持原始選項不變。請參閱The Open Group Base Specifications Issue 6中的2.5.2 特殊參數部分:

@

擴展到位置參數,從一個開始。當擴展發生在雙引號內時,並且在執行欄位拆分(請參閱欄位拆分)時,每個位置參數都應擴展為單獨的欄位,前提是第一個參數的擴展仍應與開頭部分連接原詞(假設擴展參數嵌入一個詞中),最後一個參數的擴展仍應與原詞的最後部分連接。如果沒有位置參數,“@”的擴展將生成零欄位,即使“@”被雙引號引起來。

使用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

引用自:https://unix.stackexchange.com/questions/282084