Создание массива из текстового файла в Bash
A script принимает URL-адрес, анализирует его для обязательных полей и перенаправляет его вывод для сохранения в файле file.txt. Выход сохраняется на новой строке каждый раз, когда поле было найдено.
file.txt
A Cat
A Dog
A Mouse
etc...
Я хочу взять file.txt
и создать массив из него в новом script, где каждая строка становится своей собственной строковой переменной в массиве. До сих пор я пробовал:
#!/bin/bash
filename=file.txt
declare -a myArray
myArray=(`cat "$filename"`)
for (( i = 0 ; i < 9 ; i++))
do
echo "Element [$i]: ${myArray[$i]}"
done
Когда я запускаю этот script, пробелы приводят к разрыву слов и вместо того, чтобы получать
Требуемый выход
Element [0]: A Cat
Element [1]: A Dog
etc...
Я получаю следующее:
Фактический выход
Element [0]: A
Element [1]: Cat
Element [2]: A
Element [3]: Dog
etc...
Как я могу настроить петлю ниже, так что вся строка в каждой строке будет соответствовать друг другу с каждой переменной в массиве?
Ответы
Ответ 1
Используйте команду mapfile
:
mapfile -t myArray < file.txt
Ошибка используется for
- идиоматический способ зацикливания строк файла:
while IFS= read -r line; do echo ">>$line<<"; done < file.txt
Смотрите BashFAQ/005 для более подробной информации.
Ответ 2
mapfile
и readarray
(которые являются синонимами) доступны в Bash версии 4 и выше. Если у вас более старая версия Bash, вы можете использовать цикл для чтения файла в массив:
arr=()
while IFS= read -r line; do
arr+=("$line")
done < file
Если у файла есть неполная (отсутствует новая строка) последняя строка, вы можете использовать эту альтернативу:
arr=()
while IFS= read -r line || [[ "$line" ]] do
arr+=("$line")
done < file
Связанные с:
Ответ 3
Вы также можете сделать это:
oldIFS="$IFS"
IFS=$'\n' arr=($(<file))
IFS="$oldIFS"
echo "${arr[1]}" # It will print `A Dog`.
Примечание:
Расширение имени файла все еще происходит. Например, если есть строка с литеральным *
, она будет распространяться на все файлы в текущей папке. Поэтому используйте его, только если ваш файл свободен от такого сценария.
Ответ 4
Вы можете просто прочитать каждую строку из файла и назначить его массиву.
#!/bin/bash
i=0
while read line
do
arr[$i]="$line"
i=$((i+1))
done < file.txt
Ответ 5
Использовать файл карты или прочитать -a
Всегда проверяйте свой код, используя shellcheck. Он часто даст вам правильный ответ. В этом случае SC2207 охватывает чтение файла, который либо разделяет пробел, либо разделяет значения новой строки в массиве.
Не делайте этого
array=( $(mycommand) )
Файлы со значениями, разделенные символами новой строки
mapfile -t array < <(mycommand)
Файлы со значениями, разделенными пробелами
IFS=" " read -r -a array <<< "$(mycommand)"
Страница shellcheck даст вам обоснование, почему это считается лучшей практикой.
Ответ 6
Этот ответ говорит, чтобы использовать
mapfile -t myArray < file.txt
Я сделал shim для mapfile
, если вы хотите использовать mapfile
в bash < 4.x по любой причине. Он использует существующую команду mapfile
, если вы находитесь на bash >= 4.x
В настоящее время работают только параметры -d
и -t
. Но этого должно быть достаточно для этой команды выше. Я тестировал только на macOS. В macOS Sierra 10.12.6 система bash равна 3.2.57(1)-release
. Таким образом, прокладка может пригодиться. Вы также можете просто обновить свой bash с помощью homebrew, построить bash самостоятельно и т.д.
Он использует эту технику для установки переменных на один стек вызовов.