Вопрос или проблема
Я выполняю второе задание по BASH из Руководства по написанию сценариев на Bash TLDP, и мне удалось разобраться с большинством задач, пока не дошло до момента, когда нужно копировать сжатые файлы на вставленную флешку USB.
Список домашнего каталога
Выполните рекурсивный список каталогов в домашнем каталоге пользователя и сохраните информацию в файл. Сожмите файл, заставьте скрипт попросить пользователя вставить флешку USB, затем нажать ENTER. Наконец, сохраните файл на флешке после того, как убедитесь, что флешка правильно смонтирована, проанализировав вывод команды df. Обратите внимание, что флешку нужно размонтировать перед тем, как ее удалить.
По мере того как я продвигаюсь в написании скрипта, он становится все менее … элегантным, и мне интересно, есть ли более лучший способ сделать это. Я понимаю, что создание файлов, вероятно, не самый эффективный способ провести сравнения, но пока не разобрался с расширениями оболочки, и планирую изменить их, как только добьюсь работоспособности.
Проблема конкретно в том, чтобы убедиться, что USB смонтирован и что я записываю на флешку USB, а не куда-то еще. Я сравниваю последнюю строку df
после подключения USB с последней строкой df
из diff
между df
до подключения USB и df
после его подключения и перемещаюсь в цикле, пока они не совпадут. К сожалению, результат diff
начинается с >, но я намерен использовать sed, чтобы от него избавиться. Реальная проблема в том, что путь к тому месту, где смонтирована моя флешка USB, следующий:
/media/flerb/”Название USB с пробелами”
Чтобы сделать это портативным для USB, которые могут иметь разные имена, мне, возможно, стоит сделать что-то с awk и разделителями полей? И как продолжение, я знаю, что это довольно неэлегантно, и меня интересует, есть ли более чистый способ решить эту задачу … особенно потому, что это второе задание и все еще на УРОВНЕ ЛЕГКО.
Вывод из df в конце:
/dev/sdb1 15611904 8120352 7491552 53% /media/flerb/CENTOS 7 X8
> /dev/sdb1 15611904 8120352 7491552 53% /media/flerb/CENTOS 7 X8
Скрипт на данный момент
1 #!/bin/bash
2
3 if [ "$UID" -eq 0 ] ; then
4 echo "Не запускайте это от имени root"
5 exit 1
6 fi
7
8 #Создайте резервную копию с заголовком с датой в резервной директории
9 BACKUP_DIR="$HOME/backup"
10 DATE_OF_COPY=$(date --rfc-3339=date)
11 BACKUP_FILE="$BACKUP_DIR/$DATE_OF_COPY"
12
13 [ -d "$BACKUP_DIR" ] || mkdir -m 700 "$BACKUP_DIR"
14
15 #найдите все файлы рекурсивно в домашнем каталоге $HOME
16 find -P $HOME >> "$BACKUP_FILE"
17
18 #используйте lzma для сжатия
19 xz -zk --format=auto --check=sha256 --threads=0 "$BACKUP_FILE"
20
21 #создание файлов для операций
22 BEFORE="$BACKUP_DIR"/before_usb.txt
23 AFTER="$BACKUP_DIR"/after_usb.txt
24 DIFFERENCE="$BACKUP_DIR"/difference.txt
25
26 df > "$BEFORE"
27 read -p 'Вставьте USB и нажмите любую кнопку' ok
28 sleep 2
29 df > "$AFTER"
30 diff "$BEFORE" "$AFTER" > "$DIFFERENCE"
31 sleep 2
32 echo
33
34 TAIL_AFTER=$(tail -n 1 "$AFTER")
35 TAIL_DIFF=$(tail -n 1 "$DIFFERENCE")
36
37 until [ "$TAIL_AFTER" == "$TAIL_DIFF" ] ;
38 do
39 echo "Еще не время"
40 df > "$AFTER"
41 TAIL_AFTER=$(tail -n 1 "$AFTER")
42 diff "$BEFORE" "$AFTER" > "$DIFFERENCE"
43 TAIL_DIFF=$(tail -n 1 "$DIFFERENCE")
44 echo "$TAIL_AFTER"
45 echo "$TAIL_DIFF"
46 sleep 1
47
48 done
49 exit $?
Чтобы получить правильный USB и его путь:
-
заметил, что первый USB, подключенный к моей системе, смонтирован на файловой системе /dev/sdb1, а второй – на файловой системе /dev/sdc1. Я добавил раздел, чтобы переключаться на /dev/sdc1, если
udevadm info -q all -n "/dev/sdb" | grep "ID_BUS=usb"
возвращает true. -
если
diff
междуdf --output=target | grep "/media/darren"
до и после содержит “/media/darren”, то это, предположительно, новый USB. USB смонтирован. -
if udevadm info -q all -n "/dev/sdb" | grep "ID_BUS=usb"
истинно, то это устройство, смонтированное на “/dev/sdb”, является usb устройством. -
чтобы получить путь и убедиться, что я захватываю нужный:
df --output=source,avail,target | grep "$FILESYSTEM" | grep "$MOUNTPOINT"
и некоторые очень красивые awkudevadm info -q all -n "$FILESYSTEM" | grep "ID_BUS=usb" > $RESULT USB_ADDR="$BACKUP_DIR"/usb_addr.txt if grep "ID_BUS=usb" "$RESULT" ; then df --output=source,avail,target | grep "$FILESYSTEM" | grep "$MOUNTPOINT" > "$USB_ADDR" awk -i inplace '{$0=gensub(/\s*\S+/,"",1)}1' "$USB_ADDR" awk -i inplace '{$0=gensub(/\s*\S+/,"",1)}1' "$USB_ADDR" sed -i 's/^ *//' "$USB_ADDR"
К сожалению, процесс diff не срабатывает, если добавляются или удаляются другие элементы. Но, по крайней мере, он завершает работу корректно, если захватывает неправильное устройство при использовании отфильтрованного вывода df
и grep для файловой системы и точки монтирования.
Пробовал awk 'NR!=1{print $NF}' <(df)|sort
. Все еще не очень силен в расширениях bash.
#!/bin/bash
if [ "$UID" -eq 0 ] ; then
echo "Не запускайте это от имени root"
exit 50
fi
#Создайте резервную копию с заголовком с датой в резервной директории
BACKUP_DIR="$HOME/backup"
DATE_OF_COPY=$(date --rfc-3339=date)
BACKUP_FILE="$BACKUP_DIR/$DATE_OF_COPY"
[ -d "$BACKUP_DIR" ] || mkdir -m 700 "$BACKUP_DIR"
#найдите все файлы рекурсивно в домашнем каталоге $HOME
find -P $HOME >> "$BACKUP_FILE"
#используйте lzma для сжатия
xz -zk --format=auto --check=sha256 --threads=0 "$BACKUP_FILE"
#создание файлов для операций по информации до и после вставки usb и разности между ними
#это часть того, как мы будем удостоверяться, что мы записываем на правильное устройство
BEFORE="$BACKUP_DIR"/before_usb.txt
AFTER="$BACKUP_DIR"/after_usb.txt
DIFFERENCE="$BACKUP_DIR"/difference.txt
#MOUNTPOINT=где компьютер по умолчанию монтирует USB
MOUNTPOINT="/media/darren"
FILESYSTEM="/dev/sdb"
RESULT="$BACKUP_DIR"/result.txt
#если на точке монтирования что-то есть до подключения флешки, мы хотим это знать
#чтобы записать на новое устройство
udevadm info -q all -n "$FILESYSTEM" | grep "ID_BUS=usb" > $RESULT
if grep "ID_BUS=usb" "$RESULT" ; then
FILESYSTEM="/dev/sdc"
echo $FILESYSTEM
fi
df --output=target | grep $MOUNTPOINT > "$BEFORE"
read -p 'Вставьте USB или жесткий диск и нажмите любую кнопку' ok
df --output=target | grep $MOUNTPOINT > "$AFTER"
#diff для поиска нового
diff "$BEFORE" "$AFTER" > "$DIFFERENCE"
# Ищем на точке монтирования в разности между выводами df до и после
SEARCH_FOR_MOUNT=$(grep "$MOUNTPOINT" "$DIFFERENCE")
until [ "$SEARCH_FOR_MOUNT" ] ;
do
df --output=target | grep $MOUNTPOINT > "$AFTER"
diff "$BEFORE" "$AFTER" > "$DIFFERENCE"
SEARCH_FOR_MOUNT=$(grep "$MOUNTPOINT" "$DIFFERENCE")
done
#проверяем, подключено ли новое устройство в файловую систему является ли это USB
udevadm info -q all -n "$FILESYSTEM" | grep "ID_BUS=usb" > $RESULT
USB_ADDR="$BACKUP_DIR"/usb_addr.txt
if grep "ID_BUS=usb" "$RESULT" ; then
df --output=source,avail,target | grep "$FILESYSTEM" | grep "$MOUNTPOINT" > "$USB_ADDR"
awk -i inplace '{$0=gensub(/\s*\S+/,"",1)}1' "$USB_ADDR"
awk -i inplace '{$0=gensub(/\s*\S+/,"",1)}1' "$USB_ADDR"
sed -i 's/^ *//' "$USB_ADDR"
if [ ! -s $USB_ADDR ] ; then
echo "Ошибка при поиске адреса USB"
exit 100
ADDR=$(cat "$USB_ADDR")
cp "$BACKUP_FILE.xz" "$ADDR"
rm "$BACKUP_FILE" "$USB_ADDR" "$RESULT" "$BEFORE" "$AFTER" "$DIFFERENCE"
else
echo "Устройство не является USB"
exit 52
fi
exit $?
Ответ или решение
Автоматическое определение и запись на USB с переменными пробелами в его названии
Введение
В этой статье мы рассмотрим, как написать bash-скрипт, который позволяет автоматически определять USB-накопитель с учетом переменных пробелов в его названии, а также сохранять на него сжатый файл с информацией о пользователе. Учитывая важные моменты, такие как монтирование устройства, мы обеспечим надежное выполнение сценария без рисков записи на другие накопители.
Шаг 1: Понимание структуры скрипта
Скрипт начинается с проверки прав пользователя, создания резервной копии домашней директории и сжатия полученного файла. Важно, чтобы скрипт корректно идентифицировал вставленный USB-накопитель и записывал на него данные.
Шаг 2: Определение USB-накопителя
Для определения USB-накопителя нам нужно учитывать, что его имя может содержать пробелы. Мы можем использовать команду df
для получения информации о подключенных устройствах. Тем не менее, вместо того чтобы сравнивать полные строки, эффективнее будет использовать awk
для извлечения нужных полей.
Как только USB будет вставлен, вы можете использовать следующую конструкцию для получения названия файловой системы:
NEW_USB=$(df -h --output=target | awk '/\/media\/.*[a-zA-Z0-9]/ {print; exit}')
Эта команда будет извлекать первую строку, соответствующую подключенному USB-устройству.
Шаг 3: Параметры монтирования
Скрипт должен проверять, действительно ли устройство USB смонтировано. После нажатия клавиши Enter для подтверждения вставки накопителя мы сравниваем текущее состояние монтирования с предыдущим.
Шаг 4: Запись на USB-накопитель
Используя переменную NEW_USB
, мы можем безопасно создать резервную копию. Если название устройства содержит пробелы, его необходимо обрамить в кавычки, чтобы избежать проблем с синтаксисом в bash:
cp "$BACKUP_FILE.xz" "$NEW_USB"
Полный пример скрипта
Вот полная версия скрипта, учитывающая все описанные выше аспекты:
#!/bin/bash
# Проверьте, что вы не запускаете скрипт от имени root
if [ "$UID" -eq 0 ]; then
echo "Не запускайте этот скрипт с правами root"
exit 1
fi
# Создание резервного файла
BACKUP_DIR="$HOME/backup"
DATE_OF_COPY=$(date +%F)
BACKUP_FILE="$BACKUP_DIR/$DATE_OF_COPY"
mkdir -p -m 700 "$BACKUP_DIR"
find -P $HOME > "$BACKUP_FILE"
xz -zk --format=auto "$BACKUP_FILE"
# Сохранение состояния монтирования до вставки USB
BEFORE_MOUNT=$(df -h --output=target | awk '/\/media\/.*[a-zA-Z0-9]/')
read -p 'Вставьте USB и нажмите Enter...' ok
# Сохранение состояния монтирования после вставки USB
AFTER_MOUNT=$(df -h --output=target | awk '/\/media\/.*[a-zA-Z0-9]/')
while [ "$BEFORE_MOUNT" == "$AFTER_MOUNT" ]; do
sleep 1
AFTER_MOUNT=$(df -h --output=target | awk '/\/media\/.*[a-zA-Z0-9]/')
done
# Получение названия нового USB
NEW_USB=$(df -h --output=target | awk '/\/media\/.*[a-zA-Z0-9]/ {print; exit}')
# Копирование сжатого файла на USB
if [ -n "$NEW_USB" ]; then
cp "$BACKUP_FILE.xz" "$NEW_USB"
echo "Резервная копия успешно создана на $(basename "$NEW_USB")"
else
echo "Не удалось определить USB-накопитель."
exit 1
fi
exit 0
Заключение
Создание сценария для автоматического детектирования USB-накопителей с учетом пробелов в названиях является полезной задачей для автоматизации процессов резервного копирования. Этот скрипт поддерживает основной функционал и может быть расширен для более сложных операций, в зависимости от ваших потребностей.