понедельник, 22 апреля 2019 г.

Вывод из файла определённых строк средствами bash


Необходимость вывода определённых строк из файла возникла при дальнейшем анализе автоматически созданных отчётов о состоянии Диска Яндекс. Особенно это актуально при использовании нескольких аккаунтов Диска Яндекс с целью определения: вместится ли туда дополнительный объём информации или надо ждать освобождения доступного дискового пространства в результате автоматической очистки его Корзины (или очищать Корзину вручную). После выполнения команды yandex-disk status и записи результатов в файл yandex-disk.txt, например

yandex-disk status > /home/user/reposrts/yandex-disk.txt

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

Статус ядра синхронизации: ожидание команды
Путь к папке Яндекс.Диска: '/home/user/Yandex.Disk/account1'
Всего: 43 GB
Занято: 38.44 GB
Свободно: 4.56 GB
Максимальный размер файла: 50 GB
Размер корзины: 6,38 GB

Последние синхронизированные пути:

...

Из полученного файла предмет интереса составляют строки "Занято", "Свободно", "Размер корзины". Другими словами, интерес представляют только строки 4, 5 и 7.

С этой целью создан скрипт на bash, в котором

reports – указание каталога отчётов,

values – указание каталога для записи промежуточных значений,

sed -n -e 4,5p -e 7p  – команда вывода 4, 5 и 7 строк из файла отчёта accoun1.txt

#!/bin/bash
reports=/home/user/reports
values=/home/user/reports/values
yandex-disk status > $values/accoun1.txt
sed -n -e 4,5p -e 7p $values/account1.txt > $reports/account1.txt
exit

В результате его выполнения получается желаемый результат:

Занято: 38.44 GB
Свободно: 4.56 GB
Размер корзины: 6,38 GB

При "разборе полётов" был использован материал ресурса rus-linux.net.

среда, 17 апреля 2019 г.

Предупреждение о заполненности диска через bash

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

Несмотря на имевшийся ранее опыт эксплуатации жёстких дисков с заполненностью до 99 % , такие диски являлись хранилищами данных, операции записи и удаления на которых производились не ежедневно. На диске с установленной системой такое состояние вряд ли можно считать приемлемым.

Стимулами к автоматизации уведомления о сильной заполненности диска явились сравнительно малая ёмкость винчестера и оказание содействия другу, совершенно недавно переметнувшемуся из союза пользователей Windows в сообщество Linux, имеющему точно такой же винчестер как и у меня объёмом в 500 Гигабайт (при форматировании в основной раздел с файловой системой его объём превращается в 465 Гигабайт).


Так как считается, что заполнять диск данными более чем 90 % не является желательным, а для операций по исправлению ошибок на файловой системе (особенно в ходе которых будут осуществляться попытки перезаписи данных из плохих кластеров в хорошие) может потребоваться не менее 5 % доступного для записи места на диске, то было принято решение автоматически уведомлять о наличии свободного места (доступного пользователю), начиная от его значений в 10 % и ниже. Несмотря также на то, что по сравнению с ntfs файловая система ext4 в значительно меньшей степени подвержена фрагментации, её чрезмерное заполнение может дурно повлиять на производительность. Если кому интересно про дефрагментацию в Linux, то читать здесь.

В публикации рассматривается диск, на котором имеется 1 раздел с установленной системой Linux. Отдельного раздела под /home не создавалось, домашний каталог расположен в корне файловой системы, поэтому в дальнейшем имеется в виду, что диск соответствует /dev/sda1

Автоматизация уведомления достигается запуском скрипта при старте системы или через назначение пользовательского задания cron.

Формируемые в процессе работы скрипта данные записываются в выделенный для этой цели каталог  /home/user/scripts/temp , который в скрипте будет выступать как переменная dest. Создаваемые файлы имеют имена df*.txt, где под символом * понимаются значения от 1 до 6. Под user понимается домашний каталог пользователя, под логином которого осуществлён вход в систему.

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

#!/bin/bash
dest=/home/user/scripts/temp

Получить сведения о дисковом пространстве:

df -h /dev/sda1 > $dest/df1.txt

Файл df1.txt будет иметь примерно такой вывод:


Внимательный пользователь может задаться вопросом о разнице между снимком экрана в GParted (в начале заметки) и только что приведенным. Дело в том, что используются разные алгоритмы подсчёта, при которых какие-то данные учитываются, а какие-то – нет. Кроме того, ряд данных может оказаться недоступным для чтения. 
Определённую информацию о существующих в этом вопросе разночтениях можно получить здесь и здесь. Лично мне понравится комментарий: "Другое дело нужно не забывать о разнице между K/M/G и KiB/MiB/GiB. Собственно за *iB хочется вырвать все конечности маркетологам производителей HDD, которые внедрили K=1000 в информационных системах когда изначально K было 1024 для IT и 1000 для физиков.".
Кроме того, вывод df1.txt отображает более объективную информацию, так как показывает дисковое пространство, которое доступно (см. Дост 44G). "Доступно" – это именно тот объём диска, на который пользователь может осуществить запись данных под своими полномочиями в системе. Часть свободного дискового пространства диска пользователю не доступна для записи.

Из файла df1.txt необходимо получить вывод 5-го столбца и записать его в файл df2.txt

cat $dest/df1.txt | awk '{print($5)}' > $dest/df2.txt


Из полученного результата для дальнейшего использования нужны только цифры, поэтому из df2.txt последовательно удаляются слово Использовано% и символ % . Результаты операций записываются в файлы df3.txt и df4.txt

str=Использовано%
while read LINE; do
echo "${LINE##*$str}"
done < $dest/df2.txt | tee $dest/df3.txt


rev $dest/df3.txt | cut -c 2- | rev > $dest/df4.txt


Так как далее скрипт будет "читать" вывод из файла, то в df4.txt необходимо избавиться от первой строки, которая является пустой. Результат записывается в файл df5.txt

sed -n 2p $dest/df4.txt > $dest/df5.txt


Предупреждение о заполненности диска использует условие "если значение равно x, то y (выводится предупреждение), а если не равно х, то z (ничего не делать).

Выполнение условия реализуется инструкцией:

if [ "значение" -eq x ];
then
вывод предупреждения
else
:
fi

Но параметр -eq не применяется с двузначными числами, поэтому из df5.txt необходимо удалить первый символ и записать результат в df6.txt

rev $dest/df5.txt | cut -c 1 | rev > $dest/df6.txt


Полученный результат уже можно использовать в операциях соответствия заданным условиям.

Предусматривается, что при значениях df6.txt от 0 до 9 будут выводиться предупреждения:

0 – осталось свободно 10 % диска
1 – осталось свободно 9 % диска
2 – осталось свободно 8 % диска
3 – осталось свободно 7 % диска
4 – осталось свободно 6 % диска
5 – осталось свободно 5 % диска
6 – осталось свободно 4 % диска
7 – осталось свободно 3 % диска
8 – осталось свободно 2 % диска
9 – осталось свободно 1 % диска

которые будут отображаться на экране до тех пор, пока пользователь не нажмёт кнопку OK. Содержанию файла df6.txt присваивается переменная value.

value=`cat $dest/df6.txt`
if [ $value -eq 0...9 ];
then
zenity --width=190 --height=50 --info --text "\nОсталось свободно 10...1 % диска." 2>/dev/null
else
:
fi

Теоретически, при каждом запуске скрипта в имеющиеся в файлах df*.txt значения будут записываться новыми. Но эти промежуточные данные можно и удалять. Например, удалить перечисленное:

rm $dest/df1.txt $dest/df2.txt $dest/df3.txt $dest/df4.txt $dest/df5.txt $dest/df6.txt

или найти и удалить объекты, совпадающие по шаблону имени – 

find $dest -name df*.txt -delete

Всё было бы неплохо, если бы полученные в df5.txt значения были от 90 и выше. Но они могут быть и 20, и 30, и 70 ... В таком случае если занято 72% диска, то будет выдано предупреждение о наличии только 8% доступного (свободного) места на диске, что далеко не соответствует действительности. Поэтому после получения значения df5.txt производится дополнительное сравнение: если 89 и менее, то всё хорошо и ничего делать не надо, в противном случае необходимо выдавать предупреждения.

Проверка реализуется следующим образом. Читается значение a, которое и является результатом вывода df5.txt  Значению b устанавливается пороговая величина 89. Затем производится сравнение двузначных чисел a и b.

a=`cat $dest/df5.txt`
b=89
if [ $a -le $b ]
then
# 89% и меньше, ничего делать не надо, всё в порядке
else
# команды вывода предупреждения
fi

Команды вывода предупреждений оформляются в отдельный скрипт alarm.sh, который будет запущен в случае else (см. выше).

В конечном итоге получилось 2 скрипта: diskusage.sh и alarm.sh

Листинг diskusage.sh:

#!/bin/bash
dest=/home/user/scripts/temp
df -h /dev/sda1 > $dest/df1.txt
cat $dest/df1.txt | awk '{print($5)}' > $dest/df2.txt
str=Использовано%
while read LINE; do
echo "${LINE##*$str}"
done < $dest/df2.txt | tee $dest/df3.txt
rev $dest/df3.txt | cut -c 2- | rev > $dest/df4.txt
sed -n 2p $dest/df4.txt > $dest/df5.txt
a=`cat $dest/df5.txt`
b=89
if [ $a -le $b ]
then
:
else
/home/user/scripts/alarm.sh
fi
#   Если необходимо удалить все полученные df*.txt
#,  то убрать символ решётки (#) в строке ниже
#find $dest -name df*.txt -delete
exit

Листинг alarm.sh:

#!/bin/bash
level=/home/user/scripts/temp
rev $level/df5.txt | cut -c 1 | rev > $level/df6.txt
value=`cat $dest/df6.txt`
if [ $value -eq 0 ];
then
zenity --width=190 --height=50 --info --text "\nОсталось свободно 10 % диска." 2>/dev/null
else
:
fi
if [ $value -eq 1 ];
then
zenity --width=190 --height=50 --info --text "\nОсталось свободно 9 % диска." 2>/dev/null
else
:
fi
if [ $value -eq 2 ];
then
zenity --width=190 --height=50 --info --text "\nОсталось свободно 8 % диска." 2>/dev/null
else
:
fi
if [ $value -eq 3 ];
then
zenity --width=190 --height=50 --info --text "\nОсталось свободно 7 % диска." 2>/dev/null
else
:
fi
if [ $value -eq 4 ];
then
zenity --width=190 --height=50 --info --text "\nОсталось свободно 6 % диска." 2>/dev/null
else
:
fi
if [ $value -eq 5 ];
then
zenity --width=190 --height=50 --info --text "\nОсталось свободно 5 % диска." 2>/dev/null
else
:
fi
if [ $value -eq 6 ];
then
zenity --width=190 --height=50 --info --text "\nОсталось свободно 4 % диска." 2>/dev/null
else
:
fi
if [ $value -eq 7 ];
then
zenity --width=190 --height=50 --info --text "\nОсталось свободно 3 % диска." 2>/dev/null
else
:
fi
if [ $value -eq 8 ];
then
zenity --width=190 --height=50 --info --text "\nОсталось свободно 2 % диска." 2>/dev/null
else
:
fi
if [ $value -eq 9 ];
then
zenity --width=190 --height=50 --info --text "\nОсталось свободно 1 % диска." 2>/dev/null
else
:
fi
exit

То есть, проверочным запускаемым скриптом является diskusage.sh  Если значение a (полученное из файла df5.txt) составит 90 и более, то запускается скрипт alarm.sh

Для приведенных в примере данных файлов df*.txt результат работы скриптов приводится ниже:


понедельник, 15 апреля 2019 г.

Отображение в Linux встроенных в файлы exe и dll иконок

Интересный результат получился после установки пакета exe-thumbnailer, который был  установлен больше "из любопытства", чем необходимости.


Теперь применяемые мной некоторые файлы Windows отображаются не значком приложений wine, а так как и в Windows:

  

    

    

вторник, 9 апреля 2019 г.

Очистка объектов Корзины старше N дней через bash


Данная публикация является продолжением более ранней, опубликованной под наименованием "Об автоматизации очистки Корзины в Linux или "страсти" по очистке Корзины через терминал" (https://onformix.blogspot.com/2018/01/trashclean.html).

На тот момент не хватило знаний для реализации последнего пункта "А если необходимо удалить содержимое Корзины только старше N дней?" через скрипт на bash. Но эта нерешённая задача постоянно стимулировала к поиску вариантов её реализации, несмотря на то, что были найдены такие инструменты как autotrash и trash-cli.

Этот процесс напоминал абзац из произведения Пола Андерсона "Патруль времени":

Дорогой мой, вы же ещё ни с чем не управились! Для вас и для меня, с точки зрения нашего индивидуального биологического времени, эту  работу ещё предстоит сделать. И не думайте, пожалуйста, что успех предрешён,  раз он зафиксирован в истории. Время эластично, а  человек обладает свободой воли. Если вы потерпите неудачу, история изменится. Упоминание о вашем успехе пропадёт из её анналов, а моего рассказа об этом успехе не будет. Именно так это и происходило в тех считанных эпизодах, когда Патруль терпел поражение. Работа по этим делам всё ещё ведется, и если там достигнут наконец успеха, то история изменится и окажется, что  успех  был как бы "всегда". Tempus non nascitur, fit [время не рождается само собой, а делается (лат.)], если можно так выразиться.

Решение этой задачи оказалось частным случаем реализации другой – удаления файлов/папок через N дней через скрипт bash.

Можно даже сказать, что они почти идентичны за исключением того, что файл отчёта с перечнем окончательно удалённых объектов из Корзины приводится в более "читабельный" вид, так как перед именем каждого из удалённых объектов указывается ещё и путь /home/user/.local/share/Trash/files/, что делает его не совсем удобным для восприятия.

В процессе очистки от объектов старше N дней используются понятия о структуре Корзины и методике поиска удаления через связку команд find и -delete, которые описаны здесь и здесь.

В процессе генерации отчёта об удалённых объектах, который имеет в скрипте наименование Trash.txt к имени указанного файла добавляются дата удаления (месяц.день) и наименование компьютера H-3. А старые файлы отчёта старше установленного времени очистки Корзины (N) удаляются через N+2 дней.

В приводимом примере N устанавливается равным 3.

Результатом является следующее:

#!/bin/bash
D1=/home/user/.local/share/Trash/files
D2=/home/user/.local/share/Trash/info
D3=/home/user/Reports
file=`find $D1 -type f | wc -l`
if [ $file -eq 0 ];
then
:
else
date=`date '+%m.%d'`
find $D1 -mtime +3 > $D3/Trash-ini.txt
find $D1 -mtime +3 -delete
find $D2 -mtime +3 -delete
fi
str=/home/user/.local/share/Trash/files/
while read LINE; do
echo "${LINE##*$str}"
done < $D3/Trash-ini.txt | tee $D3/$date.H-3.Trash.txt
rm $D3/Trash-ini.txt
dir=`find $D1 -type d -empty | wc -l`
if [ $dir -eq 0 ];
then
:
else
find $D1 ! -name 'files' -empty -delete
fi
file=`find $D3 -name *.H-3.Trash.txt -mtime +5 | wc -l`
if [ $file -eq 0 ];
then
:
else
find $D3 -name *.H-3.Trash.txt -mtime +5 -delete
fi
file=`find $D3 -name *.H-3.Trash.txt -empty | wc -l`
if [ $file -eq 0 ];
then
:
else
find $D3 -name *.H-3.Trash.txt -empty -delete
fi
exit