sed-i sh единственная строка для выполнения арифметики c внутри подстановки - PullRequest
4 голосов
/ 06 августа 2020

У меня есть строка формы FOO_123_BAR.bazquux, где FOO и BAR - фиксированные строки, 123 - число, а bazquux - текст произвольной формы.

Мне нужно выполнить преобразование текста в этой строке: извлечь 123 и bazquux, увеличить число и затем расположить их в другой строке. Например, FOO_123_BAR.bazquuxFOO=124 BAR=bazquux. (Фактическое преобразование более сложное.)

Естественно, я могу сделать это в последовательности вызовов sed и expr, но это некрасиво:

shopt -s lastpipe

in=FOO_123_BAR.bazquux
echo "$in" | sed -r 's|^FOO_([0-9]+)_BAR\.(.+)$|\1 \2|' | read number text
out="FOO=$((number + 1)) BAR=$text"

Есть ли более мощный инструмент обработки текста что может сделать работу за один вызов? Если да, то как?

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

Ответы [ 6 ]

6 голосов
/ 06 августа 2020

С помощью GNU sed вы можете выполнить всю заменяющую строку как внешнюю команду с помощью флага e.

$ s='FOO_123_BAR.bazquux'
$ echo "$s" | sed -E 's/^FOO_([0-9]+)_BAR\.(.+)$/echo FOO=$((\1 + 1)) BAR=\2/e'
FOO=124 BAR=bazquux

Чтобы избежать конфликта с метасимволами оболочки, вам необходимо заключить в кавычки неизвестные части:

$ s='FOO_123_BAR.$x(1)'
$ echo "$s" | sed -E 's/^FOO_([0-9]+)_BAR\.(.+)$/echo FOO=$((\1 + 1)) BAR=\2/e'
sh: 1: Syntax error: "(" unexpected

$ echo "$s" | sed -E 's/^FOO_([0-9]+)_BAR\.(.+)$/echo FOO=$((\1 + 1)) BAR=\x27\2\x27/e'
FOO=124 BAR=$x(1)
5 голосов
/ 06 августа 2020

Использование любого awk в любой оболочке в каждом поле UNIX и при условии, что ни одна из ваших подстрок не содержит _ или .:

$ s='FOO_123_BAR.bazquux'
$ echo "$s" | awk -F'[_.]' '{print $1"="$2+1,$3"="$4}'
FOO=124 BAR=bazquux
4 голосов
/ 06 августа 2020

Вы можете сделать это с помощью perl:

perl -pe 's|^FOO_([0-9]+)_BAR\.(.+)$|"FOO=" . ($1 + 1) . " BAR=" . $2|e' <<< "$in"

См. Онлайн-демонстрацию

($1 + 1) будет увеличивать число, захваченное в группе 2.

3 голосов
/ 06 августа 2020

Не могли бы вы попробовать следующие, написанные и протестированные с показанными образцами в GNU awk.

1-е решение: Добавление решения с функцией match awk.

echo "FOO_123_BAR.bazquux" | 
awk '
match($0,/FOO_[0-9]+_BAR/){
  split(substr($0,RSTART,RLENGTH),array,"_")
  print array[1]"="array[2]+1,array[3] "=" substr($0,RSTART+RLENGTH+1)
}'

2-е решение:

echo "FOO_123_BAR.bazquux" | 
awk '
BEGIN{
  FS="_"
}
{
  $2+=1
  sub(/_/,"=")
  sub(/_/," ")
  sub(/\./,"=")
}
1'
2 голосов
/ 07 августа 2020

Чистый bash однострочник будет

[[ $s =~ FOO_([0-9]+)_BAR\.(.*) ]] && echo "FOO=$((BASH_REMATCH[1] + 1)) BAR=${BASH_REMATCH[2]}"

, если переменная s установлена ​​в строку, которая анализируется перед вызовом этой строки (s=FOO_123_BAR.bazquux).

1 голос
/ 06 августа 2020

Использование замены var:

in=FOO_123_BAR.bazquux
raw=(${in//_/ })
$ echo "$raw=$[raw[1]+1] ${raw[2]//./=}"
FOO=124 BAR=bazquux
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...