Только для Bash:
# VERSION 1
while IFS=$'-' read -a line
do
printf '%s %s\n' ${line[@]}
done < infile > outfile
или ...
# VERSION 2
main(){
local IFS=$'-\n'
local a=( $( outfile
}
main
или ...
# VERSION 3
while IFS= read -r line
do
printf '%s\n' "${line/-/ }"
done < infile > outfile
Но будьте осторожны: см. этот пост , если вы думаете применить это к большим файлам.
Ради интереса, несколько тестов для файла среднего размера. Результаты следуют; Очевидно, что лучшим выбором будет 'tr', за ним следует sed, а затем awk. Лучший Bash - это версия 2 (в 625 раз медленнее, чем tr, а использование памяти в 82 раза выше). Для сравнения, sed в 7,5 раз медленнее, а awk в 9 раз медленнее, чем tr.
$ stat -c %s bigdata.txt && wc -l bigdata.txt
1439952
179994 bigdata.txt
# tr '-' $' ' < "$1" > tr.txt
CPU TIME AND RESOURCE USAGE OF './tr bigdata.txt'
VALUES ARE THE AVERAGE OF ( 10 ) TRIALS
CPU, sec : 0.02
CPU, pct : 97.10
RAM, kb : 1390.00
# sed 's/-/ /g' < "$1" > sed.txt
CPU TIME AND RESOURCE USAGE OF './sed bigdata.txt'
VALUES ARE THE AVERAGE OF ( 10 ) TRIALS
CPU, sec : 0.15
CPU, pct : 98.90
RAM, kb : 1386.80
# awk 'BEGIN{FS="-"} ; { print $1,$2 }' "$1" > awk.txt
CPU TIME AND RESOURCE USAGE OF './awk bigdata.txt'
VALUES ARE THE AVERAGE OF ( 10 ) TRIALS
CPU, sec : 0.18
CPU, pct : 98.80
RAM, kb : 1402.00
# BASH: VERSION 1
CPU TIME AND RESOURCE USAGE OF './bash_1 bigdata.txt'
VALUES ARE THE AVERAGE OF ( 10 ) TRIALS
CPU, sec : 16.35
CPU, pct : 99.00
RAM, kb : 1486.40
# BASH: VERSION 2
CPU TIME AND RESOURCE USAGE OF './bash_2 bigdata.txt'
VALUES ARE THE AVERAGE OF ( 10 ) TRIALS
CPU, sec : 12.51
CPU, pct : 99.40
RAM, kb : 114002.40
# BASH: VERSION 3
CPU TIME AND RESOURCE USAGE OF './bash_3 bigdata.txt'
VALUES ARE THE AVERAGE OF ( 10 ) TRIALS
CPU, sec : 15.45
CPU, pct : 99.00
RAM, kb : 1484.00
В базовом sed диапазоны выражений в квадратных скобках соответствуют Posix. В Posix диапазоны выражений в квадратных скобках следуют порядку сортировки. Порядок сортировки определяется на основе числового значения символа только в локали C. Но только для однобайтовых значений. Остальные локали не определены в Posix.
Чтобы заставить диапазон работать в выражении скобок sed, нам нужно использовать порядок сортировки, который сортируется по числовой кодовой точке Unicode, это C.UTF -8. Но это создает вторичное требование кодирования символов диапазона в utf8:
Получить символьное восьмеричное представление диапазона кодовых точек Unicode (, если используется локаль utf -8):
$ printf '\u452\u490' | od -An -to1
Если не используется локаль utf -8, преобразовать значения в utf -8:
$ printf '\u452\u490' | iconv -t utf-8 | od -An -to1
321 222 322 220
Добавьте тире и \o, чтобы он работал в старой/настоящей версии sed:
$ printf '\o%s\o%s-\o%s\o%s' $(printf '\u452\u490'|iconv -tutf-8|od -An -to1)
\o321\o222-\o322\o220
Использовать этот диапазон можно в sed:
$ echo "$a" | LC_ALL=C.UTF-8 sed 's/[^\o321\o222-\o322\o220]//g'
Но убедитесь, что локаль C.UTF -8 и что заданная строка закодирована в utf8, и преобразуйте ее обратно в используемую локаль:
$ echo "$a" | iconv -t utf-8 |
LC_ALL=C.UTF-8 sed 's/[^\o321\o222-\o322\o220]//g' |
iconv -f utf-8
Обратите внимание , что выше мы использовали оболочку для преобразования \u452\u490
.
GNU awk может генерировать строку символов с учетом шестнадцатеричного кода Unicode (при условии, что действующая локаль разрешает такие символы):
<<<"$a" awk 'BEGIN{for(i=0x452;i<=0x490;i++){r=r sprintf("%c", i)}}
{gsub("[^" range "]", "")}1'
Если текущая локаль не содержит эти точки кода Unicode -в кодовой точке Unicode, вам необходимо преобразовать локаль в локаль, которая, как известно, содержит такие точки кода -и использовать соответствующую переменную среды локали, что-то как:
<<<"$a" iconv -t utf8 |
LC_ALL=en_US.UTF-8 awk '
BEGIN{for(i=0x452;i<=0x490;i++){r=r sprintf("%c", i)}}
{gsub("[^" r "]", "")}1
' | iconv -f utf8
Итог либо более высокая оболочка (GNU bash или zsh )либо awk (требуется только GNU ).
Или используйте язык более высокого уровня, например perl:
$ echo "$a" | perl -Mopen=locale -ane 's/[^\x{452}-\x{490}]//g; print'
Согласно POSIX, диапазоны выражений в квадратных скобках указываются только на основе кодовой точки в локали C/POSIX. В других регионах это не указано и часто зависит от порядка сортировки, как вы узнали. Вы обнаружите, что в некоторых локалях, в зависимости от инструмента, [g-j]
, например, включает i
, но также и ı
, ǵ
, иногда даже I
или даже ch
, как в некоторых чешских локалях.
zsh
— один из тех редких, чьи диапазоны [x-y]
основаны на кодовой точке независимо от локали. Для однобайтовых наборов символов -это будет основано на значении байта, для многобайтовых -байтовых — на кодовой точке Unicode или на том, что система использует для представления широких символов внутри с mbstowc()
и ко. API (обычно Unicode ).
Так в zsh
,
[[ $char = [$'\u452'-$'\u490'] ]]
[[ $char = [^ђ-Ґ] ]]
y=${x//[^ђ-Ґ]/}
будет работать в вашем случае для сопоставления символов в этом диапазоне Unicode при условии, что кодировка локали является многобайтовой -и имеет эти два символа. Существуют однобайтовые наборы символов -, которые содержат некоторые из этих символов (, например ISO8859 -5, в котором большинство из них находятся в U+0401..U+045F ), но в локалях, которые их используют, диапазоны [ђ-Ґ]
будут основываться на кодовой точке байтового значения (в соответствующем наборе символов, а не на кодовой точке Unicode ).
В локали C диапазоны основаны на кодовой точке, но набор символов в локали C гарантированно включает только символы из переносимого набора символов , который представляет собой лишь несколько символов, необходимых для написания POSIX. или код C (, ни один из которых не написан кириллицей ). Также гарантируется одиночный -байт , поэтому он не может включать все символы, указанные в Unicode. На практике чаще всего это ASCII.
На практике вы не можете установить LC_COLLATE
на C, не установив также LC_CTYPE
на C (или, по крайней мере, локаль с одной -байтовой кодировкой ). Однако многие системы имеют локаль C.UTF-8
, которую вы можете использовать здесь.
UTF -8 — это один из тех наборов символов, которые могут представлять все символы Unicode, а значит, и все символы любой кодировки. Так что вы могли бы сделать:
< file iconv -t utf-8 |
LC_ALL=C.UTF-8 sh -c 'sed "$(printf "s/[^\321\222-\322\220]//g")"' |
iconv -f utf-8
Первое iconv
преобразование из кодировки локали пользователя в UTF -8, \321\222
и \322\220
представляет собой кодировку UTF -8 U+0452 и U+0490 соответственно, второе iconv
преобразование обратно в кодировку локали.
Если текущая локаль уже использует UTF -8, так как кодировка (и file
записываются с использованием этой кодировки ), это можно упростить до:
<file LC_ALL=C.UTF-8 sed 's/[^ђ-Ґ]//g'
или:
<file LC_ALL=C.UTF-8 sed "$(printf "s/[^\321\222-\322\220]//g")"
С помощью GNU sed
и при условии, что $POSIXLY_CORRECT
не находится в среде, вы можете указывать символы на основе значения байтов их кодировки.
<file LC_ALL=C.UTF-8 sed 's/[^\321\222-\322\220]//g'
Хотя в старых версиях вам может понадобиться:
<file LC_ALL=C.UTF-8 sed 's/[^\o321\o222-\o322\o220]//g'
Или шестнадцатеричный вариант:
<file LC_ALL=C.UTF-8 sed 's/[^\xd1\x92-\xd2\x90]//g'
Другой вариант,для локалей, использующих многобайтовый набор символов -, который включает эти символы в системах, где широкоформатное представление символов основано на Unicode, следует использовать GNU awk
и:
awk 'BEGIN{for (i = 0x452; i<=0x490; i++) range = range sprintf("%c", i)}
{gsub("[^" range "]", ""); print}'
(Первоначально я считал, что POSIX требует, чтобы реализации awk вели себя как GNU awk, но это не так, поскольку POSIX оставляет поведение sprintf("%c", i)
неопределенным для значений i
, которые не соответствуют кодировке .(не кодовая точка )символа в локали. Это означает, что его нельзя использовать переносимо для многобайтовых -символов ).
В любом случае обратите внимание, что диапазон U+0400.. U+052F — не единственные символы Unicode в кириллице , не говоря уже о языках, использующих кириллицу в качестве письма. Список символов также зависит от версии Unicode.
В системе, подобной Debian -, вы можете получить их список с помощью:
unicode --max 0 cyrillic
(, что дает 435 различных на Ubuntu 16.04, 444 на Debian sid (, вероятно, использующих другую версию Unicode ).
В perl
см. \p{Block: Cyrillic}
, \p{Block: Cyrillic_Ext_A,B,C}
, \p{Block: Cyrillic_Supplement}
... для сопоставления блоков Unicode и \p{Cyrillic}
для сопоставления символов кириллицы (, назначенных в настоящее время в версии Unicode, которая ваша версия perl
использует (см. perl -MUnicode::UCD -le 'print Unicode::UCD::UnicodeVersion'
, например )).
Так:
perl -Mopen=locale 's/\P{Cyrillic}//g'