Если я правильно понял, я думаю, что вы хотите в основном перебирать списки значений, а затем 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 сделает это намного лучше .
$ 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/,"")
удаляет временную новую строку, которую мы добавили на первом шаге.
Метод 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
Вот один из способов выполнить эту задачу с помощью 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
Следующий код 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
, т.е. строка после первого=
)] заменяется содержимым переменной awk
repl
, которую мы установили на содержимое оболочки переменной $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