pv 控制 pipe 的速度

因為想控制某些程式的傳輸速度, 所以想找找看有沒有 linux 命令 能像節流閥那樣串在管道中就能限制通過的流量, 就找到一個叫 pv 的小工具, 不只是節流閥,還可以做流量統計。 隔天想想其實也可以自己用 shell script 寫一個土炮版本的。

透過 wifi 傳檔案但手機和筆電不合

我習慣用筆電開熱點,把有線網路分享給手機無線上網。 然後就能用 ftp 或 adb 傳檔案,省去 usb 線的麻煩。 但我的筆電好像和手機不合,如果 wifi 流量太大手機會自動斷線。 所以要嘛用 ftp 的斷點續傳,要嘛手動控制傳輸速度。

ftp 斷點續傳

ftp 支援斷點續傳,所以可以無腦重傳, 用 shell script 控制只要斷線就重傳的無窮迴圈就好了。 其實我用的是 sftp,然後我習慣把手機寫到 ssh_config 裡, 就能用 alias 連線。

while echo 'reput -r my-file/' | sftp atom
do
    sleep 1s
done

用 adb 傳送檔案的二種方法

另一招就是用 adb,我的手機有 root, 所以能 用 app 設定透過 wifi 連結 adb。 連上後可以用 adb push 傳檔案,或是用 shell 傳。 adb push 我不會控制速度,以我的經驗就是速度會吃滿, 然後手機就斷線了。 shell 就是之前提過用 pipe 傳, 我就想如果中間加個管道命令,像節流閥那樣就能控制速度了。

# 用 adb
adb push my-file/ /sdcard/

# 用 shell
tar -zcf - my-file/ | adb shell tar -C /sdcard/ -zxf -

# 如果有節流閥
tar -zcf - my-file/ \
    | pipe-speed-controll --max-per-second 200k \
    | adb shell tar -C /sdcard/ -zxf -

管道哲學的好處就是,可以方便的內嵌各種工具。 像如果要做壓縮,就在收發端各加個 gzip 命令, (不過上面的 tar 已經在打包時壓縮了。) 要用 ascii 傳,就加個 base64。

# 手動用 gzip 壓縮
tar -cf - my-file/ \
    | gzip -c - \
    | adb shell 'zcat - | tar -C /sdcard/ -xf -'

# base64
tar -cf - my-file/ \
    | base64 - \
    | adb shell 'base64 -d - | tar -C /sdcard/ -xf -'

流量工具 pv

於是 google 後就找到一個叫 pv 的工具, 全名是 pipe-view,不只有節流閥功能,還有流量統計。 基本上和 cat 相同都是把輸入轉到輸出, 但就是多了流量控制、統計的功能。

例如限制流量為 200k/s, 同時 pv 會自動統計流量,輸出到 standard error:

tar -zcf - my-file/ \
    | pv --rate-limit 200k \
    | adb shell tar -C /sdcard/ -xf -
# 輸出
# 2.05MiB 0:00:07 [ 301KiB/s] [      <=>  ]

另外也可以改成控制 buffer 大小。 總之是個實用的小工具, 在 debian 可以直接用 apt install pv 安裝。

用 shell script 徒手實現 pv

隔天發白日夢時,忽然想到 pv 的功能其實可以用 shell script 實現, 只要用 sleep 控制一秒讀一次,head 則控制讀多少資料。 或說每 sleep 一秒,就用 head 讀固定量的資料。

my_pv() {
    local byte=$1
    while sleep 1s
    do
        head -c $byte
    done
}
yes | my_pv 8

但這樣在流量慢時會一直卡在 sleep 那邊, sleep 整整一秒後才開始讀資料。 改善方式是可以把 sleep 丟到背景執行, 就能馬上在一秒期間內 sleep 的同時執行 head, 再用 wait 確保上一次 sleep 退出後再開始下一次讀取。

my_pv() {
    local byte=$1
    while wait
    do
        head -c $byte
        sleep 1s &
    done
}
yes | my_pv 200