Если вы не хотите использовать SQL, *Кроме того, вам будет сложно выполнять любые операции со схемой базы данных из сценария оболочки.
Вы можете написать приложение Pro *C (или что-то подобное ), использовать модули Perl DBI
и DBD::Oracle
или эквивалент на таком языке, как Python или PHP.
Поскольку \t
является символом пробела(символом, для которого isspace()
возвращает значение true или grep '[[:space:]]'
соответствует ), это стандартное поведение разделения слов , как указано по POSIX :последовательности IFS -пробельные символы (пробельные символы, присутствующие в $IFS
), обрабатываются как один разделитель и начальные, конечные единицы или единицы по обе стороны от символы IFS без пробела -игнорируются.
Первоначально в ksh, откуда происходит эта «функция»¹, эта специальная обработка ограничивалась символами TAB, NL и пробелами, но POSIX изменила ее на любой символ, который считается пробелом в локали. Однако на практике большинство оболочек по-прежнему делают это только для TAB/NL/SPC (, которые также имеют значение по умолчанию$IFS
). Единственное исключение (единственная оболочка, совместимая с POSIX -в этом отношении ), которую я знаю, это yash
.
В bash поведение изменилось в версии 5.0, и теперь bash ведет себя как ksh93 в том, что специальная обработка применяется к пробельным символам, но только к тем, которые закодированы в одном байте.
Сравните обработку символа en space и символа возврата каретки :
$ s=$'\u2000' a="a${s}${s}b" bash -c 'IFS=$s; [[ $s = [[:space:]] ]] && echo yes; printf "<%s>\n" $a'
yes
<a>
<>
<b>
$ s=$'\r' a="a${s}${s}b" bash -c 'IFS=$s; [[ $s = [[:space:]] ]] && echo yes; printf "<%s>\n" $a'
yes
<a>
<b>
Таким образом, в bash
5.0+, чтобы bash
больше не придавалось особого отношения к TAB, вам необходимо создать локаль, в которой TAB не считается пробелом. Даже в этом случае этого было бы недостаточно, так как даже для символов, отличных от -, пробелов, например ,
вместо TAB, a,b,c,
разбивается на a
, b
и c
вместо a
, b
, c
и пустая строка .
Здесь лучше использовать оболочку, в которой можно отключить специальную обработку пробельных символов в отношении разделения IFS, или иметь правильный оператор разделения, или, что еще лучше, как говорит @cas, использовать правильный язык программирования, такой как perl
вместо оболочки, которая для этого не предназначена.
В ksh93 или zsh специальную обработку можно удалить, удвоив соответствующий символ в $IFS
. zsh
Разбиение слов также не отбрасывает завершающий пустой элемент. Так в zsh
,
IFS=$'\t\t' read -rA array
действительно сработает. Там вы также можете сделать:
IFS= read -r line && array=( "${(@ps[\t])line}" )
Чтобы прочитать строку, а затем разделить ее с помощью флага раскрытия параметра s
.
В ksh93 вы могли выполнить разбиение с помощью:
set -o noglob
IFS=$'\t\t'
IFS= read -r line && array=( $line'' )
(все еще есть разница с подходами zsh
выше в том, что пустая строка приводит к массиву с одним пустым элементом вместо массива без элемента ).
ksh93 поддерживает многомерные массивы, которые могут вам помочь:
i=0
IFS=$'\t\t'; set -o noglob
typeset -a array=()
while IFS= read -r line; do
array[i++]=( $line'' )
done < file.tsv
А затем ${array[2][5]}
даст вам, например, 6-е -е поле 3-й -й строки.
¹ Разделение IFS -изначально происходит из оболочки Bourne, но в оболочке Bourne все символы получили эту обработку, а не только TAB/NL/SPC
Использование Raku (, ранее известного как Perl _6)
Весь приведенный ниже код выполняется в командной строке bash:
raku -e 'my @a = lines>>.split("\t");.raku.put for @a;' test_tsv.tsv
ВОЗВРАТ:
$(("1", "A", "J", "", "", "", "1").Seq)
$(("2", "B", "K", "N", "", "", "1").Seq)
$(("3", "C", "L", "O", "P", "Q", "1").Seq)
Выше строки разделены на вкладки \t
. Метод .raku
используется для визуализации пустых строк. Гипероператор >>
— это сокращение от отображения, как в .map(*.split("\t"))
. Приведенный выше код возвращает объект с 3 .Seq
элементами, однако его легко привести к списку:
raku -e 'my @a = lines>>.split("\t");.list.raku.put for @a;' test_tsv.tsv
("1", "A", "J", "", "", "", "1")
("2", "B", "K", "N", "", "", "1")
("3", "C", "L", "O", "P", "Q", "1")
Метод elems
используется в Raku для получения количества элементов, либо элементов всего объекта массива, либо элементов каждой «линии»:
raku -e 'my @a = lines>>.split("\t"); @a.elems.put;' test_tsv.tsv
3
raku -e 'my @a = lines>>.split("\t"); @a>>.elems.put;' test_tsv.tsv
7 7 7
Как только вы убедитесь, что получили желаемую структуру, можно легко упростить код или добавить/удалить идиомы Perlish/Rakuish, такие как for
(, чтобы получить желаемый результат для визуализации или дальнейших манипуляций):
raku -e 'my @a = lines>>.split("\t");.put for @a;' test_tsv.tsv
1 A J 1
2 B K N 1
3 C L O P Q 1
raku -e 'my @a = lines>>.split("\t"); @a>>.list.raku.put;' test_tsv.tsv
(("1", "A", "J", "", "", "", "1"), ("2", "B", "K", "N", "", "", "1"), ("3", "C", "L", "O", "P", "Q", "1"))