Заполнить два массива «на лету» из файла stdin в Bash

#arrays #bash #shell

#массивы #bash #оболочка

Вопрос:

Я написал скрипт bash, который считывает файл из stdin $ 1, и ему нужно прочитать этот файл построчно в цикле, и на основе инструкции условия на каждой итерации каждая проверенная строка из файла будет передаваться в один из двух новых массивов, скажем, с именами GOOD array и BAD array. Наконец, я выведу общее количество элементов каждого массива.

 #!/bin/bash

      for x in $(cat $1); do
        #testing something on x
        if [ $? -eq 0 ]; then
          #add the current value of x into array called GOOD
        else
          #add the current value of x into array called BAD
        fi
      done

      echo "Total GOOD elements: ${#GOOD[@]}"
      echo "Total BAD elements: ${#BAD[@]}"
  

Какие изменения я должен внести, чтобы выполнить это?

Комментарии:

1. for x in $(cat ...anything...) является антипаттером. Смотрите в BashFAQ # 1 рекомендации по итерации содержимого файла.

2. Ваш вопрос звучит так: «Пожалуйста, сделайте за меня мою домашнюю работу». Чтобы улучшить свой вопрос, укажите, каков результат, что вы пробовали и что конкретно вы пытаетесь исправить.

3. Хотя я ответил на этот вопрос, я согласен, что в настоящее время это не тот вопрос, к которому мы стремимся здесь. Идеальный вопрос был бы более узким — скажем, «как мне добавить к массиву в bash?» (хотя это дубликат) или «почему чтение из файла с for word in $(cat $1) глобусами расширения, когда я этого не хочу?» (хотя это тоже дубликат); с самым коротким кодом, необходимым для иллюстрации этого единственного, конкретного, узкого вопроса.

4. Спасибо @CharlesDuffy за вашу помощь, мы признательны. Массивы в bash для меня немного неоднозначны, я уже просмотрел 3 курса по написанию сценариев на bash, прежде чем задать этот вопрос. Простите, что задаю вопрос, но это мой первый раз, и впредь я буду его улучшать.

5. @MoatazOsama : Ваш скрипт обрабатывает ввод не построчно, а слово за словом. Кроме того, я не понимаю, что [ $? ... ] предполагается делать. $? это код состояния самой последней выполненной команды.

Ответ №1:

 #!/usr/bin/env bash

# here, we're checking the number of lines more than 5 characters long
# replace with your real test
testMyLine() { (( ${#1} > 5 )); }

good=( ); bad=( )
while IFS= read -r line; do
  if testMyLine "$line"; then
    good =( "$line" )
  else
    bad =( "$line" )
  fi
done <"$1"

echo "Read ${#good[@]} good and ${#bad[@]} bad lines"
  

Примечание:

  • Мы используем while read цикл для перебора содержимого файла. При этом не требуется считывать в память более одной строки за раз (поэтому оперативная память не будет исчерпана даже при работе с действительно большими файлами), и у него нет нежелательных побочных эффектов, таких как изменение строки, содержащей * список файлов в текущем каталоге.
  • Мы не используем $? . if foo; then это гораздо лучший способ ветвления по статусу выхода foo , чем foo; if [ $? = 0 ]; then — в частности, это позволяет избежать зависимости от значения $? not being changed между тем, когда вы его назначаете, и тем, когда оно вам нужно; и оно помечается foo как «проверено», чтобы избежать выхода через set -e или срабатывания ловушки ошибок, когда ваше логическое значение возвращает false .
  • Имена переменных в нижнем регистре используются намеренно. Имена в верхнем регистре используются для встроенных в оболочку переменных и имен, имеющих особое значение для операционной системы — и поскольку определение обычной переменной оболочки перезаписывает любую переменную среды с тем же именем, это соглашение применимо к обоим типам. Смотрите http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html