Тот факт, что вы можете что-то делать в bash
, не означает, что вы должны . Сценарии
sh
(и bash
и т. Д.) Лучше всего подходят в качестве относительно простых оболочек для запуска программ или команд обработки текста. Для более сложных задач, включая анализ файлов ini и работу с ними, больше подходят другие языки. Думали ли вы о написании сценария на perl
или python
? У обоих есть хорошие парсеры файлов .ini - я несколько раз использовал модуль Perl Config :: INI
, когда мне нужно было разобрать файл ini.
Но если вы настаиваете на использовании bash, вам следует использовать ассоциативный массив вместо установки отдельных переменных.
Начните примерно так:
#! /bin/bash
inifile='user1074170.ini'
# declare $config to be an associative array
declare -A config
while IFS='=' read -r key val ; do
config["$key"]="$val"
done < <(sed -E -e '/^\[/d
s/#.*//
s/[[:blank:]]+$|^[[:blank:]]+//g' "$inifile" )
# now print out the config array
set | grep '^config='
Скрипт sed
удаляет строку [Section1]
(фактически, все строки, начинающиеся с открытой квадратной скобки [
- вы захотите обработать это по-другому [1] в ini-файле с несколькими разделами) и удалить комментарии, а также начальные и конечные пробелы. Цикл while
считывает каждую строку, используя =
в качестве разделителя полей, и присваивает содержимое переменным $ key и $ val, которые затем добавляются в массив $ config.
Вывод:
config=([value1]="abc\`def" [value3]="mno\$pqr" [value2]="ghi>jkl" [value4]="stu;vwx" )
Вы можете использовать записи массива позже в вашем скрипте следующим образом:
$ echo value1 is "${config[value1]}"
value1 is abc`def
$ [ "${config[value4]}" = 'stu;vwx' ] && echo true
true
[1] awk
или perl
имеют удобные простые способы чтения файлов в режиме «абзаца». Абзац определяется как блок текста, отделенный от других блоков текста одной или несколькими пустыми строками.
например.чтобы работать только с [Section1]
, вставьте ниже скрипт awk
непосредственно перед скриптом sed
, вводящим в цикл while
выше:
awk -v RS= -v ORS='\n\n' '/\[Section1\]/' "$inifile" | sed ...
(и, конечно, удалите "$ inifile"
из конца командной строки sed
- вы не хотите загружать файл снова после того, как вы перешли в проблема извлечения из него только [Section1]
).
Настройка ORS
не является строго необходимой, если вы извлекаете только один раздел из ini-файла, но будет полезна для сохранения разделения абзацев, если вы извлекаете два или более разделов.
Используйте специализированный инструмент для выполнения подобных задач (, также известный как база данных):
# Remove spaces around the field separator
sed -i.fixed 's/ *\; */\;/g' a
sed -i.fixed 's/ *\; */\;/g' b
# Add to sqlite database
echo -e '.separator ";"\n.import a.fixed a' | sqlite3 db.sqlite
echo -e '.separator ";"\n.import b.fixed b' | sqlite3 db.sqlite
# Select whatever you need
echo -e 'select a.username,a.mail,b."AccountStatus (locked=0 or unlocked=1)" from a join b on a.username = b.username;' | sqlite3 db.sqlite
awk
раствор:
users=( $(awk -F";" 'NR>1{print $1";"$3}' a) )
for u in "${users[@]}"; do
username=$(echo "$u" | cut -d';' -f1)
mail=$(echo "$u" | cut -d';' -f2)
awk -v "u=$username" -v "m=$mail" -F';' 'NR>1 { if ($3 == 0) print "User "u" ("m") is locked"; }' b
done
Во-первых, если у вас есть пробелы вокруг разделителя, вам нужно удалить их в своем скрипте, как сказал @RoVo. Команды sed сделают это за вас.
Во-вторых, вы в основном хотите, чтобы цикл while считывал каждую строку из фиксированного файла A и получал имя пользователя и адрес электронной почты, а также, возможно, полное имя пользователя. Затем вы хотите проверить статус этого пользователя в фиксированном файле B.
Что-то вроде следующего небольшого цикла должно помочь вам начать работу:
#!/bin/bash
# Remove spaces around delimiter
sed -i.fixed 's/[ ]*\;[ ]*/\;/g' fileA
sed -i.fixed 's/[ ]*\;[ ]*/\;/g' fileB
# Read in each line from the fixed fileA
while read l; do
# Skip the header line
[[ ${l} =~ ^username ]] && continue
# Get the user from the line that was read in.
u=$(echo ${l} | awk -F\; '{print $1}')
# Get the lock status for that user from the fixed fileB
l=$(awk -F\; -v u=${u} '{if ($1 == u) {print $3}}' fileB.fixed)
# Echo out the 2 fields.
echo ${u}=${l}
# Other stuff can go here.
done <fileA.fixed
exit 0
Используя awk
, сначала прочитайте имена пользователей, чья учетная запись заблокирована, из второго файла, затем извлеките их адреса электронной почты из первого файла (, затем надейтесь, что им не нужно входить в систему, чтобы прочитать их электронные письма):
awk -F ';' 'NR == FNR && $NF == 0 { names[$1] }
NR != FNR && $1 in names { print $NF }' B.csv A.csv
Это предполагает, что каждое имя пользователя имеет одинаковое количество пробелов вокруг себя в обоих файлах. Если это не так, вы можете использовать -F ' *; *'
, чтобы включить любые символы пробела в разделитель, который использует awk
. Также предполагается, что в данных нет встроенных символов ;
.
NR
— номер записи (строки )текущей записи в целом, а FNR
— тот же номер, но в текущем файле. Если NR == FNR
, то читаем из первого файла, заданного в командной строке (B.csv
). NF
— количество полей (столбцов )в текущей записи, $NF
— данные в последнем поле (и $1
— данные в первом поле ).
В приведенном выше коде используется ассоциативный массив/хэш, names
с ключом для имен заблокированных -пользователей, считанных из первого файла(B.csv
). $1 in names
будет истинным, если $1
является ключом в этом массиве.
Зацикливание:
awk -F ';' 'NR == FNR && $NF == 0 { names[$1] }
NR != FNR && $1 in names { print $NF }' B.csv A.csv |
while read addr; do
printf 'Would send an email to "%s"\n' "$addr"
#mail -s 'Account locked' "$addr" <template-email.txt
done
Или что-то в этом роде.Чтение адресов электронной почты таким образом в цикле удалит все пробелы вокруг них. Приведенный выше цикл не отправляет электронные письма, а печатает адреса, на которые необходимо отправить. Удалите #
передmail
(и напишите электронную форму в template-email.txt
), чтобы на самом деле отправить электронное письмо (, но вы можете сделать это по-другому ).
Использованиеcsvkit
:
csvjoin -d ';' -c 1 A.csv B.csv |
csvgrep -c 5 -m False |
csvcut -S -c 3 | sed 1d
CSVkit предоставляет инструменты анализа CSV для работы с CSV-файлами. Это необходимо, если ваши данные CSV не являются «простыми», т. е. если они используют правила CSV для цитирования встроенных символов ;
и т. д. Приведенный выше конвейер будет
0
будет изменено на False
на этом этапе конвейера ). sed
). #!/bin/bash
cat fileA.txt | sed 1d | while IFS=';' read -r line; do #read fileA.txt starting with line #2
name=$(echo $line | awk '{print $1}') #find names in each line/column 1 of the table
lock_status=$(grep $name fileB.txt | awk '{print $5}') # find lock/unlock status in fileB.txt
if [[ "$lock_status" -eq 0 ]];then
echo "Locked: To mail the user : replace echo by the command mail";
else
echo "unlocked";
fi
done