Разделить текстовый файл на массив на основе пустой строки или любого неиспользуемого символа

У меня есть текстовый файл, который содержит текстовые строки, разделенные пустой строкой текста. Я хочу поместить содержимое этого файла в массив и использовать пустую строку в качестве разделителя. Я попробовал IFS="\n" (или "\r\n" и т. д.), но не смог заставить его работать, поэтому вместо этого я подумал, что заменю любую пустую строку символом, которого нет в файле, поэтому Я поднял испанский перевернутый вопросительный знак (\xBF)

sed 's/^$/'$(echo -e "\xBF")'/'))

Итак, это работает, у меня есть символ, который я буду использовать, чтобы нарезать файл и поместить его в массив (немного случайного трюка, но эй, это всего лишь один из способов сделать это ..)

Теперь мне нужно изменить $IFS, чтобы он использовал перевернутый вопросительный знак для нарезки данных для массива.

Если я наберу

IFS=$(echo -e "\xBF")

в командной строке работает нормально

 echo "$IFS"
¿

Но если я наберу эту команду с конечным read -a, она ничего не сделает:

[user@machine ~]$ IFS=$(echo -e "\xBF") read -a array <<< "$var"
[user@machine ~]$ echo "$IFS"
[user@machine ~]$

Так что это странно, потому что $var имеет значение.

Еще более удивительно, когда я проверяю значение IFS сразу после получения:

[user@machine ~]$ echo -n "$IFS" | od -abc
0000000  sp  ht  nl
    040 011 012
         \t  \n
0000003
[user@machine ~]$ 

Что является значением по умолчанию для IFS.

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

В качестве альтернативы, если у вас есть какой-нибудь трюк в рукаве, чтобы разделить файл в массиве с разделением на основе пустых строк, мне интересно! (все же я хотел бы добраться до сути этого ради понимания).

Большое спасибо и хороших выходных :)


person Bluz    schedule 30.08.2013    source источник


Ответы (2)


Прежде всего, по замыслу переменные, установленные с помощью var=foo command, доступны только для command и не будут устанавливаться для остальной части скрипта.

Что касается вашей проблемы, read читает запись до первого разделителя (-d, по умолчанию: перевод строки), а затем разбивает ее на поля с помощью $IFS.

Чтобы перебрать ваши элементы, вы можете использовать

sed -e 's/^$/\xBF/' | while read -d $'\xBF' var
do
    printf "Value: %s\n-----\n" "$var"
done

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

IFS=$'\xBF' read -d '' -a array <<< "$var"
person that other guy    schedule 30.08.2013
comment
Спасибо за ваш ответ! Я не уверен, какой должна быть ваша команда. Не могли бы вы объяснить? Спасибо !:) - person Bluz; 02.09.2013
comment
Это будет ваша команда для создания \xBF отдельных элементов, которые вы указали только в вопросе. Я обновил его с этим. - person that other guy; 03.09.2013

Этот скрипт должен делать то, что вы хотите:

#!/bin/bash

i=1
s=1
declare -a arr
while read -r line 
do
    # If we find an empty line, then we increase the counter (i), 
    # set the flag (s) to one, and skip to the next line
    [[ $line == "" ]] && ((i++)) && s=1 && continue 

    # If the flag (s) is zero, then we are not in a new line of the block
    # so we set the value of the array to be the previous value concatenated
    # with the current line
    [[ $s == 0 ]] && arr[$i]="${arr[$i]}
$line" || { 
            # Otherwise we are in the first line of the block, so we set the value
            # of the array to the current line, and then we reset the flag (s) to zero 
            arr[$i]="$line"
            s=0; 
    }
done < file

for i in "${arr[@]}"
do
   echo "================"
   echo "$i"
done 

Тестовый файл:

$ cat file
asdf dsf s dfsdaf s
sadfds fdsa fads f dsaf as

fdsafds f dsf ds afd f saf dsf
sdfsfs dfadsfsaf

sdfsafds fdsafads fd saf adsfas
sdfdsfds fdsfd saf dsa fds fads f

Выход:

================
asdf dsf s dfsdaf s
sadfds fdsa fads f dsaf as
================
fdsafds f dsf ds afd f saf dsf
sdfsfs dfadsfsaf
================
sdfsafds fdsafads fd saf adsfas
sdfdsfds fdsfd saf dsa fds fads f

Обновлять:

Чтобы игнорировать строки, начинающиеся с #, вы можете добавить эту строку после do:

[[ $line =~ ^# ]] && continue
person user000001    schedule 30.08.2013
comment
Спасибо, все работает отлично, но я не понимаю, что происходит в цикле while. Не могли бы вы объяснить, пожалуйста? :) Спасибо! - person Bluz; 02.09.2013
comment
@Bluz добавил объяснение. Дайте мне знать, если вам нужны дополнительные разъяснения. - person user000001; 02.09.2013
comment
Большое спасибо большое! Я понимаю, что такое флаг, хотя я пытаюсь выполнить grep файл, чтобы удалить комментарии в моем исходном файле (строки, начинающиеся с #), но я борюсь... Не знаю, куда поместить мой grep -vE ^[#]* . Не могли бы вы помочь? Обещаю, после этого я перестану задавать вопросы! :) В очередной раз благодарим за помощь ! :) - person Bluz; 02.09.2013
comment
@Bluz, почему бы не сделать grep -vE '^[#]' oldfile > newfile, а затем запустить скрипт в новом файле? - person user000001; 02.09.2013
comment
ммм, я полагаю, мог бы это сделать, но когда сценарий будет запущен в производство, это будет не один файл, а тысячи файлов в одном каталоге, поэтому я немного не хочу создавать большой временный файл, такой как cat directory/* | grep -vE '^[#]' › новый файл. В идеальном мире было бы лучше, если бы я мог делать все это исключительно в оперативной памяти. - person Bluz; 02.09.2013
comment
@Bluz Тогда вы можете просто добавить [[ $line =~ ^# ]] && continue сразу после строки do (обновлено) - person user000001; 02.09.2013
comment
Идеальный! Спасибо большое ! - person Bluz; 02.09.2013