Fix scp command failure · noraworld/dotfiles@4d7cf4f
たとえば ~/.bashrc などで、echo コマンドなどを使って何かを出力するようにしていると scp コマンドが失敗します。
# リモートホスト側
echo "something"
# クライアント側
$ scp username@example.com:~/test .
something
ファイルがカレントディレクトリにダウンロードされるはずがダウンロードされず、“something” と表示されただけで終わってしまいます。
ログイン時に標準出力に何かを表示するようにしていると scp コマンドが失敗するらしいので、scp コマンド実行時のみ ~/.bashrc などで何も出力しないようにします。
scp でのログインかどうかを判定する is_not_scp() 関数を作ります。is_not_scp() 関数は scp からのログインでなければ true を返し、scp からのログインだったら false を返します。
そして、scp だったら、ログイン時に何かを出力するようにしている部分を出力しないようにします。
is_not_scp() {
if [ ! -f /proc/$PPID/cmdline ]; then
echo true
elif [ -f /proc/$PPID/cmdline ] && [[ "$(cat /proc/$PPID/cmdline | sed 's/\x0//g')" =~ sshd:([[:blank:]]+.*)@notty ]]; then
echo false
else
echo true
fi
}
if "$(is_not_scp)"; then
echo "something"
fi
macOS では /proc/$PPID/cmdline は存在しないのでスキップします。
Linux では /proc/$PPID/cmdline に現在ログイン中の端末の情報が表示されます。たとえば SSH ログイン中に実行すると以下のように表示されます。
$ cat /proc/$PPID/cmdline
sshd: ubuntu@pts/0
これが scp コマンドだと以下のようになります。
sshd: ubuntu@notty
notty は端末が割り当てられていないということなのですが、scp コマンドだと notty と表示されるので、そうなっていれば scp コマンドからだと判断します1。
sed コマンドを使っているのは、cat /proc/$PPID/cmdline の出力にヌルバイトが混ざっているからです。sed でヌルバイトを除去しています。
ログイン時に何かを出力している箇所を if "$(is_not_scp)"; then と fi で囲めば scp 時にはその部分は出力されなくなるので、scp がうまく機能するようになります。
~/.bashrc じゃなくて ~/.bash_profile に何かを出力していたり、Zsh を使っていて ~/.zshrc とかに何かを出力していた場合も同様です。適宜、ファイル名は読み替えてください。
それ以外で sshd: ubuntu@notty のように表示されるケースもありますが実用上はおそらく問題ないと思いますので割愛します。 ↩