Ответы пользователя по тегу bash
  • Как добавить имя текстового файла в сам файл первой строчкой?

    3vi1_0n3
    @3vi1_0n3
    Попробуйте так:
    #!/bin/bash
    
    for filename in "file1" "file2" "file3"
    do
      sed -i $filename -e "1i $filename"
    done


    Это добавит имя файла в сам файл первой строчкой. В файлы нулевого размера не добавит, если это нужно, скажите
    Ответ написан
    Комментировать
  • Как добавить/обновить блок в файле bash скриптом?

    3vi1_0n3
    @3vi1_0n3
    Если вам надо писать на баше, то надо делать ровно то, что надо, и не более. Баш достаточно буквальный язык.

    Первая часть:
    #!/bin/bash
    
    # Создаем шаблон конфига из скрипта (без использования массива)
    cat > ./syslog-template <<"EOF"
    /var/log/syslog
    {
      rotate 10
      size 10M
      daily
      missingok
      copytruncate
      notifempty
      compress
      delaycompress
      sharedscripts
      postrotate
        invoke-rc.d syslog-ng reload > /dev/null
      endscript
    }
    EOF
    
    # Удаляем секцию из файла
    sudo sed -i '/\/var\/log\/syslog/,/}/d' /etc/logrotate.d/syslog-ng
    # Добавляем секцию в файл
    # Тут, возможно, надо будет " | sudo tee -a" вместо пайпа
    cat ./syslog-template >> /etc/logrotate.d/syslog-ng
    # ^-- Вот эту команду можно совместить с первой, чтобы не создавать лишний файл


    Вторая часть (не уверен, зачем она вообще нужна (так как изменения, внесенные первой частью, идемпотентны), но, раз вопрос так стоит, то пусть будет):
    #!/bin/bash
    
    # На момент вызова файл шаблона должен существовать
    ORIGINAL=$(cat ./syslog-template) 
    EXISTING=$(sed -rne '/\/var\/log\/syslog/,/}/p' /etc/logrotate.d/syslog-ng)
    [[ "$ORIGINAL" == "$EXISTING" ]] && echo "файл настроен" || echo "файл не настроен"
    Ответ написан
  • Как в bash создать динамический select?

    3vi1_0n3
    @3vi1_0n3
    Исходя из описания задачи вот так:
    #!/bin/bash
    
    select site in $(jq -r '.sites[].title' sites.json) 
    do
        select area in "admin" "front"
        do
            # get links for "area"
            links=$(jq '.sites[] | select(.title == "'${site}'") | .'${area}' |.[]' sites.json)
            # here you can do whatever you want - iterate through links, opening them with xdg-open, or just print them out
            echo $links
            break
        done
        break;
    done

    Если это не то, что надо, уточните описание.

    Если надо динамически брать набор ключей с исключением "title", то
    select area in "admin" "front"
    заменить на
    select area in $(jq -r '.sites[] | select(.title == "'${site}'") | keys | .[] | select(. != "title")' sites.json)
    Ответ написан
    1 комментарий
  • Как написать bash мониторинг файловой системы EXT4?

    3vi1_0n3
    @3vi1_0n3
    Тут уже много интересного написали. Штука только в том, что в вопросе недостаточно информации, что не позволяет эту задачу решить эффективно. Есть вопросы, на которые надо ответить, прежде чем приступать:

    1. Где хранятся эти файлы, которые могут потенциально вырасти до таких размеров и сколько их?
    Если они хранятся в одной директории, тогда смысла делать поиск по всей системе нет. На моем десктопе с 500К файлов поиск занимает примерно 9.5 секунд с полным сканированием дерева файловой системы. Если файлов потенциально пара сотен, и они все в одной директории или в поддереве директорий, время поиска можно сильно сократить. Я подозреваю, что find читает информацию о размере из inode'ов, поэтому количество файлов играет значительную роль.

    2. Что это за файлы, надо ли их на самом деле мониторить?
    Если это логи, к примеру, есть много способов этого не делать (ротация логов, централизованное хранение), если это образы дисков для виртуальных машин (LVM, или еще что), то find может быть не совсем верное решение, могут быть другие инструменты (получение данных из гипервизора, и так далее)

    3. Можно ли сделать линки на директории, в которых хранятся файлы, в одну директорию и сканировать её вместо всей файловой системы?
    Логично, что, если файлы хранятся где попало и их надо искать по всей системе, поиск будет занимать больше времени.

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

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

    Учитывая, что вы хотите это сделать через bash, я бы сначала посчитал, сколько подобных событий (увеличение размера файлов до 16+Тб) происходит и попробовал оптимизировать поиск таких файлов за счет отсекания ненужного поиска (специфическая директория, фильтрация по типу файлов, имени и прочее).

    Если файлы имеют какую-то одинаковую часть имени, можно использовать locate (вместо find) для получения списка файлов, и потом передавайте его в скрипт, который тупо проверит размер каждого (через stat, например). В этом случае надо при появлении новых файлов не забывать вызвать updatedb, если это не происходит автоматически по какой-то причине.

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

    P.S. updatedb делает то же самое примерно, что и find, поэтому не надо его вызывать каждый раз перед использованием locate
    Ответ написан
    Комментировать
  • Как правильно подменить (скопировать) скрипта в самом скрипте?

    3vi1_0n3
    @3vi1_0n3
    Заменять скрипт в /etc/init.d из самого себя не очень правильная идея.
    Да, заменить или модифицировать скрипт из самого себя можно, но, по идее, скрипты для запуска сервисов/демонов не должны модифицироваться напрямую, желательно только с новой версией пакета (если он есть).
    Даже я бы так не стал делать, хотя я тот еще нетрадиционный баш-скриптер.

    Если вам прям очень надо именно заменять сам скрипт по условию, то лучше это делать так:
    1. Вызываем /etc/init.d/medteco.sh
    2. Скрипт /etc/init.d/medteco.sh проверяет наличие скрипта на флэшке
    3. Скрипт /etc/init.d/medteco.sh проверяет целостность скрипта на флэшке
    4. Если скрипт существует и проверка целостности проходит, копируем на диск под другим именем, проверяем целостность еще раз. Контрольную сумму можно заранее посчитать тем же md5 и положить в файл рядом с новым скриптом.
    5. Если шаг 4 успешен, удаляем/переименовываем старый скрипт (тот, который не /etc/init.d/medteco.sh, а старая версия скрипта с флэшки), переименовываем новый скрипт в старый.
    6. Безусловно выполняем/source'им тот самый скрипт, передавая ему параметры при необходимости. В этом случае у вас будет вызвана либо старая рабочая версия, либо новая рабочая версия.

    Скрипт /etc/init.d/medteco.sh затронут не будет (и не должен), он будет нужен для обработки ошибок и т.д., чтобы контролировать процесс независимо от его результата. Если прямо очень надо, чтобы старая версия не запускалась, бросаем ошибку и прерываем выполнение скрипта.

    Альтернативный вариант: собираем пакет, который будет устанавливаться в систему, устанавливаем его прямо с флэшки. Пакетный менеджер прервет установку, если пакет поврежден и не должен заменить рабочую версию в теории.
    Ответ написан
    Комментировать
  • Сравнить файлы и удалить совпадения по первым 7 символам?

    3vi1_0n3
    @3vi1_0n3
    На баше как-то так (минимум внешних команд, от cat можно избавиться, если использовать exec):
    #!/bin/bash
    
    octets=({0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1})
    
    addrtobin(){
        for digit in ${1//./ }; do echo -n ${octets[digit]}; done
    }
    
    # Просто пример для списков адресов и сетей.
    # Лучше используйте exec N<>filename.txt + read -u N
    ips=$(cat ips.txt)
    nets=$(cat nets.txt)
    
    for ip in $ips
    do
        out=1
        bin_ip=$(addrtobin $ip)
        for net in $nets
        do
            bin_net=$(addrtobin ${net%%/*})
            net_mask=${net#*/}
            if [ "${bin_ip:1:$net_mask}" == "${bin_net:1:$net_mask}" ]
            then
                out=0
                break # Чтобы не проходить до конца списка сетей, если адрес уже найден
            fi
        done
        [ $out -eq 1 ] && echo $ip
    done


    К сожалению, придется делать двойной цикл. К счастью, можно не прогонять внутренний цикл до конца, если совпадение найдено, поэтому будет самую малость быстрее.
    Ответ написан
    Комментировать
  • Почему не отрабатывает exit на третьем уровне скрипта?

    3vi1_0n3
    @3vi1_0n3
    Я могу предположить, что такая структура скриптов призвана обеспечить определенную независимость каждого подменю. Если брать ровно те скрипты, которые у вас есть, то:
    1. Добавляем код возрата в функцию PMI - просто добавляем строчку "return $g" после "esac". Это не сломает существующий скрипт. Но! Придется разбираться с ошибками выполнения вызовов IPMI меню отдельно.
    2. Проверяем код возврата в unem
    read b
            case $b in
              1) clear ; ./1_1_system_menu.sh && exit; unem ;;
              2) clear; ./1_1_ipmi_menu.sh && exit; unem ;;
              3) clear ; menu ;;
              0) exit 0 ;;
            esac

    " && exit" здесь выполнится, если скрипт возвращает 0, что и произойдет, если PMI() вернет 0. Соответственно, при выборе любого другого варианта выхода не будет.

    Однако, несмотря на то, что с такими минимальными изменениями оно может работать как надо, все-таки имеет смысл сделать так, как jcmvbkbc уже упомянул - не исполнять скрипты, а включать их. Я не согласен по поводу места в скрипте, куда их включать, я бы поставил source до определения функции unem.
    И я лично сделал бы это несколько по-другому. Как-то так:

    menu.sh
    #!/bin/bash
    
    # Insert necessary sub-menus
    . ./ipmi_menu.sh
    
    declare -A MAINITEMS
    MAINITEMS['System settings']="echo 'System settings selected'"
    MAINITEMS['Ipmi tools']="PMI"
    MAINITEMS['Back to previous']="break"
    MAINITEMS['Exit']="exit"
    
    while :
    do
      select menuitem in "${!MAINITEMS[@]}"
      do
        echo "Menu item: $menuitem"
        ${MAINITEMS[$menuitem]}
      done
    done

    ipmi_menu.sh
    #!/bin/bash
    
    PMI(){
    echo "PMI functions"
    
    PMI_TITLES=(\
            "Sensors check" \
            "Writing Motherboard FRU information"\
            "Writing MAC address  BMC"\
            "Wtiting MAC address Motherboard"\
            "Back to previous"\
            "Exit")
    PMIITEMS[1]="echo Sensors check"
    PMIITEMS[2]="echo fru information"
    PMIITEMS[3]="echo mac address"
    PMIITEMS[4]="echo mac address motherboard"
    PMIITEMS[5]="break"
    PMIITEMS[6]="exit"
    
    select pmiitem in "${PMI_TITLES[@]}"
    do
      ${PMIITEMS[$REPLY]}
    done
    }


    В menu.sh я использую associative array, это сильно упрощает добавление элементов в главное меню.

    В ipmi_menu.sh я использую обычный массив, чтобы соблюсти порядок пунктов меню. Тут PMIITEMS[XXX] будет просто имя функции, содержащейся в файле ipmi_menu.sh

    В этом примитивном варианте экран не очищается и перерисовка пунктов текущего меню происходит по нажатию ENTER, зато это должно (по идее) работать в любом, даже самом тупом терминале. Если надо красоты, можно select поменять на echo+read, смысл от этого не поменяется, все равно можно брать список функций из массива.
    Ответ написан
    Комментировать
  • Как сделать так, чтобы программа закрывалась после третьего неудачного ввода?

    3vi1_0n3
    @3vi1_0n3
    #!/bin/bash
    
    FAILS=0
    Func(){
    read -p "Введите имя: "
    case "$REPLY" in
      Саша|Алекс|Alex)
        echo "Полное имя: Александр"
        return 1
        ;;
      *)
        FAILS=$(($FAILS + 1))
        return 0
        ;;
    esac
    }
    
    while [ $FAILS -lt 3 ] && Func
    do
        echo "Неизвестное имя системе, просим прощения"
    done
    Ответ написан
    Комментировать
  • Как разархивировать архив tar.gz, в котором архив и т.д?

    3vi1_0n3
    @3vi1_0n3
    Если не надо сохранять все промежуточные файлы, то может так?
    while [ -f *.tar.gz ]; do filename="$(ls -1 *.tar.gz | head -n 1)"; tar -xvf $filename && rm $filename; done;
    Ответ написан
    Комментировать
  • Как правильно установить пакет через скрипт bash?

    3vi1_0n3
    @3vi1_0n3
    У вас в таком варианте никогда не отработает "apt -f install". Штука в том, что после "dpkg -i" код возврата не ноль. В данном случае лучше попробовать без условного выполнения, либо заменить "&&" на "||". Тогда "apt -f install" будет выполняться после безуспешной установки пакета с помощью dpkg (что в вашем случае и происходит).
    Должно быть что-то типа такого:
    install_timeshift(){
    wget "https://.../...deb" && [ -f "...deb" ] && { dpkg -i ....deb ||  apt -f install; }
    }

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

    Пример как это работает:
    $ touch file && [ -f file ] && { test -d whatever || echo 'apt -f install called'; }
    apt -f install called
    $ rm file
    $ touch file1 && [ -f file ] && { test -d whatever || echo 'apt -f install called'; }
    $ touch file1 && [ -f file ] && { test -d whatever || echo 'apt -f install called'; } || echo 'no file downloaded'
    no file downloaded
    $
    Ответ написан
    1 комментарий
  • Как записывать в файл до тех пор пока есть место в нем?

    3vi1_0n3
    @3vi1_0n3
    Учитывая, что изначальный размер определяется размером созданного файла (который в теории может быть другим), я бы начал с получения размера и записыванием ровно такого же объема.
    #!/bin/bash
    FILE="test.txt"
    SIZE=$(stat -c %s $FILE)
    { while [ $(stat -c %s $FILE) -lt $SIZE ]
    do
            cat /dev/urandom | tr -dc 'A-Za-z0-9' | head -c 76
    done } | head -c $SIZE > $FILE
    Ответ написан
    Комментировать
  • Автоматическое выполнение скрипта для инициализации, работающая только в определенной папке?

    3vi1_0n3
    @3vi1_0n3
    Не сильно свежий вопрос, но мало ли, вдруг актуально еще.
    В .bashrc можно задать переменную PROMPT_COMMAND
    PROMPT_COMMAND='[ "$(pwd)" = "/whatever/lineageos-19.1" ] && . build/envsetup.sh'

    Должно сработать прямо сразу, как только выполните "cd lineageos-19.1"
    Ответ написан
    Комментировать
  • Как локализовать скрипт на BASH?

    3vi1_0n3
    @3vi1_0n3
    #!/bin/bash
    
    declare -A LANGUAGES
    
    BASE="./langs"
    for path in $(find $BASE -name '*.lng')
    do
            base=$(basename $path)
            language=${base%.*}
            line=$(grep "language" $path)
            name=${line#*=}
            LANGUAGES[$name]=$language
    done
    
    echo ${!LANGUAGES[@]}
    echo ${LANGUAGES[@]}
    
    select l in ${!LANGUAGES[@]}
    do
            echo "Selected: $l"
            echo "Selected file: " $BASE/${LANGUAGES[$l]}.lng
            break
    done
    Ответ написан
    1 комментарий
  • Как найти и переименовать все файлы, удалив подстроку (-git)?

    3vi1_0n3
    @3vi1_0n3
    #!/bin/bash
    
    find . -name "*-git*.txt" | (
    IFS='
    '
    while read oldname
    do
    newname=${oldname//-git}
    mv "$oldname" "$newname"
    done
    )
    Ответ написан
    3 комментария
  • Как удалить дубликаты внутри строки?

    3vi1_0n3
    @3vi1_0n3
    #!/bin/bash
    IFS='
    '
    exec 3<>data.txt
    while read -u 3 line
    do
    (
            IFS=' '
            result=()
            for word in $line
            do
                    [[ ! "${result[*]}" =~ "$word" ]] && result=(${result[*]} $word)
            done
            echo ${result[*]}
    )
    done
    Ответ написан
    1 комментарий
  • Какую комманду нужно ввести для фильтрации в Linux?

    3vi1_0n3
    @3vi1_0n3
    join --nocheck-order -t : ./B ./A
    Ответ написан
    Комментировать
  • Bash проверка открытого порта. Как проверить результат работы команды?

    3vi1_0n3
    @3vi1_0n3
    Bash скрипт без nc, с использованием того же $?
    echo "" > /dev/tcp/192.168.1.10/443 && echo "Opened" || echo "Closed"
    Ответ написан
    Комментировать
  • Как реализовать в скрипте таймаут для запросов BigQuery?

    3vi1_0n3
    @3vi1_0n3
    В coreutils есть команда timeout, скорее всего она уже есть на сервере.
    Ответ написан
    1 комментарий
  • Правильно ли перевел с C++ на BASH?

    3vi1_0n3
    @3vi1_0n3
    Можно как-то так, чтобы избежать многочисленных if
    #!/bin/bash
    res=0
    read h1 m1 h2 m2
    while :
    do
      if [ $m1 -eq 0 ]; then
        if [ $h1 -gt 12 ]; then
            dif=$(($h1-12))
        else
            [ $h1 -eq 0 ] && dif=12 || dif=$h1
        fi
        res=$(($res+$dif))
      else
        [ $m1 -eq 30 ] && ((res++))
      fi
     
      [ $h1 -eq $h2 ] && [ $m1 -eq $m2 ] && break
      ((m1++))
      [ $m1 -eq 60 ] && m1=0 && ((h1++))
    done
    echo $res

    Хотя врать не буду, не понял, какую задачу вы решаете на плюсах. Может даже проще можно.
    Ответ написан
    Комментировать
  • Как добавить запятые к выводу?

    3vi1_0n3
    @3vi1_0n3
    G=$(groups $USER);G=${G#*: };echo ${G// /,}
    Ответ написан
    Комментировать