Разделите 1 аргумент на 2 аргумента, используя регулярное выражение в bash-скрипте

Вот моя ситуация. В настоящее время у меня есть скрипт, который принимает два аргумента: название книги и название главы. Например:

$ myscript book1 chap1

Теперь, по причинам, которые потребуют много времени для объяснения, я бы предпочел, чтобы мой сценарий мог принимать один аргумент следующего формата: {имя книги}.{Название главы}. Например:

$ myscript book1.chap1

Сложность для меня заключается в том, что я не знаю, как взять строку $1=abc.xyz и превратить ее в две отдельные переменные: $var1=abc и $var2=xyz. Как я могу это сделать?

6 ответов

Решение

Если это всего лишь два тега, вы можете использовать выражение bash

arg=$1
beforedot=${arg%.*}
afterdot=${arg#*.}

Это быстрее чем cut потому что это встроенная оболочка. Обратите внимание, что это помещает все перед первой последней точкой в beforedot и все после в afterdot,

РЕДАКТИРОВАТЬ:

Существует также конструкция подстановки / реинтерпретации, если вы хотите разделить на произвольное количество токенов:

string=a.b.c.d.e
tokens=(${string//\./ })

Вы заменяете точки пробелами, и тогда это интерпретируется как объявление массива + определение из-за круглых скобок.

Однако я обнаружил, что это менее переносимо для братьев и сестер и потомков. Например, это не работает в моей любимой оболочке, zsh,

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

echo "Third token: ${tokens[2]}"

Вы также можете перебирать их, разыменовывая весь массив с помощью [@]:

for i in ${tokens[@]}
do
    # do stuff
done

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

pattern='^([^.]*)\.(.*)'
[[ $1 =~ $pattern ]]
book=${BASH_REMATCH[1]}
chapter=${BASH_REMATCH[2]}

Группы захвата являются элементами в BASH_REMATCH массив. Элемент 0 содержит весь матч.

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

Если $arg содержит book.chap

read BOOK CHAP<<<$(IFS="."; echo $arg)

установит переменные BOOK и CHAP соответственно. При этом используется внутренний разделитель полей (IFS), который контролирует, как bash понимает границы слов. Если (скажем) у вас есть несколько разделителей в вашем оригинале $arg затем просто укажите дополнительные переменные, которые будут содержать результаты.

Отсюда:

$ IFS по умолчанию использует пробел (пробел, табуляция и перевод строки), но может быть изменен, например, для анализа файла данных, разделенных запятыми

#!/bin/bash

book=${1%.*}
chapter=${1#*.}

printf 'book: %s\nchapter: %s\n' "$book" "$chapter"

Подстановка шаблонов с расширением параметров оболочки

Есть много способов выполнить то, что вы пытаетесь сделать. Одним из способов, которые не описаны в других ответах, является замена шаблона.

Если вы знаете, что значение всегда будет правильно делиться на период, вы можете применить к шаблону подстановку, чтобы его можно было легко маркировать с помощью IFS. Например:

set -- foo.bar
myvar="${1/./ }"
echo $myvar

Это даст foo bar,

Вы можете использовать скобки, чтобы захватить две части; после этого вы можете использовать обратные ссылки, чтобы получить их снова. Синтаксис отличается в разных языках; проверьте http://www.regular-expressions.info/brackets.html урок по обратным ссылкам в целом.

Другие вопросы по тегам