【Shell】シェル ~ 文字列抽出あれこれ ~

■ はじめに

 なんだかんだで、シェルスクリプト(Bsh)を扱う機会があって
その中で文字列抽出をちょこちょこのだが、すぐに忘れてしまう、、、

 で、ブログにまとめておいたつもりだったが、
全然まとまっていなかったので、今回で整理する。
また、cut / revコマンドを学んだので、ついでにメモっておく。

目次

【0】実行環境
【1】cut / rev コマンドで抽出する
 例1:Pythonバージョンを短く抽出する
 例2:ファイル名から特定の文字列を抽出する
 例3:【Key】:【Value】の文字列抽出 & Trim
 例4:Docker image名からImage名/Tag名を抽出する
 例5:JDBC URLからHost/Portを抽出する
【2】sedコマンドで抽出する
【3】awkコマンドで抽出する
 例1:カンマ区切りの文字列を抽出する
【4】bashによる文字列置換
【5】grepコマンドで抽出する
【6】WhiteSpaceでパースする

【0】実行環境

* 気軽に動作確認するなら、以下のサイトが便利。

https://paiza.io/projects/QzTJKlZTT6VvvqVUGNvT4A?language=bash

【1】cut / rev コマンドで抽出する

* 文字列が固定されている場合に有効。

a) cutコマンド

* 文字列を切り出す
オプション 説明 サンプル
-c <数字> 切り出す位置の指定する 例1参照
-d <区切り文字> 区切り文字の指定(d:delimiter)。-fを組み合わせる 例2参照
-f <区切り文字> 項目数の指定(f:fields)。-dを組み合わせる 例2参照

https://eng-entrance.com/linux-command-cut

b) revコマンド

* 文字列を逆順にする(e.g. "World" => "dlroW")

例1:Pythonバージョンを短く抽出する

PYTHON_VERSION="3.10.13"
# 3.10.13 -> 3.10
PYTHON_SHORT_VERSION=`echo $PYTHON_VERSION | cut -d '.' -f 1,2`
# PYTHON_SHORT_VERSION=3.10
echo "PYTHON_SHORT_VERSION=$PYTHON_SHORT_VERSION"

例2:ファイル名から特定の文字列を抽出する

# ファイル名。ここから「sample_table」を抽出する
input_data="0000_sample_table_xxxx.sql"

echo "0) input_data = ${input_data}"

data1=`echo ${input_data} | cut -c 6-`
echo "1) data1 = ${data1}"

data2=`echo ${data1} | rev | cut -c 10- | rev`
echo "2) data2 = ${data2}"

出力結果

0) input_data = 0000_sample_table_xxxx.sql
1) data1 = sample_table_xxxx.sql
2) data2 = sample_table

例3:【Key】:【Value】の文字列抽出 & Trim

#!/bin/bash

result="   Total Size:   102400"

key=`echo ${result} | cut -d ':' -f 1 | sed 's/^ *\| *$//'`
# key=Total Size
echo "key=${key}"

value=`echo ${result} | cut -d ':' -f 2 | sed 's/^ *\| *$//'`
# value=102400
echo "value=${value}"

例4:Docker image名からImage名/Tag名を抽出する

source="xxxx.xxx.ecr.us-west-2.amazonaws.com/hello-world-ecr:latest"
image_name=`echo ${source} | cut -d ':' -f 1`
tag_name=`echo ${source} | cut -d ':' -f 2`

echo "image_name=${image_name}"
echo "tag_name=${tag_name}"

出力結果

image_name=xxxx.xxx.ecr.us-west-2.amazonaws.com/hello-world-ecr
tag_name=latest

例5:JDBC URLからHost/Portを抽出する

jdbc_url="jdbc:postgresql://localhost:5432/sample_db"
host_n_port=`echo ${jdbc_url} | cut -d '/' -f 3`
host=`echo ${host_n_port} | cut -d ':' -f 1`
port=`echo ${host_n_port} | cut -d ':' -f 2`

echo "host_n_port=${host_n_port}"
echo "host=${host}"
echo "port=${port}"

出力結果

host_n_port=localhost:5432
host=localhost
port=5432

参考文献

https://shuzo-kino.hateblo.jp/entry/2015/04/12/153737

【2】sedコマンドで抽出する

* sed (セド) は、文字列の置換/削除するコマンド
 => 不要な部分を削除してしまえば、抽出することができる
* 詳細は、以下の関連記事を参照のこと。

sedコマンド
https://dk521123.hatenablog.com/entry/2019/11/23/101625

例1:ファイル名からデータの一部を抽出する

https://www.atmarkit.co.jp/ait/articles/1610/18/news008.html

# 「(~)」により、文字列の一部を取り出せる
echo "systemname_table1.sql" | sed -r 's/systemname_(.*).sql$/\1/'

出力結果

table1

例2:取り出した値を変数に格納する

https://myokoym.hatenadiary.org/entry/20110408/1302292907

#!/bin/bash

# 'person'を取りたい。100は可変。
result=`echo "100_table_name_person.sql" | sed -r "s/^.*_table_name_(.*).sql$/\1/"`
echo "Result : ${result}"

出力結果

Result : person

【3】awkコマンドで抽出する

* awk (オーク) は、区切り文字のテキストを処理するコマンド
* 詳細は、以下の関連記事を参照のこと。

https://dk521123.hatenablog.com/entry/2019/11/22/223043

1)サンプル

例1:カンマ区切りの文字列を抽出する

# [構文] awk -F'[区切り文字]' 'パターン {アクション}'
result=`echo "1,2,4,8" | awk -F',' '{print $3}'`

# Result is 4 ($3 で3番目の要素を出力した)
echo "Result is ${result}"

例2:ドット区切りの文字列を抽出し、くっつけ直す

# [構文] awk -F'[区切り文字]' 'パターン {アクション}'
result=`echo "3.10.14" | awk -F'.' '{ printf("%s.%s", $1, $2) }'`

# Result is 3.10 ($1, $2 で1,2番目の要素を出力した)
echo "Result is ${result}"

【4】bashによる文字列置換

* 詳細は、以下の関連記事を参照のこと。

https://dk521123.hatenablog.com/entry/2020/03/24/223323

【5】grepコマンドで抽出する

以下を参考にした。

https://ez-net.jp/article/7A/9qV1-XpJ/6o8QiIwz08um/

1)サンプル

hiveコマンド「DESC FORMATTED <your_table_name>;」で
得られた結果をファイルに保存して、その結果をパースする

https://dk521123.hatenablog.com/entry/2023/02/21/223137
sample.sh

#!/bin/bash

echo "Start!"

location=`grep -i "Location:" input.txt | sed -e "s/^\([^:]*\):\(.*\)$/\2/" | sed 's/^ *\| *$//'`
location=${location:--}

last_access_time=`grep -i "LastAccessTime:" input.txt | sed -e "s/^\([^:]*\):\(.*\)$/\2/" | sed 's/^ *\| *$//'`
last_access_time=${last_access_time:--}

table_type=`grep -i "Table Type:" input.txt | sed -e "s/^\([^:]*\):\(.*\)$/\2/" | sed 's/^ *\| *$//'`
table_type=${table_type:--}

num_rows=`grep -i "numRows" input.txt | xargs echo | cut -d ' ' -f 2 | sed 's/^ *\| *$//'`
num_rows=${num_rows:--}

total_size=`grep -i "totalSize" input.txt | xargs echo | cut -d ' ' -f 2 | sed 's/^ *\| *$//'`
total_size=${total_size:--}

num_partitions=`grep -i "numPartitions" input.txt | xargs echo | cut -d ' ' -f 2 | sed 's/^ *\| *$//'`
num_partitions=${num_partitions:--}

# location = hdfs://user/hive/wrehouse/default/book
echo "===location = ${location}==="
# last_access_time = UNKNOWN
echo "===last_access_time = ${last_access_time}==="
# table_type = MANAGED_TABLE
echo "===table_type = ${table_type}==="
# num_rows = 11
echo "===num_rows = ${num_rows}==="
# total_size = 20
echo "===total_size = ${total_size}==="
# num_partitions = -
echo "===num_partitions = ${num_partitions}==="

echo "DONE..."

input.txt

# col_name              data_type               comment
 
id              string
name              string
year                int
pages                   int

# Detailed Table Information
Database:               default
Owner:                  hadoop
CreateTime:             Thu Mar 21 09:28:45 PDT 2019
LastAccessTime:         UNKNOWN
Protect Mode:           None
Retention:              0
Location:               hdfs://user/hive/wrehouse/default/book
Table Type:             MANAGED_TABLE
Table Parameters:
        COLUMN_STATS_ACCURATE   {\"BASIC_STATS\":\"true\"}
        numFiles                0
        numRows                 11
        rawDataSize             0
        totalSize               20
        transient_lastDdlTime   1553185725
 
# Storage Information
SerDe Library:          org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
InputFormat:            org.apache.hadoop.mapred.TextInputFormat
OutputFormat:           org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
Compressed:             No
Num Buckets:            -1
Bucket Columns:         []
Sort Columns:           []
Storage Desc Params:
        serialization.format    1

【6】WhiteSpaceでパースする

* 以下に載っているやり方が一番やりやすい

https://stackoverflow.com/questions/1469849/how-to-split-one-string-into-multiple-strings-separated-by-at-least-one-space-in

サンプル

sentence="This is a Hello world!!"
sentence_array=($sentence)

echo ${sentence_array[0]}
for value in "${sentence_array[@]}"
do
  echo $value
done

参考文献

https://myokoym.hatenadiary.org/entry/20110408/1302292907

関連記事

シェル ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2014/10/23/005406
シェル ~ 文字列置換 ~
https://dk521123.hatenablog.com/entry/2023/10/25/000000
JDBCシェルスクリプトでパースする
https://dk521123.hatenablog.com/entry/2020/03/24/223323
シェルで split するには
https://dk521123.hatenablog.com/entry/2021/09/02/000000
sedコマンド
https://dk521123.hatenablog.com/entry/2019/11/23/101625
awkコマンド
https://dk521123.hatenablog.com/entry/2019/11/22/223043