Bashでコマンドライン上にタブ文字をはりつけると消える問題に対応

はじめに

MacのデフォルトシェルもZshになったこともあり、最近インタラクティブな操作目的でBashを起動する機会は徐々に減っています。 とはいえ、やはりサーバのログインシェルに利用されているのは未だBashが多く、今後もこの状況が続くでしょう。

Bashのコマンドライン上にタブ文字を含む文字列を貼り付けた場合、タブ文字が消えてしまうことがないでしょうか? このタブ文字が消える問題への対処法について紹介します。

検証環境

1
2
3
4
5
$ uname -moi
x86_64 x86_64 GNU/Linux

$ bash -version | head -n 1
GNU bash, version 4.2.46(2)-release (x86_64-redhat-linux-gnu)

タブ文字が消える問題について

ここで、Bashのヒアドキュメント機能を利用して文字列を出力させたい、というケースを考えてみます。 出力させない内容は、タブ区切り文字列である TSV 形式となっています。

以下のような3行からなるTSVです。

1
2
3
aa	bb	cc
aa	bb	cc
aa	bb	cc

cat コマンドと ヒアドキュメント を使って出力してみます。 以下の内容をコピーして、Bashシェル上に貼り付けてみます。

1
2
3
4
5
cat <<EOF
aa	bb	cc
aa	bb	cc
aa	bb	cc
EOF

うまくいく環境もあるでしょうが、僕の環境ではタブが消えてしまいます。

これはなぜでしょう?

原因はBashの補完機能

原因はBashの補完機能によるものです。 実は、タブが貼り付けられたタイミングで補完が実行されていますが、 aabbcc といった文字列で前方一致するファイルがないため、補完対象が見つかっていないだけなのです。

貼り付ける文字列を以下のようなものに変更するとより挙動がはっきりします。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# ホームディレクトリに移動
$ cd

# カレントディレクトリのファイルを確認
$ ls .bashrc
.bashrc

# .bashrcが補完される
$ cat <<EOF
.bashr	bb	ccc
EOF

おそらく、以下のような文字列が表示されることでしょう。

1
.bashrc bbccc

.bashr という文字列とその後方のタブ文字により、 .bashrc というカレントディレクトリのファイル名が補完されたわけです。

保管機能を一時的に無効にする

これに対処するためには、以下のように補完機能を一時的に無効にすればよいです。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# 新しいインタラクティブなBashを起動(一時的な設定変更をexitしたときに無効にできる)
$ bash

# 補完を無効化
$ bind 'set disable-completion on'

# 貼り付け
$ cat <<EOF
aa	bb	cc
aa	bb	cc
aa	bb	cc
EOF

# exitしてしまえば、設定は無効になります。
$ exit

今度はうまくいくはずです。

1
2
3
aa      bb      cc
aa      bb      cc
aa      bb      cc

サーバ上の操作でタブを含む文字列を貼り付ける場合には注意が必要

ここで一点注意が必要です。 このようなことは運用操作上起こり得るものです。

cat コマンドだけでなく、 echo コマンドで、例えば何かのファイルの末尾に1行情報を付与したい( /etc/hosts など)場合にも問題が発生します。

1
echo '192.168.0.1	hello.com' >> /etc/hosts

上記のようなコマンドをクリップボードに貼り付け、サーバ上のログインシェルに貼り付けて実行した場合は以下のようになってしまいます。

1
2
3
4
5
6
7
8
9
$ cat /etc/hosts
127.0.0.1       localhost
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2      8b982518588b
192.168.0.1hello.com

気づかないと意外とハマってしまいます。

ひとこと

タブ文字が消える問題。 奥が深い。