LoginSignup
14
8

More than 5 years have passed since last update.

.bashrc内にechoを入れておくとscpに失敗する

Posted at

はじめに

ターミナルでbashを実行した際に読み込まれるファイルは.bashrcや.bash_profileなどあるが、ここでは.bashrcを対象にする。これはbashの設定ファイルであり、PATHを通したりaliasをかけたりとできるが、ここである書き方をすると、scpに失敗する。これまで個人で使用する分にはDropboxやファイルサーバを使うなどして取り立てるほどの問題ではなかったが、今回調べて見たのでその結果をまとめる。

なお、要約すると、以下のとおりである。

  • 非対話型モードでechoなど実行するとscpのセッションが壊れる
  • 非対話型モードにechoなどを回避するように記述する

.bashrcを編集した理由

当時Unix系システムを勉強し始めたばかりであり、さらに複数の端末を有していた私は、

  • ログイン時にどんなファイルを読み込んだか常に分かるようにしていた
  • どのマシンにログインしたのか、ターミナル上ですぐわかるようにしておきたい

との理由で、.bashrcに以下のような内容を書き連ねていった。

.bashrc
function read_usr_bashrc(){
        echo 'read /Users/hikaru/.bashrc...'
}


read_usr_bashrc

このマシンでterminalを起動すると、次のように表示される。

Last login: Sun Dec 23 10:31:41 2018
read /Users/hikaru/.bashrc
[hikaru@Judith ~]$ 

なお、このとき

.profile
if [ -r $HOME/.bashrc ]; then
        source $HOME/.bashrc
fi

として読み込んでいる。

これでリモートログインしても、どの端末にログインしてどんなファイルを読み込んだのかがすぐに分かる。当初の目的は達成された、はずだった。

scpの失敗

端末間でファイルをやり取りすることはよくあるが、Unix系のシステムを使っていると、USBメモリなどではなく、network経由で簡単に済ませてしまいたい。しかし、これを実際にやろうとすると、

[hikaru@Ava ~]$ scp hikaru@Judith:~/hoge.txt .
Password:
read /Users/hikaru/.bashrc...
[hikaru@Ava ~]$ ls -la hoge.txt
ls: hoge.txt: No such file or directory
[hikaru@Ava ~]$

となってファイルを転送できない。これでは困る。

scpについて

真面目に調べて見るとstackoverflowの記事を見つけた。
ここを内容をまとめて、以下に述べる。

Bashの仕様について

まずbash 3.2.57のman pageを確認する。

[hikaru@Judith ~]$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)
Copyright (C) 2007 Free Software Foundation, Inc.
[hikaru@Judith ~]$ man bash
(中略)
       When  bash is invoked as an interactive login shell, or as a non-inter-
       active shell with the --login option, it first reads and executes  com-
       mands  from  the file /etc/profile, if that file exists.  After reading
       that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile,
       in  that order, and reads and executes commands from the first one that
       exists and is readable.  The --noprofile option may be  used  when  the
       shell is started to inhibit this behavior.
##要約##
# bashは対話型or非対話型にかかわらず、/etc/profileを読み込む
# その後 ~/.bash_profile, ~/.bash_login, ~/.profileの順に探索し、最初に見つけたものを読み込む。
######

(中略)
       When an interactive shell that is not a login shell  is  started,  bash
       reads  and executes commands from ~/.bashrc, if that file exists.  This
       may be inhibited by using the --norc option.  The --rcfile file  option
       will  force  bash  to  read  and  execute commands from file instead of
       ~/.bashrc.
##要約##
# bashがログインシェルではない場合、bashを実行すると~/.bashrcを読み込む。
######

(中略)
       Bash attempts to determine when it is being run  by  the  remote  shell
       daemon,  usually  rshd.  If bash determines it is being run by rshd, it
       reads and executes commands from ~/.bashrc, if that file exists and  is
       readable.  It will not do this if invoked as sh.  The --norc option may
       be used to inhibit this behavior, and the --rcfile option may  be  used
       to  force  another  file to be read, but rshd does not generally invoke
       the shell with those options or allow them to be specified.
##要約##
# リモートシェルデーモンが走った場合、~/.bashrcのみを読み込む。
######
(以下略)

Login shell がbashとすると、

  • 自端末でログイン: Login shellがbashなので、/etc/profile -> ~/.profile (さらに読み出している場合 -> ~/.bashrc)
  • sshでログイン: Login shellがbashなので、/etc/profile -> ~/.profile (さらに読み出している場合-> ~/.bashrc)
  • scpでファイル転送: remote shell daemonが実行され、~/.bashrcのみが実行

sshとscpの比較検証

下記のように.profileを書き換えて検証した。

.profile@Judith
if [ -r $HOME/.bashrc ]; then
    source $HOME/.bashrc
fi

function read_usr_profile(){
    echo 'read '$HOME'/.profile'
}

function echo_machine_name(){
        echo '-----------------------------------------------'
        echo '      ██╗██╗   ██╗██████╗ ██╗████████╗██╗  ██╗ '
        echo '      ██║██║   ██║██╔══██╗██║╚══██╔══╝██║  ██║ '
        echo '      ██║██║   ██║██║  ██║██║   ██║   ███████║ '
        echo ' ██   ██║██║   ██║██║  ██║██║   ██║   ██╔══██║ '
        echo ' ╚█████╔╝╚██████╔╝██████╔╝██║   ██║   ██║  ██║ '
        echo '  ╚════╝  ╚═════╝ ╚═════╝ ╚═╝   ╚═╝   ╚═╝  ╚═╝ '
        echo '-----------------------------------------------'

}

read_usr_profile
echo_machine_name

この条件下で、sshとscpを実行する。

[hikaru@Ava ~]$ ssh hikaru@192.168.179.112
Password:
Last login: Mon Dec 24 18:23:47 2018 from 192.168.179.127
read /Users/hikaru/.bashrc...
read /Users/hikaru/.profile
-----------------------------------------------
      ██╗██╗   ██╗██████╗ ██╗████████╗██╗  ██╗ 
      ██║██║   ██║██╔══██╗██║╚══██╔══╝██║  ██║ 
      ██║██║   ██║██║  ██║██║   ██║   ███████║ 
 ██   ██║██║   ██║██║  ██║██║   ██║   ██╔══██║ 
 ╚█████╔╝╚██████╔╝██████╔╝██║   ██║   ██║  ██║ 
  ╚════╝  ╚═════╝ ╚═════╝ ╚═╝   ╚═╝   ╚═╝  ╚═╝ 
-----------------------------------------------
[hikaru@Judith ~]$ exit
logout
Connection to 192.168.179.112 closed.
[hikaru@Ava ~]$ scp hikaru@192.168.179.112:~/hoge.txt .
Password:
read /Users/hikaru/.bashrc...
[hikaru@Ava ~]$ 

確かに、sshで接続した際は.profileから読み込んでいるが、scpで接続したときは.bashrcのみ読み込まれた。

scpの仕様について

scpのman pageにある通り、scpはrcpをもとに開発されたプログラムである。
rcpは.profileなどの中で何らかの出力があるとうまく動作しないことがバグとして報告されており、scpにおいても、出力が実行されるうまくいかない。

scpを利用する際の回避策

scp実行時に呼び出されるのは.bashrcのみであるから、このファイルの書き方を気にすれば良い。

.bashrcの中でechoなど出力を伴う処理を除去

最も簡単な手法で、出力さえ伴わなければ上記のバグを回避できる。

対話型モードか否かの判定文を使用

これは、bashのman pageにあるPS1 is set and $- includes i if bash is interactive, allowing ashell script or a startup file to test this state.を元に、以下のいずれかを.bashrcの先頭に書き込めば良い。

if [[ $- != *i* ]]; then return; fi

あるいは

if [ -z "$PS1" ]; then
    return
fi 

これで解決できた。

14
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
8