Применить замену N раз - PullRequest
       2

Применить замену N раз

0 голосов
/ 17 декабря 2018

У меня есть строка, подобная

data1_data2_data3_data4@data5,data6

Бывает, что иногда data5 содержит подчеркивания, которые являются разделителями полей.Ужасно, я знаю.

Я хочу прочитать эти фрагменты данных примерно так:

IFS="_@," read d1 d2 d3 d4 d5 d6 <<< "$input"

Проблема возникает, когда data5 содержит подчеркивание.Чтобы обойти эту проблему.Я хочу заменить первые три подчеркивания запятыми (и @ тоже).Я нашел простой способ с помощью sed:

sed 's/_/,/; s/_/,/; s/_/,/; s/@/,/' <<< "$input"

Но повторение три раза одной и той же замены кажется совершенно неэффективным.Что произойдет, если мне нужно будет повторить это 5000 раз?

Можно ли как-нибудь сказать sed повторить подстановку определенное количество раз?

Чтобы завершить, пример ввода:

input="data1_data2_data3_data4@d_a_t_a_5,data6"
IFS="," read d1 d2 d3 d4 d5 d6 <<< "$input"

Ожидаемый результат:

d1=="data1"
d2=="data2"
d3=="data3"
d4=="data4"
d5=="d_a_t_a_5"
d6=="data6"

Ответы [ 5 ]

0 голосов
/ 17 декабря 2018

Если у вас есть более одного раза поле с @. _. _....,
Вы можете попробовать это awk:

echo "data1_data2@d_a_t_a_17,data3_data4@d_a_t_a_5,data6_data7" |
awk '
{
i = split ( $0 , a , "_" )
for ( j = 1 ; j <= i ; j++ )
  if ( a[j] !~ /@/ )
    print "d" ++k "==\"" a[j] "\""
  else
    {
      split ( a[j] , b , "@" )
      print "d" ++k "==\"" b[1] "\""
      sub ( ".*@" , "" , a[j] )
      while ( a[j] !~ "," )
        {
          c = c a[j] "_"
          j++
        }
        split ( a[j] , b , "," )
        c = c b[1]
        print "d" ++k "==\"" c "\""
        a[j] = b[2]
        j--
        c = ""
    }
}'
0 голосов
/ 17 декабря 2018

Используйте awk.

$ input="data1_data2_data3_data4@d_a_t_a_5,data6"
$ awk -v RS='[@\n]' '{ if(NR % 2){ gsub(/_/, ","); ORS = "," } else ORS = "\n" } 1' <<< "$input"
data1,data2,data3,data4,d_a_t_a_5,data6
0 голосов
/ 17 декабря 2018

Вы можете использовать это awk в подстановке процесса:

input="data1_data2_data3_data4@d_a_t_a_5,data6"

IFS=, read d1 d2 d3 d4 d5 d6 < <(awk -F@ -v OFS=, -v n=3 '{
while (i++<n) sub(/_/, ",", $1)} 1' <<< "$input")

# check variable values
declare -p d1 d2 d3 d4 d5 d6

declare -- d1="data1"
declare -- d2="data2"
declare -- d3="data3"
declare -- d4="data4"
declare -- d5="d_a_t_a_5"
declare -- d6="data6"
  • awk команда использует @ в качестве разделителя полей.
  • awk команда заменяет _ на , в только в 1-м поле и точно n раз.
0 голосов
/ 17 декабря 2018

В bash я бы использовал вместо этого регулярное выражение.

$ cat input
one_two_three_fourpt1_fourpt2@fivept1_fivept2,six
$ regex='([^_]+)_([^_]+)_([^_]+)_(.+)@([^,]+).(.*)'
$ while IFS= read -r line; do
> [[ $line =~ $regex ]]
> done < input
$ printf '%s\n' "${BASH_REMATCH[@]}"
one_two_three_fourpt1_fourpt2@fivept1_fivept2,six
one
two
three
fourpt1_fourpt2
fivept1_fivept2
six

Элемент ноль BASH_REMATCH содержит полное совпадение;остальные элементы содержат отдельные группы захвата слева.

В качестве альтернативы, вы можете использовать read, чтобы сначала разделить на @, а затем снова, чтобы разделить две половины, используя _ и ,при необходимости.

$ IFS="@" read -r first second <<< "$line"
$ IFS=_ read -r f1 f2 f3 f4 <<< "$first"
$ IFS=, read -r f5 f6 <<< "$second"

Поскольку у второго вызова read только 4 аргумента, f4 будет содержать все, что следует за 3-м _, без дальнейшего разделения поля на дополнительные _ s.


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

0 голосов
/ 17 декабря 2018

может быть возможность разделить вручную, используя расширение оболочки. ${var%%pat} удаляет самый большой суффикс, совпадающий с шаблоном, и ${var#pat} удаляет самый короткий префикс, совпадающий с патчем

while IFS= read line; do
    tmpline=$line
    d1=${tmpline%%_*} tmpline=${tmpline#*_}
    d2=${tmpline%%_*} tmpline=${tmpline#*_}
    d3=${tmpline%%_*} tmpline=${tmpline#*_}
    d4=${tmpline%%@*} tmpline=${tmpline#*@}
    d5=${tmpline%%,*} tmpline=${tmpline#*,}
    d6=${tmpline}

    printf "%s\n" "d1=$d1" "d2=$d2" "d3=$d3" "d4=$d4" "d5=$d5" "d6=$d6"
done <<< "$input"

, или, чтобы обойти медленную скорость чтения bash, разбивает строки вручную.

tmpinput=$input
while [[ $tmpinput ]]; do
    if [[ $tmpinput = *$'\n'* ]]; then
        tmpline=${tmpinput%%$'\n'*} tmpinput=${tmpinput#*$'\n'}
    else
        tmpline=${tmpinput} tmpinput=''
    fi

    d1=${tmpline%%_*} tmpline=${tmpline#*_}
    d2=${tmpline%%_*} tmpline=${tmpline#*_}
    d3=${tmpline%%_*} tmpline=${tmpline#*_}
    d4=${tmpline%%@*} tmpline=${tmpline#*@}
    d5=${tmpline%%,*} tmpline=${tmpline#*,}
    d6=${tmpline}

    printf "%s\n" "d1=$d1" "d2=$d2" "d3=$d3" "d4=$d4" "d5=$d5" "d6=$d6"
done 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...