Официальная возможность получить лицензионный софт бесплатно.
Giveaway of the Day
Это не реклама!

Щелкните для получения прогноза по Биробиджану


Показаны сообщения с ярлыком bash. Показать все сообщения
Показаны сообщения с ярлыком bash. Показать все сообщения

пятница, 15 марта 2024 г.

Получить время непрерывной работы жесткого диска из smartctl

 Некоторые утилиты выдают время непрерывной работы диска в годах, днях и часах. Но штатный smartctl - только в часах, к тому же среди остальной информации.

Попробуем сочинить команду для получения подобного результата.

sudo smartctl --all /dev/sdX|grep -A10 "ID#"|grep "^ \{0,2\}9"|awk '{print $10}'

  1. smartctl выдает длинную портянку атрибутов и прочих полезностей. Нам нужен атрибут №9: "power on hours" или "power on time" или как-то еще. Название не стандартизировано, а вот номер всегда одинаков.
  2. для этого мы находим заголовок таблицы атрибутов и берем первые 10 строк этой таблицы. 10 - чтобы с запасом. А таблицу ищем, чтобы не взять в работу что-то не то, которое будет выглядеть похоже на искомое.
  3. в этих 10 строках ищем ту, которая начинается с девятки, перед которой могут быть, а могут и не быть несколько пробелов.
  4. awk понимает, что если между осмысленными данными несколько разделителей-пробелов подряд, то их надо считать одним разделителем. В десятом поле строки, полученной в п.п. 1-3 содержится количество отработанных диском часов.

Например:

$ sudo smartctl --all /dev/sdb|grep -A10 "ID#"|grep "^ \{0,2\}9"|awk '{print $10}'

25457

Без awk строка выглядит так:

  9 Power_On_Hours          0x0032   071   071   000    Old_age   Always       -       25457

То есть, диск отпахал уже более 25 тысяч часов. Дальше можно поизвращаться с дополнительными конвейерами, дабы получить более удобное представление, но я этим заниматься не хочу, а вместо этого займусь целочисленной арифметикой. Для этого присвою вывод предыдущей команды переменной workedHours и поработаю с ней.

$ echo $(( workedHours * 100 / 24 / 36525 )) $(( (workedHours * 100 / 24 % 36525 ) /100 ))

Если взять workedHours=25457, то на выходе получим

2 328

то есть, диск отработал 2 года и 328 дней.

Для чего лишние умножения и деления на 100? Bash умеет только целочисленную арифметику, а реальная продолжительность года 365.25 суток, как я помню. Именно из-за этой четвертушки каждый четвертый год - високосный, с одним добавочным днем. И чтобы имитировать такую арифметику, мы умножаем делимое и один из делителей на 100. Количество часов в сутках суть константа, ее умножать не надо.

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

Собственно, всё. Наслаждайтесь.

вторник, 4 октября 2022 г.

Рисование текстовых рамок в bash, часть 2

 В августе 2021 я уже писал на эту тему. Но потом мне захотелось прекрасного и недавно я доработал алгоритм, сведя его в отдельную процедуру и дополнив некоторыми возможностями.

Красиво оформлено через pastebin


Вот как выглядит результат работы этого скрипта

Функция border() принимает 2 аргумента. Первый, обязательный, текст для вывода внутри рамки. Второй, не обязательный, выбор типа рамки. Сейчас воспринимаются только два модификатора: g - graphical, и d - double, соответственно обычная и двойная псевдографические рамки.

По сути, модификатор просто определяет набор знаков, используемых для рисования. Всего имеется 8 компонентов рамки: 4 угла и 4 "стенки" - 2 вертикальных (левая/правая) и 2 горизонтальных (верхняя/нижняя). Меняя символы в этих наборах, можно без проблем рисовать рамки из любых символов.

Забавно, что символ "одиночная псевдографическая горизонталь" (переменные BTM и BBM в наборе "g", \Ux2500, если не ошибаюсь) я никак не мог ввести в консоли. Не помогала даже вставка из "таблицы символов". 


Под катом - не раскрашенный текст скрипта на случай, если pastebin перестанет работать.

понедельник, 16 марта 2020 г.

Bash: разница в днях между двумя датами

В баше есть встроенная арифметика, которая во многих случаях может заменить простой целочисленный калькулятор. Понадобилось высчитать разницу между двумя датами, решается вот так (хотя способ не единственный):

echo $(( ( $(date +%s -d 4/13/2020 ) - $(date +%s -d 3/23/2020 ) )/86400 ))

Функция вычислений $(( выражение ))
Вывод команды $( команда )
Арифметическое операторы и скобки (-/)
Перевести дату, заданную параметром -d в формат UNIX-date (число секунд с начала эры) +%s
Начальная дата в формате mm/dd/yyyy 4/13/2020
Конечная дата в формате mm/dd/yyyy 3/23/2020
Разницу между датами делим на число секунд в сутках 86400

Есть и другие способы такой арифметики, но именно этот первым пришел в голову. Данная команда выдает результат "21", то есть, между 13 апреля 2020 года и 23 марта 2020 года 21 день

По арифметике с временами у меня есть более ранний пост.

вторник, 24 декабря 2019 г.

bash: сравнение двоичных файлов с комфортом

Выкачал я с корпоративного FTP установочный образ винды. Не, всё легально - свежая официальная версия для установки внутри конторы. Копирую его на верный Zalman VE-300, гружусь с него на целевом компе и начинаю установку. Получаю славную ошибку 80070570 - "файл поврежден" или как-то так.
Не вдаваясь в лишние подробности, скажу, что образ действительно получился битый и md5 на ftp и у меня не совпали. Сегодня ночью с того же ftp скачался тот же (или перезалитый, чтобы скрыть повреждения?) образ, md5 совпали и я нормально установился с него.
Однако, всё это некрасивая лирика, а мне захотелось прекрасного. Например, как было в далёком DOS-овском детстве, можно было с помощью PCTools или Norton Utilities побайтно сравнивать файлы с большим комфортом, чем позволял штатный fc или cmp. Штатной приблуды в стандартной поставке "искаропки" нет, но свет не без добрых людей... Аж в 2010 году кто-то заинтересовался вопросом и получил на него интересные и полезные ответы.

--
diff  + xxd
Try diff in the following combination of zsh/bash process substitution:
diff -y <(xxd foo1.bin) <(xxd foo2.bin)
Where:
  • -y shows you differences side-by-side (optional).
  • xxd is CLI tool to create a hexdump output of the binary file.
  • Add -W200 to diff for wider output (of 200 characters per line).
  • For colors, use colordiff as shown below.

colordiff + xxd

If you've colordiff, it can colorize diff output, e.g.:
colordiff -y <(xxd foo1.bin) <(xxd foo2.bin)
Otherwise install via: sudo apt-get install colordiff.
binary file output in terminal - diff -y <(xxd foo1.bin) <(xxd foo2.bin) | colordiff
--
There's a tool called DHEX which may do the job, and there's another tool called VBinDiff.
For a strictly command-line approach, try jojodiff.
--

четверг, 10 октября 2019 г.

#!/bin/bash: поиск дубликатов файлов с выводом в csv

Все мы люди и все мы человеки. Со временем наша лень побеждает нас, и некогда красиво организованные файловые ресурсы превращаются в совершенно некрасивые файловые помойки. В том числе и за счет дублирующихся файлов.
С одной стороны, есть в убунтах, а также прочих никсах утилита fdupes, которая ищет дубликаты файлов в заданном пути. С другой, вывод этой утилиты далёк от совершенства и результат не очень пригоден для дальнейшей автоматизированной обработки. Пришлось написать скрипт-обёртку, который берет вывод fdupes и выводит результаты во что-то похожее на csv.



На выходе получаем CSV (разделитель полей - точка с запятой) в UTF8. Excel в этом плане туп и открывает его в 1251. Решение: в новой книге сделать импорт данных "из текста", задав нужные параметры.
Open/Libre-office сразу предлагают окно настроек и открывают в правильной кодировке.

суббота, 13 июля 2019 г.

bash: хардлинки и файл - кто на кого указывает? files and hardlinks

Разгребал очередной завал в зимбре и выяснил, что есть там дедупликация: если сообщение приходит нескольким получателям, то вместо размножения файлов создаются хардлинки на один его экземпляр. Например, у одного из сообщений, отправленных по общему списку рассылки, было за 60 хардлинков.
Возник вопрос: а где же остальные ссылки на этот файл, если файлов с такими же именами нет? Пришлось копаться в этих ваших интернетах, и удалось накопать такой интересный материал.

Весь материал копировать не буду, ограничусь основным:

1. количество хардлинков:
ls -l имя_файла
например:
drwxr-xr-x  2 root   root     4096 апр 17 23:55  模板
цифра "2" после прав доступа показывает количество хардлинков на этот файл, точнее - на его иноду.

2. что за инода?
stat 模板
   Файл: 模板
   Размер: 4096      Блоков: 8          Блок В/В: 4096   каталог
Устройство: 819h/2073d Inode: 6004887     Ссылки: 2
Конкретно у каталога с шаблонами от WPS Office номер инода 6004887

3. на фиг оно нам надо?
Хардлинк - это одна из записей в каталоге, указывает на конкретное размещение файла в файловой системе. Место такое одно, а вот ссылок на него может быть много. На каталог без вложенных подкаталогов, как этот с китайским названием, всего две ссылки. Если в каталоге есть подкаталоги, то ссылок становится больше для обратной связи от каталогов более низкого уровня.

4. не, а всё-таки, на кой мне знать эту иноду, если мне нужно найти дубликаты?
А вот на кой:
Вариант 1:
find ~/ -inum 6004887
/home/sergei/模板
Начиная с домашней папки я поискал и нашел файл с таким номером иноды. Но почему он один? Потому что это пустой каталог, в котором есть только файлы и еще есть "псевдо-файл" с именем "." (точка), ссылающийся на самого себя. То есть, по факту ссылок две - из каталога предыдущего уровня и сам-на-себя, а файл всего один. Но если зайти внутрь:
~/模板$ ls -l
drwxr-xr-x   2 root   root    4096 апр 17 23:55  ./ 

drwxr-xr-x 144 sergei sergei 16384 июл 11 06:03  ../
то видно, например, что на домашний каталог аж 144 ссылки

5. всё равно непонятно, неужто нельзя быстрее, чем сначала узнавать иноду, а потом долго и нудно искать?
Можно! Оказывается - можно.
find ~/ -samefile 模板
/home/sergei/模板


То есть, чтобы найти все ссылки на конкретный файл, можно просто воспользоваться ключом -samefile. ЧТД.

пятница, 15 марта 2019 г.

bash: разница между двумя моментами времени

Есть у нас хитрая система учета рабочего времени, которая требует указывать продолжительность выполненных работ исключительно в минутах. Например, с 8:45 до 13:30 я был на выезде. Вопрос: сколько минут я должен записать?

Не, оно, конечно, несложно - "без 15 минут 5 часов", то есть, 4 часа 45 минут или 285 минут.
Но в гробу я видал заниматься временнОй арифметикой в уме - у меня есть и более интересные занятия. Поэтому написал небольшой не оптимизированный скрипт без наворотов, который чисто средствами баша считает разницу в минутах (и не только) между двумя моментами времени в пределах одних суток. Допиливать его под переход суток и т.п. мне лень, допилю, когда и если понадобится.

(примечание: между двумя вхождениями "Descriptions" идет длинный коммент в стандарте heredocs - см. руководство по башу, чтобы не ставить кучу диезов)

#!/bin/bash
<
<<Descriptions
 timediff - разница между двумя временами в пределах суток
 $1 - начальное время
 $2 - конечное время

 Формат параметров:
 ч:м[:с]
 ведущие нули не обязательны, но если нужно указать только минуты, то ноль часов указывать обязательно:
 :12:34 - неправильно
 0:12:34 - правильно

 Проверки на допустимость параметров не делаются, поэтому в случае чего вываливается с ошибкой "смените пользователя"

Descriptions

# с учетом возможной переделки, используем переменные для времен
# пока что $TimeX и $n - одно и то же, но в будущем это может измениться
TimeS=$1
TimeF=$2

# TimeStamp Start/Finish
TSS=$( date +%s -d $TimeS )
TSF=$( date +%s -d $TimeF )

# Разница в секундах, целых минутах, целых часах
DiffS=$(( $TSF-$TSS ))
DiffM=$(( $DiffS/60 ))
DiffH=$(( $DiffM/60 ))
# Сколько секунд сверх целых минут и минут сверх целых часов
ExtraS=$(( $DiffS-$DiffM*60 ))
ExtraM=$(( $DiffM-$DiffH*60 ))

# продолжительность в разных единицах
echo ${DiffS}s
echo ${DiffM}m
echo ${DiffH}h

# в минутах и секундах (минут может быть больше 60)
echo ${DiffM}:${ExtraS}"(m:s)"
# в часах, минутах и секундах
echo ${DiffH}:${ExtraM}:${ExtraS}"(h:m:s)"

Например:

$ timediff.sh 8:45 13:30
17100s
285m
4h
285:0(m:s)
4:45:0(h:m:s)


пятница, 26 октября 2018 г.

bash: список имеющихся расширений файлов в каталоге и подкаталогах

Есть у меня сервер на самбе, вполне себе рабочий. В нем есть поддержка "корзины" (в отличие от микрософтовских серверов). Но вот какая штука: в корзину валятся всякие файлы, в т.ч. и временные. И за рабочий день их может набраться очень много.
Каждую ночь из "корзин" удаляются файлы старше 30 дней и просто очень толстые, а также - временные. Но иногда временных очень уж много. Стало быть, надо не помещать их в корзину вовсе.
Решение очевидно:

(/etc/samba/smb.conf или его инклюд)
[global]
    vfs objects = recycle
    recycle:keeptree=yes
    recycle:repository= .recycle/%U
    recycle:exclude=*.tmp,~*,*.~*

Однако, кроме трёх указанных типов (расширений имён) файлов могут удаляться и самые разные другие, в т.ч. и такие, которым незачем занимать место в "корзине". Просматривать всё вручную - не труЪ. Логично будет сначала составить список имеющихся расширений, а уже от него плясать. Но наконец-то я нашел ситуацию, для которой в никсах, кажется, нет подходящей утилиты - для выделения только расширения файла. Выделить имя - есть basename, выделить путь - dirname, а вот для выделения суффикса ничего нет.

Покопался, нашел замечательно:

(чего-то там) |awk -F . '{print $NF}'

Пример самоочевиден: объявляем точку разделителем полей и выводим то, что есть в имени файла правее последней точки. Не очень надежно, если есть файлы без расширения, и не очень быстро из-за лишних обращений к awk, но для моих целей подошло:

# cd /var/samba/share
# find -depth -type f -wholename *recycle/* |awk -F . '{print $NF}'|sort|uniq

На выходе получаем отсортированный список уникальных расширений файлов, попавших в "корзины". Дальше осталось только выбрать самые ненужные и добавить их в список настроек виртуальной файловой системы "корзина".

Кроме того, на любимом StackExchange нашел еще приятное обсуждение по этому поводу.

Ну а для любителей башизмов, использующих встроенные возможности по максимуму, есть вот такая прелесть:

# Из начала строки убираем самое длинное вхождение "*/", получаем имя файла с расширением 
filename="${fullpath##*/}";
# Берем подстроку от начала строки (поз. 0) до позиции, где начинается собственно имя файла
### TM: по сути, именно это делает команда dirname
directory="${fullpath:0:${#fullpath} - ${#filename}}";
# Отрезаем самую короткую подстроку, состоящую из точки и как минимум одного "обычного" символа, с конца имени файла
### TM: по сути, именно это делает команда basename
base="${filename%.[^.]*}";
# Берем подстроку от позиции "длина базового имени" до конца полного имени файла
extension="${filename:${#base} + 1}";
# Если на выходе у нас есть что-то в $extension, но ничего нет в $base, то файл был без расширения
if [[ -z "$base" && -n "$extension" ]]; then
  base=".$extension";
  extension="";
fi
echo -e "Оригинал:\t'$fullpath':\n\tПуть:\t'$directory'\n\tПолное имя файла:\t'$filename'\n\tБазовое имя:\t'$base'\n\tРасширение:\t'$extension'"
Специально оставил даже форматирование, как оно было в оригинале, только комментарии перетолмачил.

среда, 3 октября 2018 г.

bash: Получение читаемой информации о пользователе из ADDS

Понадобилось мне проверить, что случилось с пользователем. Под рукой только домашняя убунта, лезть терминалом на сервер на работе не комильфо.

Но есть настроенный ldapsearch, которым мы и воспользуемся. Изначально фрагмент писался для получения данных о компах, поэтому переменная, которую ищем, называется $comp

NB! В итоговом массиве есть только одна переменная с индексом memberof, хотя пользователь может входить в несколько групп. Список групп я получаю более другими способами, поэтому ситуация в этом скрипте не анализируется. Возможно надо проверять значение индексера, и если он есть memberof, то дописывать значение параметра к переменной с таким индексом. Лично мне это просто не требуется.

#!/bin/bash
# похоже, что без промежуточного файла не обойтись - вывод ldapsearch напрямую не перехватывается
comp=$1
unset ldapData
# ldapData - ассоциативный, а не нумерованный массив!
declare -A ldapData
ldapsearch -o ldif-wrap=no -D ПОЛЬЗОВАТЕЛЬ -x -w "ПАРОЛЬ" "(cn=$comp)">/tmp/ldifDataFile
# список полей с датами, которые надо декодировать
# ведущий и хвостовой пробелы обязательны
datesList=" lastlogon pwdlastset badpasswordtime accountexpires "
while read ldiffPar ldiffData;do
  if [ "${ldiffPar:0:1}" == "#" ] || [ ${#ldiffPar} -eq 0 ]; then continue; else

# приводим ldiffPar к нижнему регистру
    ldiffPar=${ldiffPar,,}

# а для переменной-индекса убираем все двоеточия, сколько бы их там ни было
    indexer=${ldiffPar//:/}

# если два последних символа имени атрибута два двоеточия, значит он кодированный и нуждается в декодировании
    if [ ${ldiffPar: -2} == "::" ]; then
      ldapData[$indexer]="$( echo $ldiffData|base64 -d )"
    else
      ldapData[$indexer]="$ldiffData"
    fi
# если название атрибута в ADDS есть в списке $datesList, то переводим дату из
# формата MS в человекочитаемый
# не очень надежный способ поиска, поэтому ищем "пробел-индексер-пробел", а не просто "индексер"
    if  [[ "$datesList" = *" $indexer "*  ]]
    then
      ldapData[$indexer]=$( date -d @$(( ${ldapData[$indexer]}/10000000-11644473600 )) +%Y-%m-%d\ %H:%M:%S)
    fi
# отдельное внимание состоянию пользователя
    if [ $indexer == "useraccountcontrol" ]
    then
      isActive=$(( ${ldapData[$indexer]} & 2 ))
      if [ $isActive -eq 0 ]; then uacState="АКТИВЕН "; else uacState="не активен ";fi
      ldapData[$indexer]=$uacState${ldapData[$indexer]}
    fi
    echo ldapData[$indexer]=${ldapData[$indexer]}
  fi
done


На выходе получаем список всех непустых атрибутов пользователя.

среда, 13 июня 2018 г.

И снова о просмотре логов squid и выводе полей в awk.

Очередной способ получить человеко-читаемое время в логах прокси-сервера.

awk '{print strftime("%Y-%m-%d %H:%M:%S",$1) " " substr($0, index($0,$2))}' FILENAME

Основная идея: по умолчанию поля в этом логе отделяются пробелами. Awk преобразует первое поле (дата и время в юникс-формате) в читаемый вид и выводит его. Далее печатается пробел, после которого печатается всё от начала второго поля до конца строки.

Как выяснилось, это самый быстрый способ заставить awk вывести "всё остальное", не перебирая поля последовательно в цикле.

Решение найдено здесь.

воскресенье, 10 июня 2018 г.

bash: поиск и замена

Sed, awk, grep - всё это здорово. Но это внешние программы, а что же по части встроенных? Их есть у меня!

${переменная/что/на что} - меняем первое вхождение "что" на "на что"
${переменная//что/на что} - меняем все вхождения "что" на "на что"

Например, есть переменная, в которой хранится путь:

filNam="sambas/samba201/shares.conf"

но из этого пути мне надо сформировать имя файла. Не выделить собственно имя из пути, а просто как-то изменить путь. Легко! Заменим все слэши на дефисы.

$ echo ${filNam/\//-}
sambas-samba201/shares.conf

$ echo ${filNam//\//-}
sambas-samba201-shares.conf

суббота, 9 июня 2018 г.

bash: сравнение строк без учета регистра

Понадобилось сравнить строки, конкретно - атрибуты CN и SAMAccountName в AD. Только не спрашивайте, на кой чёрт мне в баше надо сравнивать виндовые данные. Просто удобнее, чем километровые команды в PS.

Можно, конечно, сначала эти строки преобразовать к верхнему или нижнему регистру с помощью tr [:upper:] [:lower:], но это лишние команды и лишнее время выполнения скрипта, особенно если надо сравнивать много таких строк.
Чтобы избежать вызова этих лишних команд, воспользуемся встроенными возможностями:

${переменная,,} - (две запятых) привести переменную к нижнему регистру
${переменная^^} - (два циркумфлекса) привести переменную к верхнему регистру

Найдено здесь.

p.s. для вытаскивания нужных атрибутов из AD я пользуюсь как самбовской net, так и adtool, про которую уже рассказывал.

среда, 13 декабря 2017 г.

bash: массовое уменьшение фото в каталоге

Есть в нашем мире долбодятлы. Чем они отличаются от обычных людей? Например тем, что принципиально не способны выбирать для работы правильный инструмент. Например, они пробивают перфоратором с метровым буром деревянную доску-десятку. Или забивают в нее гвозди кувалдой, которой только что вгоняли костыли в шпалы. Или будут есть китайскими палочками не только лапшу и салаты, но и яичницу-глазунью или суп. Только потому, что ухватив инструмент, они не хотят брать другой. Или не знают, что он есть. Но чаще им просто лень потратить несколько секунд для того чтобы взять более подходящий инструмент.
Как известно, каждый дрочит как хочет, и я не собираюсь обсуждать любителей забивать гвозди микроскопом. До тех пор, пока это не мешает работе моих файловых серверов. Нет, ни любители палочек, ни любители перфораторов мне не мешают. А вот любители размещать на серверах фото в огромном разрешении - эти мешают. И они тоже относятся к породе долбодятлов.
Например, важный снимок - ход строительства одного из наших объектов. Снято на мультимегапиксельную говномыльницу. Размер снимка 5 мегов. Сюжет: кусок кирпичной стены и тропинка вдоль нее. Всё. Никаких мелких или существенных деталей нет. Просто оператору было лень выбрать режим съемки 3 или 2Мпикс, он как выставил при покупке 12Мпикс, так и щелкает всё подряд.
Возникает вопрос: как утоптать всё это в более-менее скромный объем, если пациент принципиально не желает пользоваться настройками своего фотоаппарата или программой для уменьшения геометрических размеров фото? Очень просто: уменьшить эти снимки самостоятельно. В этом нам поможет convert из пакета ImageMagick.

Находим каталог с большим количеством неоправданно жирных фото.

find -iname *.jpg -size +3M -exec ls -l "{}" \; >large-photos

Просматриваем этот файл. Я делаю это вручную, но только потому что, мне лень еще полнее автоматизировать процесс, но может когда-то и доведу его до полного солипсизма полного автоматизма. Находим каталог-жертву и делаем ему матумбу (записано в одну строку, но внутри скрипта можно размазать и на несколько:

for a in *;do fn="${a%.*}";fe="${a##*.}";convert $a -resize 50% ${fn}s\.$fe;chmod ${fn}s\.$fe --reference=$a;chown ${fn}s\.$fe --reference=$a;touch ${fn}s\.$fe --reference=$a;done

Итак, по порядку:

for a in * - список файлов текущего каталога
fn - имя файла без расширения, выделяется с помощью штатных возможностей bash. (в Advanced Bash Scriptuing Guide глава 10. Manipulating Variables, раздел 10.1. Manipulating Strings, подраздел Substring Removal)
fe - расширение файла без имени, выделяется аналогично fn. Две этих раздельных части имени пригодятся позже
convert ИМЯ_ИСХОДНОГО_ФАЙЛА -resize 50% - уменьшает геометрический размер изображения в 4 раза (на 50% по каждой стороне).
${fn}s\.$fe - ...и записывает его файл с именем вида fns.fne, где "s" - это дописываемый к исходному имени суффикс, сокращение от слова "scaled" (масштабированный). Чтобы не возиться с определением места, куда вставить эту "s", я и использую отдельно взятые имя и расширение. На всякий случай экранируем точку, отделяющую имя от расширения.
chmod, chown и touch - заметаем следы, назначая свежесозданному мини-рисунку все атрибуты его большего предка: владельца, группу, права доступа и времена. У всех трёх команд набор параметров одинаковый: имя файла, которым манипулируем и после ключа --reference - имя файла, у которого берем "эталонные" значения. В моем случае это исходный файл.

Например, есть файл DSC01234.jPg. Соответственно:
fn=DSC01234
fe=jPg
уменьшенная копия будет записана в файл DSC01234s.jPg, и получит все атрибуты оригинала.

Я ориентировался на имена файлов, создаваемых нормальными камерами, то есть, не содержащие пробелов и спецсимволов. Возможно, для большей универсальности понадобятся кавычки.
Почему $fe используется сама по себе, а $fn - только в форме ${fn}? Потому что после $fn идет суффикс "s", который в противном случае будет считаться частью имени несуществующей переменной $fns.
Разумеется, для красоты можно ${fn}s\.$fe запихать в переменную, и, возможно, так и придется сделать при обработке файлов с вычурными именами, но пока для меня это не актуально.

четверг, 17 августа 2017 г.

Adtool и встроенная убунта в win10

В феврале 2017 я коротко рассказал о найденных мной утилитах для работы с AD и WMI из линукса.
Там же есть вполне реальный пример, где я ищу владельцев компьютеров. С тех пор прошло около полугода, мой боевой потрёпанный винсервер-2003 (на рабочем месте) заставили сменить на win10. Не скажу, что оно меня сильно радует, но после апгрейда до 1703 интерфейс выглядит достаточно удобным (я сказал "достаточно удобным", а не просто "удобным" - это значит, что в нем уже можно работать, матерясь не через каждую минуту, а не чаще чем раз в полчаса-час), и появилась отличная штука - "Подсистема Linux для Windows":

sergei@cnt300022:~/bin$ uname -a
Linux cnt300022 4.4.0-43-Microsoft #1-Microsoft Wed Dec 31 14:42:53 PST 2014 x86_64 x86_64 x86_64 GNU/Linux

sergei@cnt300022:~/bin$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 16.04.3 LTS
Release:        16.04
Codename:       xenial

Не буду подробно останавливаться на этой подсистеме, скажу лишь, что она доступна только для 64-битных версий, и что на нее без особых проблем можно ставить и обновлять софт из убунтийско-дебиановых репозиториев. При наличии работающего икс-сервера можно запускать и графические задачи (все 4 видимых приложения запущены из "встроенной" убунты для демонстрационных целей):


Однако, лично мне эта возможность пока без особой надобности, а вот возможность писать и отлаживать скрипты в баше - очень полезна. Чем я и занялся, решая свои повседневные задачи. В данном случае я снова решил довести до ума скрипт, выдергивающий из AD связи между компами и пользователями.

Возможно, кому-нибудь пригодятся и использованные приемы работы со строками, которые я применил в этом скрипте, но о которых не догадался в феврале.

#!/bin/bash
# Выгребаем из AD имена компов и находим их владельцев

# Для элегантности используем переменные и константы вместо длинных выражений.
COMPS_FILE=/tmp/comps-list.txt
OUT_COMPS_FILE=/tmp/comps_out.csv

# В моем домене все компы имеют вид cnt30xxxx, где cnt30 - постоянная часть, а

# xxx ::= [0-9]{3}[0-9a-z]{1} для рабочих станций и typ[0-9]{3} для других типов железа
# Однако, разные типы компов могут жить в разных OU, поэтому вместо "list" использую "search"

# Для удобства дальнейшей обработки найденное приводим к верхнему регистру
adtool search name cnt30*|grep -iE ^cn=cnt30[0-9]{3}[0-9a-z],|tr [:lower:] [:upper:]|sort>$COMPS_FILE


# Поскольку вывожу я всё это в подобие CSV, то сначала пишем строку заголовка
echo \"Comp DN\"\;\"Comp CN
\"\;\"UAC\"\;\"Managed by\"\;\"Description\">$OUT_COMPS_FILE

# А дальше стандартный скучный цикл построчного чтения из созданного чуть выше списка
while read COMP_NAME; do

# Поскольку attributeget работает по CN, а не по DN, но при этом search возвращает именно DN,

# то надо из DN как-то выделить CN. 
# Так можно и работает - так же я делал и в феврале, выкусывая слева "cn=", а справа всё
# после первой запятой
#    PURE_NAME=$(echo ${COMP_NAME/CN\=/}|sed "s/\,.*//")
# Но можно и так (берем 9 знаков, начиная с третьего, нумерация с нуля):
# cn=cnt300000
# 0123456789AB
    PURE_NAME=${COMP_NAME:3:9}

# Не уверен, может ли attributeget получать сразу несколько атрибутов, вероятно - нет,

# так что работаем по старинке: 1 переменная хранит 1 атрибут
    COMP_DESC=$(adtool attributeget $PURE_NAME description)

# Здесь тоже надо выделить чистое CN из прочитанного DN, но имя пользователя имеет неизвестную
# длину, поэтому выбираем его универсальным, хотя и более медленным способом
    COMP_MGBY=$(adtool attributeget $PURE_NAME managedby|sed "s/CN=//;s/\,.*//")

# Меня не очень волнует, есть ли атрибут managedBy у заблокированных компов, но раз в списке
# будут они все, то добавим столбец, показывающий состояние учётки.
# За признак enabled/disabled отвечает бит со значением 0b10 или 0d2, сделаем с ним AND
# и точно будем знать: если 0, значит учетка активна, если 2 - заблокирована
    COMP_UAC=$(( $(adtool attributeget $PURE_NAME UserAccountControl) & 2 ))

# Осталось только всё это красиво вывести
    echo \"$COMP_NAME\"\;\"$PURE_NAME\"\;\"$COMP_UAC\"\;\"$COMP_MGBY\"\;\"$COMP_DESC\"
done <$COMPS_FILE >>$OUT_COMPS_FILE

exit

Во время работы скрипта будут, скорее всего, появляться сообщения типа "error: Error in ldap_get_values for ad_get_attribute:no values found for attribute managedby in object CN=CNT306552,OU=330,OU=Comps,OU=Domain Root Entry,DC=CONTOSO,DC=MS,DC=ru", это нормально - они выводятся на stderr, а наш вывод через stdout падает сразу в файл, так что чистить выходной файл от мусора не понадобится.

На выходе получим вполне приятный CSV вроде:

"Comp DN";"CompCN";"UAC";"Managed by";"Description"
...
"CN=CNT30002A,OU=330,OU=COMPS,OU=DOMAIN ROOT ENTRY,DC=CONTOSO,DC=MS,DC=RU";"CNT30002A";"2";"Сергей Павлович";"убитый комп"
"CN=CNT30002B,OU=330,OU=COMPS,OU=DOMAIN ROOT ENTRY,DC=CONTOSO,DC=MS,DC=RU";"CNT30002B";"0";"Сергей Павлович";"тестовая Win10"

среда, 8 февраля 2017 г.

bash, AD: выбор пользователей из группы, входящих и в другую группу

Есть в нашем домене группа, куда добавлены работники разных филиалов, но мне надо обработать только тех, что находятся в моем филиале. Причем не всех, а только из отдельных подразделений.
В свое время я предлагал сделать возможность создания вложенных OU, чтобы группировать пользователей по отделам внутри филиала. В ответ узнал очень много о своей умственной неполноценности.
Не буду сейчас характеризовать тех, кто этого добивался - свалить всех в одну кучу, потому что свои проблемы я решил. Не так эффективно, как можно было, но для моих целей достаточно. А как они там фигачат километровые скрипты на PowerShell, чтобы найти жемчужинку в тоннах информационного навоза, меня не касается.
Однако, изменить структуру домена я не могу, поэтому приспосабливаемся.

Итак, задача:
1. есть группа, в которую свалены работники разных филиалов. Все логины имеют вид фамилия_инициалыN, где N - цифра, добавляемая в случае совпадения фамилии и инициалов (вонючий костыль вместо того, чтобы разрешить создавать субдомены)
2. мне надо отобрать своих
3. что-то с этими своими сделать.

bash: суммирование столбца чисел

Один из возможных вариантов. Есть и другие, например, преобразовать столбец в строку и скормить ее bc или dc.


Суммируем 3-й столбец из лог файла.
https://www.opennet.ru/tips/457_file_awk_log_print.shtml

cat logfile| awk '{s += $3} END {print s}'

То же самое, но более развёрнуто и с пояснениями:

http://kb.h1host.ru/article/view?id=55
 

четверг, 10 ноября 2016 г.

bash: Рекурсивно разворачиваем доменную группу в список пользователей

Есть у меня в домене супер-группа "разрешен доступ в интернет". Почему супер? Потому что в неё включаются преимущественно другие группы, по одной на каждое подразделение, в которые могут быть включены другие группы и т.д. Возникла задача: узнать, сколько всего пользователей прямо или косвенно входят в эту группу.

Синтаксис виндовой dsget и её родственных утилит вызывает у меня стойкое отвращение обилием лишних параметров и общей запутанностью. Но раз уж прокси-сервер у меня работает под убунтой, то почему бы не задействовать мощный язык bash-a и возможности Samba?

Что у нас есть из инструментов? В первую и главную очередь - сильномогучая команда net. Во вторую - очень полезная команда wbinfo. Надо скомпоновать результаты их работы, чтобы на выходе получить список пользователей группы и всех входящих в неё подгрупп.

Далее по тексту подразумевается, что всё выполняется на компьютере с установленной Samba, введённом в домен. Разделитель домена и имени объекта - обратный слэш "\".
Все команды выполняются от имени супер-пользователя.

среда, 18 мая 2016 г.

bash: цикл по именам файлов с пробелами

Цикл FOR по списку в bash работает хорошо. До тех пор, пока внутри элементов списка не появляются пробелы - вместе с пробелами появляются и проблемы: каждое слово в составе элемента списка рассматривается отдельно, и если попадается файл вроде "цикл не работает", то for будет выполнен для него три раза - для "цикл", для "не" и для "работает", что не есть хорошо.

Понадобилось мне сконвертировать несколько файлов из png в jpg. И как раз наступил на упомянутые грабли (имена файлов настоящие и опечатки в слове "внутренний" нет):

$ for a in `ls -Q litb\ внтуренний\ счет*.png`; do echo "$a"; done
"litb
внтуренний
счет2.png"
"litb
внтуренний
счет.png"

среда, 4 мая 2016 г.

Пауза в bash-скриптах

К моему удивлению, штатной "паузы", подобной команде pause в языке пакетных файлов DOS в bash не обнаружилось. Но это было бы не труЪ, если бы ее нельзя было придумать.

http://www.cyberciti.biz/tips/linux-unix-pause-command.html

Два  основных решения:

1. Не совсем пауза: будет ждать именно нажатия ENTER

read -p "Нажмите ВВОД для продолжения"

2. Ближе к древнему оригиналу:

read -n 1 -p "Нажмите любую клавишу для продолжения..."

3. Комбинированное решение:

read -n 1 -t 5 -p "Нажмите любую клавишу или подождите 5 секунд для продолжения..."

4. труЪ

#!/bin/bash
# init
function pause(){
   read -p "$*"
}
 
# ...
# call it
pause 'Press [Enter] key to continue...'

четверг, 28 апреля 2016 г.

Массовое переименование файлов в bash с заменой символов в середине имени

Притомили коллеги, лепящие в именах файлов все символы, предусмотренные юникодом. Кое-кто даже умудрился (честно, я не знаю, как) сохранить файл, в имени которого несколько штук \127!

Но это уже клинический случай, а есть более мягкие, когда по неизвестной мне причине в именах появляются точки с запятой. Для устранения этого безобразия заменим ";" на ",":

find -iname *\;*|while IFS='' read line; \
  do NEWNAME=`echo "$line"|sed 's/\;/\,/g'`; \
  mv "$line" "$NEWNAME"; done

Пришлось использовать переменную, потому что мне не удалось заставить задействованную в ней конструкцию правильно отдаться sed-у из-за возможных пробелов в именах файлов.