Эффективно обрабатывайте строки в сценариях автоматизации с помощью этих синтаксисов.

Bash стал языком автоматизации по умолчанию для каждой Unix-подобной или основанной на Unix операционной системе. Каждый системный администратор, инженер DevOps и программист обычно использует Bash для написания сценариев оболочки с повторяющимися последовательностями команд. Сценарии Bash обычно содержат команды, запускающие другие двоичные файлы программ. В большинстве сценариев нам может потребоваться обработать данные и создать логический поток в сценарии оболочки. Таким образом, нам часто приходится добавлять условные операторы и операторы обработки текста в наши сценарии оболочки.

Традиционные сценарии Bash и прошлые программисты, которые использовали более старые версии интерпретатора Bash, обычно использовали команды awk, sed, tr и cut для работы с текстом. Это отдельные программы. Несмотря на то, что эти программы обработки текста предлагают хорошие возможности, они замедляют ваш сценарий Bash, поскольку каждая конкретная команда имеет значительное время запуска процесса. Современные версии Bash предлагают встроенные функции обработки текста с помощью известной функции расширения параметров.

В этой статье я объясню некоторые встроенные синтаксис манипулирования строками, которые вы можете использовать для продуктивной обработки текста в сценариях Bash.

Извлечение и замена подстроки

Подстрока относится к заразному сегменту или части конкретной строки. В различных сценариях сценариев нам нужно извлекать подстроки из сегментов строки. Например, вам может понадобиться получить только сегмент имени файла из полного имени файла, состоящего из расширения. Кроме того, вам может понадобиться заменить подстроки определенными сегментами строки (т. е. изменить расширение имени файла).

Извлечение подстроки очень просто, если указать позицию и длину символа:

#!/bin/bash

str="2023-10-12"

echo "${str:5:2}" # 10
echo "${str::4}" # 2023
echo "2022-${str:5}" # 2022-10-12

Вы даже можете выполнять вычисления подстроки с правой стороны, как показано ниже:

#!/bin/bash

str="backup.sql"

echo "original${str:(-4)}" # original.sql

Bash также предлагает продуктивный встроенный синтаксис для замены подстроки:

#!/bin/bash

str="obin-linux_x64_bin"

echo "${str/x64/armhf}" # obin-linux_armhf_bin
echo "${str/bin/dist}" # odist-linux_x64_bin
echo "${str//bin/dist}" # odist-linux_x64_dist

При работе с некоторыми строками, такими как имена файлов, пути и т. д., вам может потребоваться заменить префиксы и суффиксы строк. Хорошим примером является замена расширения файла другим расширением. Посмотрите на следующий пример:

#!/bin/bash

str="db_config_backup.zip"

echo "${str/%.zip/.conf}" # db_config_backup.conf
echo "${str/#db/settings}" # settings_config_backup.zip

В приведенных выше примерах замены подстроки мы использовали точный сегмент подстроки для сопоставления, но вы также можете использовать часть подстроки, используя подстановочный знак * следующим образом:

#!/bin/bash

str="db_config_backup.zip"

echo "${str/%.*/.bak}" # db_config_backup.bak
echo "${str/#*_/new}" # newbackup.zip

Приведенный выше подход полезен, если вы не знаете точную подстроку для поиска.

Совпадения регулярных выражений, извлечения и замены

Как уже известно многим пользователям Unix или GNU/Linux, можно использовать grep и sed для поиска текста на основе регулярных выражений. sed помогает нам выполнять замену регулярных выражений. Вы можете использовать встроенные функции регулярных выражений Bash для обработки текста быстрее, чем эти внешние двоичные файлы.

Вы можете выполнить сопоставление регулярного выражения с условием if и оператором =~, как показано в следующем фрагменте кода:

#!/bin/bash

str="db_backup_2003.zip"

if [[ $str =~ 200[0-5]+ ]]; then
    echo "regex_matched"
fi

Вы также можете заменить оператор if на встроенное условие, если хотите:

[[ $str =~ 200[0-5]+ ]] && echo "regex_matched"

Как только интерпретатор Bash выполняет сопоставление с регулярным выражением, он обычно сохраняет все совпадения в переменной оболочки BASH_REMATCH. Эта переменная является массивом только для чтения и хранит все совпадающие данные в первом индексе. Если вы используете подшаблоны, Bash постепенно сохраняет эти совпадения в других индексах:

#!/bin/bash

str="db_backup_2003.zip"

if [[ $str =~ (200[0-5])(.*)$ ]]; then
    echo "${BASH_REMATCH[0]}" # 2003.zip
    echo "${BASH_REMATCH[1]}" # 2003
    echo "${BASH_REMATCH[2]}" # .zip
fi

Помните, мы использовали подстановочные знаки с предыдущим соответствием подстроки? Точно так же можно использовать определения регулярных выражений внутри расширений параметров, как показано в следующем примере:

#!/bin/bash

str="db_backup_2003.zip"
re="200[0-3].zip"

echo "${str/$re/new}.bak" # db_backup_new.bak

Методы удаления подстроки

Нам часто нужно предварительно обрабатывать текстовые сегменты, удаляя ненужные подстроки во многих требованиях к обработке текста. Например, если вы извлекаете номер версии с префиксом v и некоторыми номерами сборки и хотите найти основной номер версии, вам придется удалить некоторые подстроки. Вы можете использовать тот же синтаксис замены подстроки, но опустить параметр строки замены для удаления строки следующим образом:

#!/bin/bash

str="ver5.02-2224.e2"

ver="${str#ver}"
echo $ver # 5.02-2224.e2

maj="${ver/.*}"
echo $maj # 5

В приведенном выше примере мы использовали точную подстроку и подстановочный знак для удаления подстроки, но вы также можете использовать регулярные выражения. Проверьте, как извлечь чистый номер версии без лишних символов:

#!/bin/bash

str="ver5.02-2224_release"

ver="${str//[a-z_]}"
echo $ver # 5.02-2224

Преобразования регистра и переменные, основанные на регистре

Даже стандартный язык C предлагает функцию для преобразования регистра символа. Почти все современные языки программирования предоставляют встроенные функции для преобразования регистра. В качестве командного языка Bash не предлагает функций для преобразования регистра, но предоставляет нам возможности преобразования регистра посредством раскрытия параметров и объявления переменных.

Посмотрите на следующий пример, который преобразует регистр букв:

#!/bin/bash

str="Hello Bash!"

lower="${str,,}"
upper="${str^^}"

echo $lower # hello bash!
echo $upper # HELLO BASH!

Вы также можете использовать верхний или нижний регистр только для первого символа конкретной строки следующим образом:

#!/bin/bash

ver1="V2.0-release"
ver2="v4.0-release"

echo "${ver1,}" # v2.0-release
echo "${ver2^}" # V4.0-release

Если вам нужно сделать определенную переменную строго прописной или строчной, вам не нужно постоянно запускать функцию преобразования регистра. Вместо этого вы можете добавить атрибуты case к определенной переменной с помощью встроенной команды declare, как показано в следующем примере:

#!/bin/bash

declare -l ver1
declare -u ver2

ver1="V4.02.2"
ver2="v2.22.1"

echo $ver1 # v4.02.2
echo $ver2 #V2.22.1

Приведенные выше переменные ver1 и ver2 получают атрибут case во время объявления, поэтому всякий раз, когда вы присваиваете значение определенной переменной, Bash преобразует текстовый регистр на основе атрибутов переменных.

Разделение строк (преобразование строки в массив)

Bash позволяет определять индексированные и ассоциативные массивы с помощью встроенного declare. Большинство языков программирования общего назначения предлагают метод split в строковом объекте или через функцию стандартной библиотеки (функция strings.Split в Go). Вы можете разделить строку и создать массив несколькими способами в Bash. Например, мы можем изменить IFS на нужный разделитель и использовать встроенный read. Или мы можем использовать команду tr с циклом и построить массив. Или использование встроенного расширения параметров — еще один подход. В Bash так много подходов к разбиению строк.

Использование IFS и read — один из самых простых и безошибочных способов разбить строку:

#!/bin/bash

str="C,C++,JavaScript,Python,Bash"

IFS=',' read -ra arr <<< "$str"

echo "${#arr[@]}" # 5
echo "${arr[0]}" # C
echo "${arr[4]}" # Bash

Приведенный выше фрагмент кода использует , в качестве разделителя разделения и использует встроенную команду read для создания массива на основе IFS.

Несмотря на то, что есть простейшие способы обработки разделения без read, убедитесь, что нет скрытых проблем. Например, следующая реализация разделения настолько проста, но она ломается, когда вы включаете * (расширяется до содержимого текущего каталога) в качестве элемента и пробела в качестве разделителя:

#!/bin/bash

# WARNING: This code has several hidden issues.

str="C,Bash,*"

arr=(${str//,/ })

echo "${#arr[@]}" # contains current directory content

Изучите современные методы написания сценариев Bash с помощью следующей истории:



Спасибо за прочтение.

Повышение уровня кодирования

Спасибо, что являетесь частью нашего сообщества! Перед тем, как ты уйдешь:

  • 👏 Хлопайте за историю и подписывайтесь на автора 👉
  • 📰 Смотрите больше контента в публикации Level Up Coding
  • 💰 Бесплатный курс собеседования по программированию ⇒ Просмотреть курс
  • 🔔 Подписывайтесь на нас: Twitter | ЛинкедИн | "Новостная рассылка"

🚀👉 Присоединяйтесь к коллективу талантов Level Up и найдите прекрасную работу