Как указано в других сообщениях, наиболее распространенные оболочки не позволяют устанавливать переменные среды с точками в имени. Однако я сталкивался с ситуациями, особенно с Docker и вызываемыми программами, когда программному обеспечению требовались значения ключей с точками.
Однако в каждой из этих ситуаций эти пары ключей -значений могут быть переданы в программу с помощью других средств, а не только переменных среды. Например, в Ant вы можете использовать «-файл свойств (имя файла )» для передачи отформатированного набора значений ключей -в файле свойств. Confd позволяет использовать «-бэкэнд-файл -файл (файл yaml )».
Я передал переменные окружения в виде "C __любое _значение='my.property.key=значение'". Затем я переключил вызов программы, чтобы сначала сгенерировать файл :
.set | awk -- 'BEGIN { FS="'\''" } /^C__/ {print $2}' > my-key-values.txt
Команда set
в оболочке Borne выводит каждое свойство в отдельной строке в виде
C__any_value='my.property.key=the value'
Команда awk
обработает только переменные среды, начинающиеся с C__
, а затем извлечет значения, заключенные в одинарные кавычки.
Этот метод требует, чтобы значение переменной среды было установлено в точной форме, которая требуется программе обработки. Кроме того, если значение или ключ вашего свойства будут содержать одинарные кавычки, вам нужно будет изменить символ-разделитель полей awk на что-то, что, как вы знаете, не будет отображаться, и окружить значение этим символом.Например, чтобы использовать %
в качестве разделителя:
$ C__1="%my.key=the'value%"
$ set | awk -- 'BEGIN { FS="%" } /^C__/ {print $2}'
my.key=the'"'"'value
(точный вывод будет зависеть от вашей оболочки. )Вам потребуется предпринять дополнительные шаги, чтобы расшифровать экранирование цитаты.
Вот несколько альтернатив, которые вы можете использовать.
$ sed -e '
s/|[^|]*$/\n&/
s/\n|/\n/
y/\n|/| /
' file
$ perl -pe 's/\|/ / until tr/|/|/ == 1' file
$ perl -pe 'my $k=tr/|/|/; s/\|/ / while $k-->1' file
Без петли:
sed 's/\(.*\)|/\1\
/; s/|/ /g; s/\
/|/'
Подход, отличный от явного цикла Квазимодо в sed
:
$ sed 'h; s/.*|//; x; s/|[^|]*$//; y/|/ /; G; y/\n/|/' file
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |406 RCO 301
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |0
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |0
Для каждой строки это сохраняет строку в ячейке удержания с помощью h
, а затем удаляет все в строке до последней строки |
включительно. Затем он заменяет исходную копию строки и удаляет последнюю |
и все после нее.
Пространство шаблона теперь содержит исходную первую часть строки, а пространство хранения содержит последнюю часть строки.
Первая команда y///
заменяет все остальные |
пробелами. G
добавляет пробел в конец пространства шаблона с символом новой строки в -между ними. Вторая команда y///
преобразует этот символ новой строки в |
, и все готово.
Выполнение ограниченного (фиксированного )числа s///
замен и использование более быстрой команды y///
, когда это возможно, означает, что это выполняется быстрее, чем явное изменение цикла (~2,3 секунды для данных объемом 50 МБ, по сравнению до ~7,8 секунды для тех же данных с циклом, используя GNU sed
на моей машине ).
Интересно, что использование обратной ссылки в явном варианте цикла, как это сделали и я, и Исаак, замедляет его еще больше (~33 с с вариантом Исаака и ~29 с с моим(в комментариях ), на том же наборе данных и при тех же условиях, что и выше ).
Используя awk
, этот почти заменяет все |
разделители, кроме последнего, пробелом. «Почти», так как вставляет пробел перед последним |
.
$ awk -F '|' 'BEGIN { OFS = " " } { $NF = "|" $NF; print }' file
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |406 RCO 301
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |0
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |0
Он считывает каждую строку как набор|
-полей с разделителями,добавляет символ |
к началу последнего поля и печатает результирующую запись с пробелами для разделителей полей.
С учетом поведения awk
по умолчанию (пробел является разделителем поля вывода по умолчанию, а разделитель поля ввода доступен какFS
):
awk -F '|' '{ $NF = FS $NF; print }' file
или, немного короче, любезно предоставлено @Isaac,
awk -F '|' '{ $NF = FS $NF }; 1' file
sed ':a;/[|].*[|]/s/[|]/ /;ta' file
/[|].*[|]/
:Если линия состоит из двух труб, s/[|]/ /
:Замените первое пробелом. ta
:Если была произведена замена, вернитесь к :a
. Выход:
$ sed ':a;/[|].*[|]/s/[|]/ /;ta' file
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |406 RCO 301
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |0
FLD1 SFK TK FLD2 FLD4 FLD5 - 20200515 NNNN |0
Как заметил @steeldriver, вы можете использовать просто |
вместо [|]
в базовом регулярном выражении (BRE ), как в случае выше. Если вы добавите флаг -E
в sed, расширенное регулярное выражение (ERE )будет включено, и тогда вам нужно будет написать [|]
или \|
.
Для полноты картиныСпецификация POSIX sed говорит, что «Команды редактирования, отличные от {...}, a, b, c, i, r, t, w, :, и#может сопровождаться точкой с запятой". Тогда совместимой альтернативой вышеизложенному является:
sed -e ':a' -e '/[|].*[|]/s/[|]/ /;t a' file
Письмо:
sed -e ':a' -e '/|\(.*|\)/s// \1/;ta' file
-e ':a'
определить метку(a
)для перехода. -e '
запустить другой раздел скрипта. /|\(.*|\)/
одно регулярное выражение соответствует двум |
, все посередине и захватить все промежуточное и последнее |
. s// \1/
заменить все совпадающее выше тем, что было захвачено. ;ta
повторите цикл. ' file
для указанного имени файла. Для измерения скорости всех вариантов (от более быстрого к более медленному )вы можете использовать:
#! /bin/bash
TIMEFORMAT='run : %lR sec'
read -d '' str <<\END
FLD1 |SFK TK |FLD2 |FLD4 |FLD5 |- |20200515 |NNNN |406 RCO 301
FLD1 |SFK TK |FLD2 |FLD4 |FLD5 |- |20200515 |NNNN |0
FLD1 |SFK TK |FLD2 |FLD4 |FLD5 |- |20200515 |NNNN |0'
END
n=${1:-100}; printf "$str"'%.0s\n' $(seq "$n") > file
time perl -pe 's/\|(?=.*\|)/ /g' file >/dev/null
time sed -E ':a;/\|.{1,}\|/s/\|/ /1;ta' file >/dev/null
time sed 'h; s/.*|//; x; s/|[^|]*$//; y/|/ /; G; y/\n/|/' file >/dev/null
time sed 's/\(.*\)|/\1\x00/;y/|/ /;s/\x00/|/1' file >/dev/null
time sed 's/\(.*\)|/\1\n/;y/|/ /;s/\n/|/1' file >/dev/null
Используется как:
$./testbash.sh 235000
run : 0m07.676s sec
run : 0m17.753s sec
run : 0m22.074s sec
run : 0m24.036s sec
run : 0m24.047s sec
Используя Perl, вы можете запустить что-то вроде
perl -pe 's/\|(?=.*\|)/ /g' ex
где:
perl -pe
действие --выполняет действие и печатает \|(?=.*\|)
— это регулярное выражение, которое сопоставляет |
с неиспользованным просмотром (?=.*|)
, содержащим другое|