Одной из задач в повседневной работе является построчное чтение данных из файла, разбор данных и их обработка. Входной файл может быть либо обычным текстовым файлом, где каждая строка содержит несколько полей, разделенных пробелами, либо CSV-файлом, отформатированным с разделенными разделителями значениями в каждой строке. Bash позволяет считывать столбцы из файла и сохранять их в отдельные переменные для дальнейшей обработки.
Чтение столбцов, разделенных пробелами, в Bash
Пример текстового файла (os.txt)
1 2 3 | 1 linux 5 'A Linux-based system is a modular Unix-like operating system' 2 windows 10 'Microsoft Windows' 3 macos 21 'macOS is a proprietary graphical operating system' |
Для чтения данных столбцов из файла в bash можно использовать встроенную команду read, которая читает одну строку из стандартного ввода или дескриптора файла. В сочетании с циклом for/while команда read может читать содержимое файла построчно, пока не будет достигнут конец файла. Если каждая строка содержит несколько полей, read может считывать отдельные поля и сохранять их в заданных переменных. По умолчанию read распознает пробелы как разделитель для разных полей.
1 2 3 | while read index name version description; do echo "$index : $name, $version , $description" done < "os.txt" |
1 2 3 | cat os.txt | while read index name version description; do echo "$index : $name, $version , $description" done |
Чтение столбцов из файла CSV в Bash
При работе с CSV-файлом, в котором в качестве разделителя столбцов используется символ, не являющийся пробелом (например, "," или ";" или "|"), необходимо указать разделитель в переменной IFS. Переменная IFS (сокращение от "Input Field Separator") - это специальная переменная bash, которая указывает символ или символы, разделяющие поля.
1 2 3 | linux,5,"A Linux-based system is a modular Unix-like operating system" windows,10,"Microsoft Windows" macos,,"macOS is a proprietary graphical operating system" |
Цикл while может прочитать CSV-файл и сохранить столбцы в указанные переменные. IFs= перед командой read указывает read использовать "," в качестве разделителя слов. Для проверки, пуст ли конкретный столбец или нет, используется оператор -z.
1 2 3 4 5 6 7 | while IFS=, read name version description; do if [ -z "$version" ]; then "$name no version" else echo "$name $version" fi done < "os.csv" |
Чтение данных столбцов с помощью регулярного выражения
Более общий и сложный сценарий - это чтение данные столбцов из менее структурированных файлов, таких как журналы. Типичный файл журнала не так структурирован, как CSV-файлы, и может не использовать фиксированный символ-разделитель, а также фиксированное количество столбцов. В качестве примера рассмотрим следующий фрагмент файла auth.log.
1 2 3 4 5 6 7 8 9 10 | Feb 21 08:42:51 debian sudo: dan : TTY=pts/1 ; PWD=/home/dan/download/shc ; USER=root ; COMMAND=/usr/bin/apt-get install nload Feb 21 08:42:51 debian sudo: pam_unix(sudo:session): session opened for user root by (uid=0) Feb 21 08:42:52 debian sudo: pam_unix(sudo:session): session closed for user root Feb 21 09:40:20 debian sudo: alice : TTY=pts/1 ; PWD=/home/alice ; USER=root ; COMMAND=/usr/bin/apt remove apache2 Feb 21 09:40:20 debian sudo: pam_unix(sudo:session): session opened for user root by (uid=0) Feb 21 09:40:25 debian sudo: pam_unix(sudo:session): session closed for user root Feb 22 10:50:43 debian sudo: dan : TTY=pts/0 ; PWD=/home/dan/abc ; USER=root ; COMMAND=/usr/bin/nano /etc/hosts Feb 22 10:50:43 debian sudo: pam_unix(sudo:session): session opened for user root by (uid=0) Feb 22 10:50:49 debian sudo: pam_unix(sudo:session): session closed for user root Feb 22 10:51:56 debian sudo: dan : TTY=pts/0 ; PWD=/home/dan/abc ; USER=root ; COMMAND=/usr/bin/nano /etc/resolv.conf |
Для примера можно извлечь логин пользователя и команду sudo, запущенную пользователем.
1 2 3 4 5 6 | while read -r line; do pattern='debian sudo:\s+([^[:space:]]+).*COMMAND=(.*)' if [[ $line =~ $pattern ]]; then echo "${BASH_REMATCH[1]} : ${BASH_REMATCH[2]}" fi done < "auth.log" |
При разборе, мы проверяем соответствует ли строка регулярному выражению и извлекаем значения конкретных столбцов.