Привязать к переменной массива bash косвенно, динамически сконструированным именем переменной

Bash для создания нескольких массивов из csv с неизвестными столбцами.

Я пытаюсь написать сценарий для сравнения двух файлов csv с похожими столбцами. Мне нужно, чтобы найти соответствующий столбец из другого csv и сравнить любые различия. Кикер – это то, что скрипт должен быть динамическим, чтобы можно было вводить любое количество столбцов, и он все еще может функционировать. Я думал, что у меня есть хороший план для решения этого вопроса, но оказывается, что я столкнулся с синтаксическими ошибками. Вот пример csv, который мне нужно сравнить.

IP address, Notes, Nmap-SSH, Nmap-SMTP, Nmap-HTTP, Nmap-HTTPS, 10.0.0.1, , open, closed, open, open, 10.0.0.2, , closed, open, closed, closed, 

Когда я прочитал файл csv, я планировал искать «IF column == open, а затем заполнить массив этого столбца IP-адресом». Это дало бы мне 4 списка в этом сценарии с IP-адресами, которые прослушивали этот порт. Затем я мог бы сравнить это с конфигурацией устройства безопасности, чтобы убедиться, что он настроен правильно. Наконец, к мясу, вот что я думал о том, чтобы создать массивы для меня, чтобы искать позже. Однако я столкнулся с проблемой, когда пытался использовать переменную внутри имени массива. Может ли мой синтаксис быть исправлен или есть лучший способ сделать это?

 #!/bin/bash # # # This script compares config_cleaned_.txt output against ext_web_env.csv and outputs the differences # # # Read from ext_web_env.csv file and create Array # FILENAME=./tmp/ext_web_env.csv # index=0 # while read line do # How many columns are in the .csv? varEnvCol=$(echo $line | awk -F, '{print NF}') echo "columns = $varEnvCol" # While loop to create array for each column while [ $varEnvCol != 2 ] do # Checks to see if port is open; if so then add IP address to array varPortCon=$(echo $line | awk -F, -vi=$varEnvCol '{print $i}') if [ $varPortCon = "open" ] then arr$varEnvCol[$index]="$(echo $line | awk -F, '{print $1}')" # I get this error message "line29 : arr8[194]=10.0.0.194: command not found" fi echo "arrEnv$varEnvCol is: ${arr$varEnvCol[@]}" # Another error but not as important since I am using this to debug "line31: arr$varEnvCol is: ${arr$varEnvCol[@]}: bad substitution" varEnvCol=$(($varEnvCol - 1)) done index=$(($index + 1 )) done < $FILENAME 

ОБНОВИТЬ

Я также попытался использовать команду eval, поскольку все данные будут заполнены другими скриптами.

но я получаю это сообщение об ошибке:

./compare.sh: строка 41: arr8 [83] = 10.0.0.83: команда не найдена

Вот мой новый код для этого примера:

  if [[ $varPortCon = *'open'* ]] then eval arr\$varEnvCol[$index]=$(echo $line | awk -F, '{print $1}') fi 

 arr$varEnvCol[$index]="$(...)" 

не работает так, как вы ожидаете, – вы не можете косвенно назначать переменные оболочки – через выражение, которое расширяется до имени переменной – таким образом .

Ваша попытка обхода проблемы с eval также испорчена – см. Ниже.


ТЛ; др

Если вы используете bash 4.3 или выше:

 declare -n targetArray="arr$varEnvCol" targetArray[index]=$(echo $line | awk -F, '{print $1}') 

bash 4.2 или ранее:

 declare "arr$varEnvCol"[index]="$(echo $line | awk -F, '{print $1}')" 

Предостережение : это будет работать в вашей конкретной ситуации, но может быть неудачным в других ; прочитайте для деталей, в том числе более надежную, но громоздкую альтернативу, основанную на read .

Решение, основанное на eval упомянутое @shellter в комментариях с удалением, проблематично не только по соображениям безопасности (как они упомянули), но также и потому, что может быть довольно сложным в отношении цитирования ; для полноты, вот решение на основе eval :

 eval "arr$varEnvCol[index]"='$(echo $line | awk -F, '\''{print $1}'\'')' 

См. Ниже объяснение.


Привязать к переменной массива bash косвенно :

bash 4.3+ : используйте declare -n для эффективного создания псевдонима (‘nameref’) другой переменной

Это, безусловно, лучший вариант, если он доступен:

 declare -n targetArray="arr$varEnvCol" targetArray[index]=$(echo $line | awk -F, '{print $1}') 

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

bash 4.2- : существует несколько вариантов , каждый из которых имеет компромиссы

ПРИМЕЧАНИЕ. При использовании переменных , отличных от массива , наилучшим подходом является использование printf -v . Поскольку этот вопрос касается переменных массива , этот подход больше не обсуждается.

  • [наиболее надежный, но громоздкий]: используйте :
 IFS=$'\n' read -r -d '' "arr$varEnvCol"[index] <<<"$(echo $line | awk -F, '{print $1}')" 
  • IFS=$'\n' гарантирует, что ведущее и завершающее пробелы в каждой строке ввода остаются нетронутыми.
  • -r предотвращает интерпретацию \ chars. на входе.
  • -d '' гарантирует, что ВСЕ вход будет захвачен, даже многострочный .
    • Обратите внимание, однако, что любые trailing \n символы. лишены .
    • Если вас интересует только первая строка ввода, опустите -d ''
  • "arr$varEnvCol"[index] расширяется до элемента variable - array, в этом случае - для назначения ; обратите внимание, что обращение к index переменной внутри index массива НЕ требует префикса $ , потому что индексы вычисляются в арифметическом контексте, где префикс является необязательным .
  • <<< - так называемая this-string - отправляет свой аргумент в stdin , откуда read его ввод.

  • [простейший, но может сломаться]: use declare :

 declare "arr$varEnvCol"[index]="$(echo $line | awk -F, '{print $1}')" 
  • (Это немного противоречит интуиции, поскольку declare объявляется , а не изменяет переменную, но работает в bash 3.x и 4.x с ограничениями, указанными ниже).
  • Работает отлично ВНЕ ФУНКЦИИ - был ли массив явно объявлен с declare или нет.
  • Предостережение : INSIDE функция работает только с LOCAL переменными - вы не можете ссылаться на глобальные переменные оболочки (переменные, объявленные вне функции) изнутри этой функции . Попытка сделать это неизменно создает LOCAL-переменную ECLIPSING глобально-глобальную переменную.

  • [небезопасный и хитрый]: используйте eval :

 eval "arr$varEnvCol[index]"='$(echo $line | awk -F, '\''{print $1}'\'')' 
  • CAVEAT: используйте только eval если вы полностью контролируете содержимое проверяемой строки; eval выполнит любую команду, содержащуюся в строке, с потенциально нежелательными результатами.
  • Понимание того, какие ссылки переменных / замены команд расширяются, когда нетривиально, - самый безопасный подход - отложить расширение, чтобы они произошли, когда eval выполняется, а не мгновенное расширение, которое происходит, когда аргументы передаются в eval .
  • Для успешного выполнения оператора присваивания переменной RHS (правая сторона) должна в конечном итоге оценить один токен - либо без кавычек без пробелов, либо кавычек (необязательно с пробелом).
  • В приведенном выше примере используются одинарные кавычки для задержки расширения; таким образом, передаваемая строка не должна содержать одинарных кавычек напрямую и, следовательно, разбита на несколько частей с буквальными символами. сращивается как.
  • Также обратите внимание, что LHS (левая сторона) оператора присваивания, переданная в eval должна быть строкой с двойными кавычками - использование строки без кавычек с выборочным цитированием $ не будет работать, с любопытством:
    • OK: eval "arr$varEnvCol[index]"=...
    • FAILS: eval arr\$varEnvCol[index]=...
  • Переменные в app.config / web.config
  • Как сохранить стандартную ошибку в переменной в сценарии Bash
  • Создание нескольких нумерованных переменных на основе int
  • Объявление переменных внутри оператора switch
  • Могу ли я использовать переменные для селекторов?
  • Невозможно изменить ошибку возвращаемого значения c #
  • Какая польза от частной статической переменной в Java?
  • Как получить тип переменной?
  • Visual Studio 2015 Отладка: не удается расширить локальные переменные?
  • Как Java хранит символы UTF-16 в 16-разрядном типе символов?
  • Неявная типизация; почему только локальные переменные?
  • Давайте будем гением компьютера.