Bashシェルスクリプトで文字列を右(左)からN文字取り出す

2022-08-14Bash,CentOS,Linux,Ubuntu

はじめに

シェルスクリプトで右からN文字、あるいは左からN文字切り取る方法。

多くの言語でStringクラスの right() メソッド、 left() メソッドとして提供されている機能になります。

検証環境

$ 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)

$ cut --version | head -n 1
cut (GNU coreutils) 8.32

$ sed --version | head -n 1
sed (GNU sed) 4.8

cut を使う方法

## 左から3文字取り出す
$ echo 0123456789 | cut -c -3
012

## 右から3文字取り出す
$ echo 0123456789 | rev | cut -c -3 | rev
789

左から切り取る場合は rev コマンドで一度左右を反転させるのがポイントです。

head / tail コマンドを利用する

文字列が改行を含んだ場合、 cut コマンドでは想定外の挙動をするときがあります。

# 2行に渡って結果が出力される
$ echo '1234
5678' | cut -c -3

123
456

これは cut コマンドが文字列全体に対してではなく、各行に対して処理を行うためです。

head コマンドを利用すると問題が発生しません。

# 左から2文字取り出す
$ echo '1234
5678' | head -c 2

12

# 左から6文字取り出す
$ echo '1234
5678' | head -c 6

1234
5

左から 6 文字取り出す例では、 1234(改行)5 の 6 文字が取り出されています。

右から取り出すには tail コマンドを使います。

( _ここでは実行結果がわかりやすいように echo -n コマンドで MARKDOWN_HASHc9f0f895fb98ab9159f51fd0297e236dMARKDOWNHASH の次に改行コードが出力されないようにしています。 )

# 右から2文字取り出す
$ echo -n '1234
5678' | tail -c 2

78

# 右から6文字取り出す
$ echo -n '1234
5678' | tail -c 6

4
5678

注意 : 全角文字を含む場合

全角文字を含む場合には注意が必要です。

ここでは aiうeo という、全角の を1文字含む文字列から部分を取り出す例を見てみましょう。

# 左から2文字取り出す
$ echo 'aiうeo' | cut -c -2
ai

# 左から3文字取り出したいが、うまく行かない
$ echo 'aiうeo' | cut -c -3
ai

# 左から3文字取り出す ( 1byte + 1byte + 3byte )
$ echo 'aiうeo' | cut -c -5
aiう

# 左から4文字取り出す
$ echo 'aiうeo' | cut -c -6
aiうe

全角文字は 3byte として扱われます。
-c オプションに指定する数値は文字数ではなくバイト数を指定する必要があるため、事前に計算が必用となります。

ファイルの中身に半角文字以外が含まれる場合には注意が必要です。

sed を使う ( 改行、全角文字を含んだ文字列にも対応 )

sed コマンドを利用する方法があります。
sed コマンドを利用した場合、全角文字のバイト数の考慮は不要です。

## 左から2文字取り出す
$ echo 'aiうeo' | sed -E 's/^(.{2}).*/\1/'
ai

## 左から3文字取り出す
$ echo 'aiうeo' | sed -E 's/^(.{3}).*/\1/'
aiう

## 右から2文字取り出す
$ echo 'aiうeo' | sed -E 's/^.*(.{2})/\1/'
eo

## 右から3文字取り出す
$ echo 'aiうeo' | sed -E 's/^.*(.{3})/\1/'
うeo

sed-z オプションを利用することで、 cut コマンドで発生していた改行を含んだ場合に各行に対して処理が行われてしまう問題にも対処可能です。 ( -z オプションについては 別のエントリ で詳しく取り上げています。 )

# 左から3文字取り出す
$ echo '1234
5678' | sed -z -E 's/^(.{3}).*/\1/'
123

変数展開を使う方法 ( 改行、全角文字を含んだ文字列にも対応 )

捜査対象の変数が存在している場合は、変数の参照時に工夫して対処できます。

$ S=0123456789

## 右から3文字切り取る
$ echo "${S::3}"
012

## 左から3文字切り取る
$ echo "${S: -3}"
789

やはり左から切り取る場合には注意。 :- の間にスペースが必要です。

ひとこと

左から切り取る操作をいつも忘れて調べるのに時間を取られてしまうので、まとめました。

2022-08-14Bash,CentOS,Linux,Ubuntu