1. 概要

Bashはsh互換のシェルおよびコマンドプロセッサであり、文字列操作はシェル環境で実行される最も一般的なタスクの1つです。

このチュートリアルでは、Bashを使用して文字列を操作する方法を学習します。

2. 文字列変数の宣言と割り当て

Bashには型システムがなく、すべての変数は文字列です。ただし、変数には、文字列を処理する場合でも有益な動作を変更または制約する属性を含めることができます。

2.1. 宣言

簡単な宣言と値の割り当ては次のようになります。

$ VAR1='Hello World'
$ VAR2=Hello

等号の前後にスペースがないことに注意してください。 変数に属性を割り当てたい場合は、declareコマンドを使用できます。たとえば、 -r フラグを使用すると、読み取り専用になります。

$ declare -r VAR1='Hello world'

ここで、その変数に他の値を割り当てようとすると、エラーが発生します。

$ VAR1='Good morning Vietnam'
-bash: VAR1: readonly variable

2.2. 読む

読み取りコマンドを使用して、ユーザーに入力を求めることができます。

$ read -p 'Type your name and press enter: ' NAME
Type your name and press enter: Baeldung
$ echo "Hello $NAME"
Hello Baeldung

-p フラグを使用すると、追加のechoコマンドを入力せずにプロンプトテキストを指定できます。 コマンドの最後のパラメーターは、変数の名前です。 ここで名前を指定しない場合、デフォルトはREPLYになります。

3. パターンマッチングと置換

3.1. 長さ

変数名の前にあるパラメータ展開内のハッシュ(#)演算子を使用して、文字列の長さにアクセスできます。

$ NAME=Baeldung
$ echo ${#NAME}
8

3.2. 部分文字列

パラメーター展開内でコロン(:)演算子を使用して部分文字列を抽出し、部分文字列の開始位置とオプションで部分文字列の長さを指定できます

$ NAME=Baeldung
$ echo ${NAME:6}
ng
$ echo ${NAME:0:4}
Bael

3.2. パターンマッチング

Bashには、単純なパターンマッチングシステムが組み込まれています。いくつかのワイルドカードで構成されています。

  • * –任意の数の文字に一致します
  • + –1つ以上の文字に一致します
  • [abc] –指定された文字のみに一致します

たとえば、条件文を使用して、ファイルの拡張子が.jpgであるかどうかを確認できます。

$ if [[ "file.jpg" = *.jpg ]]; then echo "is jpg"; fi
is jpg

「拡張グロブ」と呼ばれる拡張マッチングシステムもあります。ワイルドカードを特定のパターンに制約することができます。

  • *(pattern) –任意の数のパターンの出現に一致します
  • ?(pattern) –パターンの0回または1回の出現に一致します
  • +(pattern) –パターンの1つ以上のオカレンスに一致します
  • !(pattern) –パターンを無効にし、パターンと一致しないものすべてに一致します

拡張グロビングは、shoptコマンドでオンにする必要があります。 最後のスニペットを.jpeg拡張子にも一致するように改善できます。

$ shopt -s extglob
$ if [[ "file.jpg" = *.jp?(e)g ]]; then echo "is jpg"; fi
is jpg

より表現力豊かなパターン言語が必要な場合は、正規表現を等しくない(=〜)演算子とともに使用することもできます。

$ if [[ "file.jpg" =~ .*\.jpe?g ]]; then echo "is jpg"; fi
is jpg

ここでは、-Eフラグを指定してgrepを呼び出す場合のように、拡張正規表現を使用できます。 キャプチャグループを使用する場合、それらは BASH_REMATCH 配列変数に格納され、後でアクセスできます。

3.3. 一致した部分文字列を削除する

Bashは、パラメーター展開を使用して特定の文字列から部分文字列を削除するメカニズムを提供します。常に1つの一致する部分文字列のみを削除します。 使用法に応じて、最長または最短の部分文字列と一致し、最初または最後から一致する場合があります。

変数を変更せず、変更された値のみを返すことに注意することが重要です。この事実を明示するために、例では読み取り専用変数を使用します。

それでは、ファイル名から拡張子を削除しましょう。 これを行うには、パーセント(%)演算子を使用して文字列の最後から一致させる必要があります。 singular演算子は最短の部分文字列と一致し、doubleは最長の部分文字列と一致します。

$ declare -r FILENAME="index.component.js"
$ echo ${FILENAME%.*}
index.component

単一のパーセント記号を使用したため、.jsサブストリングのみに一致しました。 すべての拡張機能を除外したい場合は、次のようにします。

$ declare -r FILENAME="index.component.js"
$ echo ${FILENAME%%.*}
index

ファイル名を削除して、拡張子のみを残すこともできます。 その場合、ハッシュ(#)演算子を使用して最初から開始する必要があります。

$ declare -r FILENAME="index.component.js"
$ echo ${FILENAME#*.}
component.js

前の例と同様に、最後の拡張子のみを残したい場合は、ダブルハッシュを使用する必要があります。

$ declare -r FILENAME="index.component.js"
$ echo ${FILENAME##*.}
js

3.4. 一致した部分文字列を置き換える

部分文字列を削除する代わりに、スラッシュ(/)演算子を使用して置き換えることができます。 singular演算子は最初の一致を変更し、double演算子はすべての一致を変更します。 どちらも可能な限り長い部分文字列と一致します。

拡張子をそのままにしてファイル名を変更するコードを書いてみましょう。

$ declare -r FILENAME="index.component.js"
$ echo ${FILENAME/*./index.}
index.js

4. ケーススタディ

上記の機能を有効に活用しましょう。 提供されたファイル内のすべての古いバージョンの文字列(たとえば、1.0.1から1.1.0)を更新し、名前に古いバージョンが添付された古いファイルのバックアップを保持するスクリプトを記述します。

ファイル名とバージョンの文字列を引数としてスクリプトに渡しますが、コードの残りの部分を読みやすくするために、それらを再宣言します。 最後に、リダイレクトメカニズムを使用して、変更されたコンテンツを保存します。

#!/bin/sh
declare -r FILENAME=$1
declare -r OLD_VERSION=$2
declare -r NEW_VERSION=$3
declare -r BACKUP_FILENAME=${FILENAME%.*}'_'$OLD_VERSION'.'${FILENAME##*.}
declare -r CONTENT=`cat $FILENAME`

cp $FILENAME $BACKUP_FILENAME
echo "${CONTENT//$OLD_VERSION/$NEW_VERSION}" > $FILENAME

5. 概要

このチュートリアルでは、宣言から置換まで、外部ツールを使用せずに純粋なBashで文字列を操作する方法を学びました。