Замена совпадающей строки, расположенной после другой определенной строки в файле

Если я правильно понял, я думаю, что вы хотите в основном перебирать списки значений, а затем readеще одно в цикле.

Вот несколько вариантов, 1 и 2, наверное, самые разумные.

1. Эмулировать массивы строками

Было бы неплохо иметь 2D-массивы, но в Bash это невозможно. Если в ваших значениях нет пробелов, один обходной путь для аппроксимации состоит в том, чтобы вставить каждый набор из трех чисел в строку и разделить строки внутри цикла:

for x in "1 2 3" "4 5 6"; do 
  read a b c <<< "$x"; 
  read -p "Enter a number: " d
  echo "$a - $b - $c - $d ";
done

Конечно, можно использовать и другой разделитель, например. for x in 1:2:3...и IFS=: read a b c <<< "$x".


2. Заменить канал другим перенаправлением на свободный стандартный ввод

Другая возможность состоит в том, чтобы read a b cчитался из другого fd и направлял ввод в этот (это должно работать в стандартной оболочке):

while read a b c <&3; do
    printf "Enter a number: "
    read d
    echo "$a - $b - $c - $d ";
done 3<

И здесь вы также можете использовать подстановку процесса, если хотите получить данные из команды:while read a b c <&3;...done 3< <(echo $'1 2 3\n4 5 6')(подстановка процесса — это функция bash/ksh/zsh)


3. Вместо этого брать пользовательский ввод из stderr

Или, наоборот, используя канал, как в вашем примере, но вводите пользовательский ввод readизstderr(fd 2 )вместо stdin, откуда идет канал:

echo $'1 2 3\n4 5 6' |
while read a b c; do 
    read -u 2 -p "Enter a number: " d
    echo "$a - $b - $c - $d ";
done

Чтение из stderrнемного странно, но на самом деле часто работает в интерактивном сеансе. (Вы также можете явно открыть /dev/tty, предполагая, что вы действительно хотите обойти любые перенаправления, это то, что такие вещи, как less, используют для получения ввода пользователя, даже если данные передаются ему.)

Хотя использование stderrтаким образом может работать не во всех случаях, и если вы используете какую-то внешнюю команду вместо read, вам, по крайней мере, потребуется добавить к команде несколько перенаправлений.

Также см. Почему моя переменная локальна в одном цикле while read, но не в другом похожем цикле? по некоторым вопросам, касающимся ... | while.


4. Разрезать части массива по мере необходимости

Я полагаю, вы также можете аппроксимировать двумерный -массив путем копирования срезов обычного -многомерного:

data=(1 2 3 
      4 5 6)

n=3
for ((i=0; i < "${#data[@]}"; i += n)); do
    a=( "${data[@]:i:n}" )
    read -p "Enter a number: " d
    echo "${a[0]} - ${a[1]} - ${a[2]} - $d "
done

Вы также можете назначить ${a[0]}и т. д. на a, bи т. д., если вам нужны имена для переменных, но Zsh сделает это намного лучше .

1
29.07.2020, 13:33
4 ответа
$ awk -v org='ABCDE-12345' -v RS= -v ORS='\n\n' '
    $1 == "[dn]" {
        $0 = "\n" $0
        sub(/\nOrganizationID[[:space:]]*=[^\n]*/,"\nOrganizationID = " org)
        sub(/^\n/,"")
    }
    { print }
' file
oid_section = OIDs

[req]
default_bits = 4096
prompt = no
default_md = sha256
distinguished_name = dn

attributes = v3_req

[OIDs]
OrganizationID = 2.5.4.97        # Don't touch this

[dn]
C = FO
ST = Foobar Monarchy
O = Lorem Ipsum
CN = specific.domain.com
OrganizationID = ABCDE-12345
#...

[v3_req]
#...

RS=<null>помещает awk в пустой -строку -режим разделенного абзаца (см. RS = ""в https://www.gnu.org/software/gawk/manual/gawk.html#Multiple-Line), ORS='\n\n'обеспечивает пустой -вывод, разделенный строкой, $0 = "\n" $0гарантирует, что каждая строка запись имеет новую строку перед ней, так что последующее sub()с использованием окружающих новых строк может быть успешным, даже если OrganizationIDявляется первой строкой записи, а затем sub(/^\n/,"")удаляет временную новую строку, которую мы добавили на первом шаге.

3
18.03.2021, 23:16

Метод 1

for  i in `sed -n '/OrganizationID/{;=;}' file| sed -n '2p'`; do sed ''$i's/.*/OrganizationID = ABCDE-98765/g' file; done

Питон

#!/usr/bin/python
import re
j=[]
t=0
h=re.compile(r'OrganizationID.*')
k=open('file','r')
for i in k:
    if re.search(h,i):
        t=t+1
        if (t == 2):
            y=re.sub(h,"OrganizationID = ABCDE-98765",i)
            print y.strip()
        else:
            print i.strip()
    else:
        print i.strip()

выход

oid_section = OIDs

[req]
default_bits = 4096
prompt = no
default_md = sha256
distinguished_name = dn

attributes = v3_req

[OIDs]
OrganizationID = 2.5.4.97        # Don't touch this

[dn]
C = FO
ST = Foobar Monarchy
O = Lorem Ipsum
CN = specific.domain.com
OrganizationID = ABCDE-98765
0
18.03.2021, 23:16

Вот один из способов выполнить эту задачу с помощью Awk. Мы помещаем REQ _ID в среду командной строки awk, откуда он становится доступным во встроенном ассоциативном массиве ENVIRON

$ REQ_ID="ABCDE-98765" \
   awk '
     BEGIN { a[1] = "OrganizationID = " ENVIRON["REQ_ID"] }
     $1 == "[dn]", match($0, /^OrganizationID[[:blank:]]*=/) {
       a[0] = $0
       $0 = a[RSTART]
     }1
  ' ssl.cfg

С sed нужно еще немного поработать, потому что мы должны убедиться, что REQ _ID можно поместить в правую часть команды s///.

$ REQ_ID="ABCDE-98765"; s="[[:blank:]]" 
$ req_id_esc=$(printf '%s\n' "$REQ_ID" | sed -e 's:[\&/]:\\&:g')
$ sed -e "/^[[]dn]/,/^OrganizationID$s*=/s/^\(OrganizationID\)$s*=.*/\1 = ${req_id_esc}/" ssl.cfg
$ REQ_ID="ABCDE-98765" \
   perl -pale '
      s//$1 = $ENV{REQ_ID}/ if 
        $F[0] eq "[dn]"... /^(OrganizationID)\s*=.*/ and
        defined $1' ssl.cfg
0
18.03.2021, 23:16

Следующий код awkизменит значение строки, начинающейся с OrganizationID, на любое, которое вы выберете, но только в разделе [dn]и только при первом появлении этого ключа:

awk -F'=' -v OFS='=' '$0=="[dn]"{f=1} (f)&&$1~/^OrganizationID/{$2="Something-New"; f=0} 1' ssl.cfg

Если вы хотите избежать жесткого -кодирования замены, вы можете импортировать ее, как в

req_id="Something-New"
awk -F'=' -v OFS='=' -v repl="$req_id" '$0=="[dn]"{f=1} (f)&&$1~/^OrganizationID/{$2=repl; f=0} 1' ssl.cfg

(, но обратите внимание, что управляющие последовательности в переменной будут интерпретироваться awk, поэтому, если вам нужны буквальные обратные косые черты, необходимы особые меры предосторожности. См. этот ответ на StackOverflow для дальнейшего чтения ).

Приведенный выше код делает следующее:

  • Он рассматривает =как разделитель полей для ввода(-F'=')и вывода (-v OFS='='), поэтому внутри строка будет разбиваться на каждый =на «столбцы», а выходные строки будут собираться путем объединения записи поля с =между ними.

  • Если вся текущая строка($0)состоит только из заголовка раздела [dn], флаг fбудет установлен в 1, чтобы указать, что правильный раздел был найден.

  • Если мы находимся в правильном разделе, и первое поле ($1, т.е. строка перед первым=)начинается с OrganizationID, то второе поле ($2, т.е. строка после первого=)] заменяется содержимым переменной awkrepl, которую мы установили на содержимое оболочки переменной $req_idс помощью опции -v repl="$req_id". Он также сбрасывает флаг, поэтому мы не заменяем значение для любого другого случая OrganizationID.

  • «Одинокий» 1за пределами блоков правил {... }является сокращенным обозначением и означает «печатать текущую строку со всеми изменениями, сделанными предыдущими правилами». Идея этого механизма на самом деле заключается в том, что вы можете указать любое условие за пределами блоков правил , которое, если оно оценивается какtrue(или не равно -нулю ), заставляет awkпечатать текущий строка (например awk 'FNR==1'напечатает только первую строку, потому что внутренний счетчик строк FNRравен 1только в этой строке ).

    Обратите внимание, что без какой-либо явной команды print/ printfвнутри блоков правил или «глобального» условия, оценивающего значение true, awkбудет , а не печатать текущую строку.

Вывод для вашего примера:

oid_section = OIDs

[req]
default_bits = 4096
prompt = no
default_md = sha256
distinguished_name = dn

attributes = v3_req

[OIDs]
OrganizationID = 2.5.4.97        # Don't touch this

[dn]
C = FO
ST = Foobar Monarchy
O = Lorem Ipsum
CN = specific.domain.com
OrganizationID =Something-New
#...

[v3_req]

Этот код не изменит файл, а выведет результат на консоль (, чтобы вы могли сначала проверить его правильность ). Как только вы будете удовлетворены,либо перенаправьте вывод в новый файл, а затем замените вручную, либо -, если ваш awkподдерживает это -, используйте расширение -i inplace(, работает с последними реализациями GNU awk).

awk -F"=" -v OFS="=" '.. etc..' ssl.cfg > ssl.cfg.new

или

awk -i inplace -F"=" -v OFS="=" '.. etc..' ssl.cfg
3
18.03.2021, 23:16

Теги

Похожие вопросы