Страницы

Поиск по вопросам

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

суббота, 11 апреля 2020 г.

Не работают некоторые сочетания клавиш в консоли FreeBSD

#консоль #shell #freebsd

                    
Я зашёл по ssh на сервер, на котором установлена ОС FreeBSD (например, на хостинге
nic.ru установлена FreeBSD 8.1), и при работе в консоли обнаружил, что некоторые привычные
сочетания клавиш (например, ctrl+стрелка вправо/влево для "перепрыгивания" между словами)
не работают. Вместо ожидаемого действия я получаю в консоль мусорный вывод такого вида:

;5D;5D;5C;5C~~


Как заставить работать сочетания клавиш, которые не работают в консоли FreeBSD "из
коробки"?
    


Ответы

Ответ 1



Дело в ненастроенном readline(3). "Мусор", который видно в терминале - это коды нажатия клавиш и их сочетаний, которым не присвоены никакие функции (чтобы увидеть коды для сочетаний клавиш, которым функции присвоены, можно нажать Ctrl+V и потом интересующее сочетание клавиш (или единичную клавишу) - для кнопки Enter, например, код будет ^M - то есть вместо Enter в консоли можно нажимать Ctrl+M с тем же эффектом, сюрприз!). Уже назначенные на разные клавиши и их сочетания функции можно посмотреть командой bind -lpvs. Для sh и bash настройки readline можно задать в файле .inputrc в домашней папке пользователя. Для реализации неработающих из коробки в FreeBSD сочетаний клавиш я использую следующие настройки: # ~/.inputrc: readline initialization file. # (you can view all current assignments by runnig `bind -lpvs`) # let Insert, PageUP, PageDown keys work properly in bash under FreeBSD and Linux: "\e[2~": overwrite-mode "\e[5~": beginning-of-history "\e[6~": end-of-history # let Ctrl-left-arrow and Ctrl-right-arrow jump over words: "\e[1;5C": forward-word "\eOC": forward-word "\e[1;5D": backward-word "\eOD": backward-word Стоит отметить, что код сочетания клавиш, который можно прочесть с помощью Ctrl+V, может отличаться для разных видов терминалов - например, не совпадать в "обычном" терминале и внутри screen. Вы можете самостоятельно расширить предлагаемый ~/.inputrc для таких случаев.

Получить адрес корневой директории в git-репозитории

#git #shell

                    
Я пишу shell-скрипт для автоматизации работы с Git. Мне нужен способ получить полный
или относительный адрес корневой директории проекта при выполнении команды в любой
вложенной директории (включая корневую).

Например, структура проекта, лежащего в Users/nickvolynkin/git-example:

- root/
  | - .git/
  | - a/
  | - b/
  | - c/
      | - c1/
      | - c2/


cd c/c1/
echo somecommand

# вариант 1
../../

# вариант 2
Users/nickvolynkin/git-example

    


Ответы

Ответ 1



Абсолютный путь через rev-parse $ echo $(git rev-parse --show-toplevel) Users/nickvolynkin/git-example Относительный путь через rev-parse $ echo $(git rev-parse --show-cdup) ../../ Абсолютный путь через alias [alias] root = "!pwd" в shell-скрипте: $ cd Users/nickvolynkin/git-example/c/c1 $ echo `git root` Users/nickvolynkin/git-example # отличие от просто pwd: $ pwd Users/nickvolynkin/git-example/c/c1 Абсолютный путь через временный alias (без регистрации и смс). $ echo $(git -c alias.root='!pwd' root) Для передачи аргументом в Git Если этот адрес необходимо передать аргументом в какую-то из команд Git, то лучше использовать :/ # из любой папки делает reset на всю рабочую область $ git reset :/ # то же самое, что и $ git reset $(git rev-parse --show-toplevel) Частично основано на ответах из Is there a way to get the git root directory in one command?

четверг, 9 апреля 2020 г.

Очередь команд в консоли Linux

#linux #shell

                    
Предположим, что в данный момент в консоли уже выполняется какая-то команда. Она
занимает продолжительное время. Я хотел бы выполнить одну или несколько команд после
того, как завершится эта. Как это сделать?

Я знаю, что можно Ctrl + Z и bg, но это не совсем то. Нужно не в бэкграунд отправить
команду, а именно дождаться ее завершения.
    


Ответы

Ответ 1



используйте не bg (background), а fg (foreground), после которой добавьте нужную команду (команды): $ date; sleep 10 Fri Oct 2 10:45:04 MSK 2015 ^Z [1]+ Stopped sleep 10 $ fg; date sleep 10 Fri Oct 2 10:45:14 MSK 2015 fg и последующую команду можно связать и условиями, например: && или ||.

вторник, 17 марта 2020 г.

Как склеить строки ?

#perl #shell #sed #linux


Вопрос в заголовке.
Только не все строки. Про tr -d '\n' мне известно.
Пример:
abc
;
fgh
;
"
qwe
rty
"
;
"
asd
"
;
end

В конвейере нужно превратить в:
abc;fgh;"qwe
rty";"asd";end\r

Другими словами, сделать нормальный CSV из вывода, где поля в отдельных строках,
разделитель полей ';' тоже в отдельной строке и поля, которые могут содержать \n, обрамлены
строками '"'. Последний \n надо заменить на \r\n
Для программы (на Си?) я бы  сформулировал задачу просто:
убрать все \n вне "...", а внутри "..." убрать первый сразу за '"'. (на Си писать
не надо, если "однострочник" не получится, то на нем сам как-нибудь напишу).
...
Пока замер на проблеме, что я никак не могу заставить работать что-нибудь типа 
s/\n;\n/;/

ни в sed, ни в perl (т.е. работать с выражением для 2-х строк)
P.S.
python 2.4 тоже подойдет.    


Ответы

Ответ 1



perl -0777 -lpe 'BEGIN{$\="\r\n"}s#\n;\n#;#gsm;s#;"\n#;"#gsm'

воскресенье, 15 марта 2020 г.

Как правильно установить новую команду в командную оболочку (shell и другие)

#bash #shell #zsh


Я написал, нашел в сети или получил по телепатической связи некоторый код для командной
оболочки Unix (shell, zsh, bash, и прочие).

Теперь я хочу ее постоянно использовать. Как установить ее, так чтобы она была доступна
в каждом экземпляре оболочки?


Без подключения вручную на каждом запуске
И даже после перезагрузки ОС


О создании установщика и пакетных менеджерах речь не идет, это вне темы этого вопроса.

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

mv -b $1 $2 && mv $2~ $1


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


Ответы

Ответ 1



в любой posix-совместимой операционной системе используется переменная окружения PATH. поместите файл со скриптом в любой из каталогов, перечисленных в этой переменной (добавив файлу биты выполнимости) — и сможете вызывать этот скрипт по имени. пример содержимого: $ echo $PATH /usr/local/bin:/usr/bin:/bin:/usr/games:/opt/bin пример добавления битов выполнимости: $ chmod +x file.with.script пример копирования в, например, /urs/local/bin (понадобятся права суперпользователя): $ cp file.with.script /usr/local/bin/ если нет прав записи ни в один из каталогов, перечисленных в PATH, можно добавить в неё любой другой каталог, в который у вас есть право записи. например, $HOME/bin. если его (пока) не существует, то можно создать так: $ mkdir $HOME/bin добавить этот каталог в переменную PATH можно так: $ export PATH="$HOME/bin:$PATH" последнюю команду, чтобы не вводить её каждый раз после запуска shell-а, можно добавить в конец «конфигурационного» файла используемого вами shell-а, который находится в вашем домашнем каталоге и, скорее всего, носит имя .shellrc, где вместо слова shell надо подставить название программы, которую вы используете в качестве shell-а. узнать название этой программы можно, например, так: $ ps PID TTY TIME CMD 12450 pts/0 00:00:00 bash 12576 pts/0 00:00:00 ps bash — этой и есть название используемой мною программы, а название «конфигурационного файла» в этом случае будет .bashrc.

Ответ 2



Конфигурация zsh осуществляется с помощью файла .zshrc, который находится в корневой директории пользователя (/usr/username/.zshrc) Шелл-скрипт можно подключать несколькими способами: 1. Разместить непосредственно в .zshrc 1.1 Через создание псевдонима (alias) В .zshrc размещаем следующую строку: alias swap='mv -b $1 $2 && mv $2~ $1' Использование в командной оболочке: swap file1.txt file2.txt 1.2 Через функцию В .zshrc размещаем строку name() { ; }. Если размещаем в одну строку, то последняя команда должна заканчиваться на ; swap() { 'mv -b $1 $2 && mv $2~ $1'; } Использование в командной оболочке: swap file1.txt file2.txt 2. Разместить в отдельном файле и подключить в .zshrc Подключение сторонних скриптов осуществляется с помощью двух команд: . и source. Код размещаем в отдельном файле в виде функции, name() { ; }: swap.sh: swap() { 'mv -b $1 $2 && mv $2~ $1'; } В .zshrc размещаем следующую строку: source path/to/swap.sh Путь можно указывать абсолютный или относительно корневой директории пользователя.

пятница, 13 марта 2020 г.

Удаление определенного количества байт с конца файла shell скрипт

#linux #shell


Добрый день. Собственно вопрос. Как средствами shell удалить с конца файла N - о
количество символов. 
Есть зашифрованный gpg файл. В конец его дописываем контрольную сумму. 
А затем ее нужно считать и удалить. Как?

пробовал средствами  sed но так и не дошел до рабочего варианта
    


Ответы

Ответ 1



Пусть в переменных n - длина контрольной суммы, file - имя файла. truncate -s $(( $(stat -c '%s' $file) - $n )) $file

Удаление n символов из начала файла зашифрованого gpg используя Shell

#linux #shell


Стоит задача удаления n числа символов из начала файла используя shell. Файл зашифрован
gpg. Файл нельзя перезаписывать в другой файл.
    


Ответы

Ответ 1



dd if=$file of=$file skip=$n iflag=skip_bytes conv=notrunc Далее см. Удаление определенного количества байт с конца файла shell скрипт

Ответ 2



Для обрезания можно использовать cut. Например, для удаления первых 10 символов: cut -c 10- fname.txt > newfile.txt

Ответ 3



Инлайном можно попытаться так: sed -i '1s/^.\{10\}//' fname.txt но валидно только для текстовых файлов.

воскресенье, 8 марта 2020 г.

Как вывести ip всех DNS серверов?

#linux #shell


Как определить IP-адреса всех используемых DNS-серверов?

Через терминал и через файл shell
    


Ответы

Ответ 1



операционная система использует dns-серверы, перечисленные в файле /etc/resolv.conf в виде строк формата: nameserver ip-адрес посмотреть всё содержимое этого файла можно, например, командой: $ cat /etc/resolv.conf вывести только строки, начинающиеся с nameserver (обычно их не более трёх), можно, например, так: $ grep ^nameserver /etc/resolv.conf вывести только ip-адреса из таких строк можно, например, так: $ sed -n 's/^nameserver\s\+//p' /etc/resolv.conf

среда, 26 февраля 2020 г.

bash script подключение по ssh

#linux #bash #ssh #shell


Необходимо запускать скрипты с сервера удаленно. Получается вот такая 
цепочка:
server1: вызывает скрипт, который командой ssh "server2" "script1.sh" вызывает другой
скрипт. Далее на server2 script1 вызывает уже другой скрипт локально, т.е source ./script2.sh
и вот в данном скрипте идет снова команда ssh "serverN" "script3". Но вылетает ошибка: 
Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).
Если запускать script2.sh с сервера, то он отрабатывает. Получается проблема в цепочке
вызовов ssh.

Хотя доступ с есть, ключи на сервере есть. Не нашел ответа в гугле. Думаю что проблема
в окружении, пробовал указывать ключ напрямую - не помогает. Как решить данную проблему.
Буду благодарен, если посоветуете организовать централизованный доступ к скриптам каким-либо
другим способом!

Вопрос решился подставление ключа -i. ssh -i. 
    


Ответы

Ответ 1



конечно, лучше не «разбрасывать» секретные части ключей по компьютерам в сети, а держать их только в локальном (т.е., физически контролируемом) компьютере. чтобы работать с цепочкой компьютеров, подключаясь от одного к другому-третьему и т.д., можно использовать два несколько отличающихся подхода (подразумевается, что публичные части используемых ключей уже скопированы на все нужные компьютеры, а все секретные части уже известны агенту): воспользоваться настройками в файле ~/.ssh/config, в частности, возможностями директивы proxycommand, и подключаться к нужному компьютеру в цепочке вводом всего одной команды. пример необходимых секций host в файле ~/.ssh/config для цепочки локальный компьютер → user1@host1 → user2@host2: host host1 user user1 forwardagent yes host host2 user user2 forwardagent yes proxycommand ssh user1@host1 nc %h %p host host3 user user3 proxycommand ssh user2@host2 nc %h %p подключение к host2 (с локального компьютера): $ ssh host2 подключение к host3 (с локального компьютера): $ ssh host3 «руками» запускать на очередном в цепочке компьютере программу ssh, указав параметром следующий в цепочке компьютер. если директив forwardagent в соответствующих секциях host не описано, то надо будет явно указывать опцию -A прогрммы ssh. подключение к первому компьютеру в цепочке: $ ssh user1@host1 hostname host1 теперь ко второму (все примеры выполняется с локального компьютера): $ ssh user1@host1 ssh user2@host2 hostname Permission denied, please try again. Permission denied, please try again. Permission denied (publickey,password). опа, забыли про опцию! исправляемся: $ ssh -A user1@host1 ssh user2@host2 hostname host2 добавляем третий компьютер: $ ssh -A user1@host1 ssh user2@host2 ssh user3@host3 hostname Permission denied, please try again. Permission denied, please try again. Permission denied (publickey,password). хм. ну, да, на host2 ведь тоже надо пробросить доступ к агенту: $ ssh -A user1@host1 ssh -A user2@host2 ssh user3@host3 hostname host3 первый вариант представляется несколько более лаконичным с точки зрения вводимых команд, но зато надо больше «писанины разводить» в ~/.ssh/config

вторник, 25 февраля 2020 г.

Как сохранить вывод любой команды в файл и сразу же увидеть его в терминале?

#bash #shell #powershell


Как после долгого ожидания вывода сохранить то что увидел в файл?
Допустим по команде:
git grep  $(git rev-list --all)

Я понимаю что было бы предусмотрительно наличие > log.txt в конце, хочется сразу
сохранять что увидел?
Все сам разобрался, суть в следующем если допустим набрать просто git log, появится
диалог просмотра со значком :. Этот экран можно двигать вверх вниз там, а вот если
нажать кнопку s то появится диалог ввода имени файла, остается набрать допустим log.txt
и весь вывод будет сохранен в этот файл    


Ответы

Ответ 1



Чтобы увидеть вывод и сохранить его одновременно есть специальная команда tee, пример: command | tee log.txt Иллюстрация: Wikipedia, GFDL.

Ответ 2



Если не для одной команды, а для "сеанса", то я пользуюсь командой script (весь протокол работы будет в файле ./typescript, а вообще, посмотрите man script).

понедельник, 24 февраля 2020 г.

Как вывести содержимое текстового файла с пронумерованными строками?

#linux #shell


Помогите разобраться.

Выполняется поиск текстового файла с наименьшим количеством слов.

$ find -name "*txt" | xargs wc -w | sort -g | head -n 1


Как вывести на экран содержимое найденного текстового файла с пронумерованными строками?
cat -n?
    


Ответы

Ответ 1



варианты выдачи содержимого файла или потока с пронумерованными строками: nl содержимое указанного файла: $ nl файл содержимое потока строк: $ какие-то команды | nl cat содержимое указанного файла: $ cat -n файл содержимое потока строк: $ какие-то команды | cat -n less содержимое указанного файла: $ less -N файл содержимое потока строк: $ какие-то команды | less -N awk содержимое указанного файла: $ awk '{ print FNR " " $0 }' файл содержимое потока строк: $ какие-то команды | awk '{ print FNR " " $0 }' perl содержимое указанного файла: $ perl -pe '$_ = "$. $_"' файл содержимое потока строк: $ какие-то команды | perl -pe '$_ = "$. $_"' у вас, как я понял, весь поток заканчивается выдачей имени файла. чтобы применить к нему любую из перечисленных команд, надо воспользоваться программой xargs, которой вы уже пользовались. например, передать это имя программе nl можно так: $ команды возвращающие имя файла | xargs nl

Как удалять последовательные дубликаты по определённому столбцу?

#bash #shell #zsh


Представим, что есть некая программа, которая отдает данные вот в таком формате:

$ cat foo.bs
#!/bin/bash

echo "[INFO] Инициализация временных сдвигов"
sleep 1
echo "[INFO] Подготовка к временному увеличению скорости света"
sleep 1
echo "[WARN] u.speed_of_light: NaN"
sleep 1
echo "[INFO] Скорость света увеличена, начинаем создание темной материи"
sleep 1
echo "[ERROR] java.lang.NullPointerException: null"

$ ./foo.bs
[INFO] Инициализация временных сдвигов
[INFO] Подготовка к временному увеличению скорости света
[WARN] u.speed_of_light: NaN
[INFO] Скорость света увеличена, начинаем создание темной материи
[ERROR] java.lang.NullPointerException: null


Что хотелось бы получить:

[INFO] Подготовка к временному увеличению скорости света
[WARN] u.speed_of_light: NaN
[INFO] Скорость света увеличена, начинаем создание темной материи
[ERROR] java.lang.NullPointerException: null


Пошаговое воспроизведение:


Выводим [INFO] Инициализация временных сдвигов
Перезаписываем предыдущую строку, потому что у нее флаг [INFO], теперь последняя
отображаемая строка [INFO] Инициализация временных сдвигов
Выводим новую строку [WARN] u.speed_of_light: NaN.
Выводим новую строку [INFO] Скорость света увеличена, начинаем создание темной материи
потому что перезаписывать варнинги нельзя
Выводим новую строку [ERROR] java.lang.NullPointerException: null.


Необходимо именно перезаписывать предыдущую выведенную строку (см. пункт 2), поскольку
необходима индикация процесса.

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

#!/bin/bash

last_line=""

while IFS= read -r line
do
    if [[ $line != \[INFO\]* ]];
    then
        echo -e "\n$line"
    elif [[ $last_line == \[INFO\]* ]];
    then
        echo -en "\r$line"
    else
        echo -en "$line"
    fi

    last_line="$line"
done

    


Ответы

Ответ 1



вот такая минипрограмма для интерпретатора awk x!=$номер;{x=$номер} (где номер — это номер сортируемого столбца) делает примерно то, что вам нужно: удаляет последовательные дубликаты, встречющиеся в указанном столбце: $ cat foo | awk 'x!=$1;{x=$1}' [INFO] Инициализация временных сдвигов [WARN] u.speed_of_light: NaN [INFO] Скорость света увеличена, начинаем создание темной материи [ERROR] java.lang.NullPointerException: null но, как видите, печатается содержимое той строки, которая встретилась первой. чтобы печаталось содержимое последней из встреченных строк, можно, конечно, «помудрить» с программой (насколько я понимаю, существенно её усложнив), а можно просто «перевернуть» содержимое файла — чтобы первой шла последняя строка, второй предпоследняя и т.д., а после обработки — «перевернуть» опять. сделать это можно программой tac (даже в названии отражён ей смысл: cat «наоборот»): $ tac foo | awk 'x!=$1;{x=$1}' | tac [INFO] Подготовка к временному увеличению скорости света [WARN] u.speed_of_light: NaN [INFO] Скорость света увеличена, начинаем создание темной материи [ERROR] java.lang.NullPointerException: null идея минипрограммы позаимствована отсюда: http://www.unixcl.com/2009/05/remove-duplicate-consecutive-fields-or.html обновление придумал, как обойтись без tac. минипрограмма, правда, значительно усложняется: $ cat foo | awk 'END{print y}{if(x!=$1&&x!=""){print y};x=$1;y=$0}' чтобы сортировать по другому столбцу, надо подставить его номер вместо 1 в оба вхождения $1. обновление2 из уточнений стало понятно, что требуется не просто конечный результат, а «интерактивное шоу», когда последняя выведенная строка стирается, если следующая за ней начинается тем же самым словом. тогда можно сделать примерно так: #!/bin/bash prevline="" prevtag="" while read line; do newtag=$(echo $line | cut -d ' ' -f 1) if [ -n "$prevline" ]; then if [ "$newtag" == "$prevtag" ]; then echo -n "$prevline" | sed 's/./\x08/g' else echo fi fi echo -n "$line" prevtag=$newtag prevline=$line done echo \x08 — это символ «забоя» (backspace). к сожалению, программа sed, которой я воспользовался в данном случае, не воспринимает распрастранённую escape-последовательность \b в качестве данного символа. связано это с тем, что на данную escape-последовательность «подвешена» функция определения границы слова.

Ответ 2



./foo.bs | awk '{$1==l?p="\r":p="\n"}{printf "%s%-80s",p,$0}{l=$1}' По поводу длинных строк. Тут вам, для начала, необходимо определиться с тем, что вы хотите получить. Можно, например, отключить перенос строк: printf %b '\033[?7l'. Тогда длинные строки будут "обрезаться" по краю окна. Если нужно выводить строку целиком, с переносом, то можно заморочиться с backspace (работает далеко не везде), либо со сложным высчитыванием реального количества выведенных строк (tput cols в помощь). В общем, простого пути нет. Ну и следует помнить, что все эти пляски со спецсимволами очень сильно зависят от настроек как терминала на стороне клиента, так и на стороне сервера.

воскресенье, 16 февраля 2020 г.

Как вывести содержимое текстового файла с пронумерованными строками?

#linux #shell


Помогите разобраться.

Выполняется поиск текстового файла с наименьшим количеством слов.

$ find -name "*txt" | xargs wc -w | sort -g | head -n 1


Как вывести на экран содержимое найденного текстового файла с пронумерованными строками?
cat -n?
    


Ответы

Ответ 1



варианты выдачи содержимого файла или потока с пронумерованными строками: nl содержимое указанного файла: $ nl файл содержимое потока строк: $ какие-то команды | nl cat содержимое указанного файла: $ cat -n файл содержимое потока строк: $ какие-то команды | cat -n less содержимое указанного файла: $ less -N файл содержимое потока строк: $ какие-то команды | less -N awk содержимое указанного файла: $ awk '{ print FNR " " $0 }' файл содержимое потока строк: $ какие-то команды | awk '{ print FNR " " $0 }' perl содержимое указанного файла: $ perl -pe '$_ = "$. $_"' файл содержимое потока строк: $ какие-то команды | perl -pe '$_ = "$. $_"' у вас, как я понял, весь поток заканчивается выдачей имени файла. чтобы применить к нему любую из перечисленных команд, надо воспользоваться программой xargs, которой вы уже пользовались. например, передать это имя программе nl можно так: $ команды возвращающие имя файла | xargs nl

Как удалять последовательные дубликаты по определённому столбцу?

#bash #shell #zsh


Представим, что есть некая программа, которая отдает данные вот в таком формате:

$ cat foo.bs
#!/bin/bash

echo "[INFO] Инициализация временных сдвигов"
sleep 1
echo "[INFO] Подготовка к временному увеличению скорости света"
sleep 1
echo "[WARN] u.speed_of_light: NaN"
sleep 1
echo "[INFO] Скорость света увеличена, начинаем создание темной материи"
sleep 1
echo "[ERROR] java.lang.NullPointerException: null"

$ ./foo.bs
[INFO] Инициализация временных сдвигов
[INFO] Подготовка к временному увеличению скорости света
[WARN] u.speed_of_light: NaN
[INFO] Скорость света увеличена, начинаем создание темной материи
[ERROR] java.lang.NullPointerException: null


Что хотелось бы получить:

[INFO] Подготовка к временному увеличению скорости света
[WARN] u.speed_of_light: NaN
[INFO] Скорость света увеличена, начинаем создание темной материи
[ERROR] java.lang.NullPointerException: null


Пошаговое воспроизведение:


Выводим [INFO] Инициализация временных сдвигов
Перезаписываем предыдущую строку, потому что у нее флаг [INFO], теперь последняя
отображаемая строка [INFO] Инициализация временных сдвигов
Выводим новую строку [WARN] u.speed_of_light: NaN.
Выводим новую строку [INFO] Скорость света увеличена, начинаем создание темной материи
потому что перезаписывать варнинги нельзя
Выводим новую строку [ERROR] java.lang.NullPointerException: null.


Необходимо именно перезаписывать предыдущую выведенную строку (см. пункт 2), поскольку
необходима индикация процесса.

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

#!/bin/bash

last_line=""

while IFS= read -r line
do
    if [[ $line != \[INFO\]* ]];
    then
        echo -e "\n$line"
    elif [[ $last_line == \[INFO\]* ]];
    then
        echo -en "\r$line"
    else
        echo -en "$line"
    fi

    last_line="$line"
done

    


Ответы

Ответ 1



вот такая минипрограмма для интерпретатора awk x!=$номер;{x=$номер} (где номер — это номер сортируемого столбца) делает примерно то, что вам нужно: удаляет последовательные дубликаты, встречющиеся в указанном столбце: $ cat foo | awk 'x!=$1;{x=$1}' [INFO] Инициализация временных сдвигов [WARN] u.speed_of_light: NaN [INFO] Скорость света увеличена, начинаем создание темной материи [ERROR] java.lang.NullPointerException: null но, как видите, печатается содержимое той строки, которая встретилась первой. чтобы печаталось содержимое последней из встреченных строк, можно, конечно, «помудрить» с программой (насколько я понимаю, существенно её усложнив), а можно просто «перевернуть» содержимое файла — чтобы первой шла последняя строка, второй предпоследняя и т.д., а после обработки — «перевернуть» опять. сделать это можно программой tac (даже в названии отражён ей смысл: cat «наоборот»): $ tac foo | awk 'x!=$1;{x=$1}' | tac [INFO] Подготовка к временному увеличению скорости света [WARN] u.speed_of_light: NaN [INFO] Скорость света увеличена, начинаем создание темной материи [ERROR] java.lang.NullPointerException: null идея минипрограммы позаимствована отсюда: http://www.unixcl.com/2009/05/remove-duplicate-consecutive-fields-or.html обновление придумал, как обойтись без tac. минипрограмма, правда, значительно усложняется: $ cat foo | awk 'END{print y}{if(x!=$1&&x!=""){print y};x=$1;y=$0}' чтобы сортировать по другому столбцу, надо подставить его номер вместо 1 в оба вхождения $1. обновление2 из уточнений стало понятно, что требуется не просто конечный результат, а «интерактивное шоу», когда последняя выведенная строка стирается, если следующая за ней начинается тем же самым словом. тогда можно сделать примерно так: #!/bin/bash prevline="" prevtag="" while read line; do newtag=$(echo $line | cut -d ' ' -f 1) if [ -n "$prevline" ]; then if [ "$newtag" == "$prevtag" ]; then echo -n "$prevline" | sed 's/./\x08/g' else echo fi fi echo -n "$line" prevtag=$newtag prevline=$line done echo \x08 — это символ «забоя» (backspace). к сожалению, программа sed, которой я воспользовался в данном случае, не воспринимает распрастранённую escape-последовательность \b в качестве данного символа. связано это с тем, что на данную escape-последовательность «подвешена» функция определения границы слова.

Ответ 2



./foo.bs | awk '{$1==l?p="\r":p="\n"}{printf "%s%-80s",p,$0}{l=$1}' По поводу длинных строк. Тут вам, для начала, необходимо определиться с тем, что вы хотите получить. Можно, например, отключить перенос строк: printf %b '\033[?7l'. Тогда длинные строки будут "обрезаться" по краю окна. Если нужно выводить строку целиком, с переносом, то можно заморочиться с backspace (работает далеко не везде), либо со сложным высчитыванием реального количества выведенных строк (tput cols в помощь). В общем, простого пути нет. Ну и следует помнить, что все эти пляски со спецсимволами очень сильно зависят от настроек как терминала на стороне клиента, так и на стороне сервера.

пятница, 14 февраля 2020 г.

Как работают фигурные скобки в Shell Linux?

#linux #shell #find


есть,скажем, команда find /Users/mac/Downloads/topdf  -exec sh -c "ls -l {}" \;
вот что выводит:


  


я не понимаю как тут работают фигурные скобки. 
 То есть понятно, что по результату с помощью {} программа выводит листинг про каждый
файл, а потом то же самое, но плюс еще и путь к файлу
но что она ставит вместо фигурных скобок ?

что-то типа

find /Users/mac/Downloads/topdf  -exec sh -c "ls -l /Users/mac/Downloads/topdf/file1.pdf" \;


потом 

find /Users/mac/Downloads/topdf  -exec sh -c "ls -l /Users/mac/Downloads/topdf/file2.pdf" \;


или я не так понимаю ? 
Буду благодарен за объяснение 
    


Ответы

Ответ 1



В данном случае фигурные скобки не имеют отношения к языку оболочки, они интерпретируются исключительно утилитой find. Ключ -exec для каждого найденного файла запускает процесс указанный в его параметрах, при этом заменяя строку {} на имя аргумента; в данном случае он по очереди запустит: sh -c "ls -l /Users/mac/Downloads/topdf/." sh -c "ls -l /Users/mac/Downloads/topdf/file1.pdf" sh -c "ls -l /Users/mac/Downloads/topdf/file2.pdf" # ... итд Замечания: Порядок обхода файлов не определён Некоторые реализации find допускают замену строки '{}' только в случае если она является отдельным аргументом. Для них пришлось бы писать нечто подобное: find -exec sh -c 'ls -l "$0"' '{}' \; К GNU и FreeBSD find это не относится. Обычно не прибегают к дополнительному вызову sh и сразу взывают необходимую утилиту, например ls: find -exec ls -l '{}' \; Окромя лучшей производительности у этого способа есть и другое важное преимущество — он корректно обрабатывает файлы с пробелами и/или переводами строк в именах. см. man find

воскресенье, 9 февраля 2020 г.

Распарсить JSON из переменной и взять первый элемент массива из поля используя Shell

#linux #json #shell


Дано:

{"code":200,"lang":"ru-en","text":["test"]}


Задача:

Поместить test в переменную.

Как пробую:

text = '{"code":200,"lang":"ru-en","text":["test"]}'

translate = $($text | jq '.text[0]')

echo $translate


В итоге пишет, что не найдено.

Вопрос:

Почему требуется больше часа на вытаскивание значения из JSON?
    


Ответы

Ответ 1



text='{"code":200,"lang":"ru-en","text":["test"]}' translate="$(echo "$text" | jq '.text[0]')" echo "$translate" Из важного: Одинарные кавычки в инициализации text echo и двойные кавычки в translate А ещё на bash'е можно так сделать: translate="$(jq '.text[0]' <<<"$text")"

Экранирование специалных символов для аргументов SSH команды

#unix #shell #символы #экранирование


Задача:


Переменная, содержит в конце $$

$ a='SYS_$$'

$ echo $a

SYS_$$

Тут разворачивает $$ в конце строки как номер процесса, а надо отобразить строку
"SYS_$$", передаваемую как параметр.

$ echo "echo \$1" | ssh another_host "/bin/sh -s $a"

SYS_18992


    


Ответы

Ответ 1



Попробуйте так: $ a='SYS_$$' $ echo 'echo $1' | ssh another_host "/bin/sh -s '$a'" SYS_$$ При вызове ssh, внутри двойных кавычек одиночные кавычки тетряют своё специальное назначение и переменная $а будет замещена её значением, т.е. $SYS_$$. Далее, при вызове sh на удалённом хосте, одиночные кавычки вновь приобретают своё специальное назначение и препятствуют замещению $$ текущим pid.

Ответ 2



надо передать символы одинарных кавычек вокруг аргумента, с которым вызывается программа sh. для этого надо добавить перед кавычками символы \: $ v='a$$'; echo 'echo $1' | ssh хост sh -s \'$v\' a$$

Как записать дату/время в начало строк лога?

#shell #bash #linux


Некий скрипт на bash выполняет бэкап и попутно пишет лог. Нужно, чтобы каждая строка
лога начиналась с даты и времени. У меня пока что получилось вот так:
LOG='./logfile'  
echo $(date +"%y-%m-%d %T") Начало копирования файлов >> $LOG  
...  
echo $(date +"%y-%m-%d %T") Окончание копирования файлов >> $LOG

Существует ли способ писать это короче?    


Ответы

Ответ 1



Ну так сделайте функцию log и используйте её. Что-то вроде этого #!/bin/bash LOGFILE=/path/to/my/log/file log(){ message="$(date +"%y-%m-%d %T") $@" echo $message echo $message >>$LOGFILE } log "Начало копирования файлов" # .... log "Окончание копирования файлов"

суббота, 8 февраля 2020 г.

Как записать результат команды в текстовый файл?

#git #shell


Такой вариант работает, но нет переносов в итоговом файле, все в одну строчку. Как
добавить переносы?   

echo $(git ls-tree -r dev --name-only) > filesUnderGit.txt

    


Ответы

Ответ 1



git ls-tree -r master --name-only > test.txt Как и для любой другой команды, которая пишет в stdout. И как с любой такой командой, можно делать pipe-поезд, если вы хотите как-то ещё обработать выходной поток. git ls-tree -r master --name-only | grep "some/path" | head 42 > test.txt Здесь мы берем только файлы, лежащие в some/path, а потом из них оставляем первые 42.

Ответ 2



данная конструкция: $ echo $(git ...) несколько «перегружена». условно её можно представить как: $ программа $(команда) сначала выполняется команда, заключённая внутри «оболочечного» (от слова shell — оболочка) оператора $(). затем её вывод (не важно, содержит он одну строку или больше) преобразуется в одну строку (символы перевода строки при этом заменяются пробелами) для передачи программе в качестве набора параметров (не опций!). вот потому и выходит «всё одной строкой». в данном конкретном случае избежать такого преобразования можно довольно просто — достаточно убрать echo и оператор $() — за ненадобностью: $ команда но может быть и такая ситуация, что и программа и оператор — нужны, но требуется, чтобы не было «преобразования в одну строку». избежать этого тоже нетрудно — достаточно поставить (двойные) кавычки «на пути» между выполнением оператора $() и формированием строки параметров для программы. примерно так: $ программа "$(команда)"

пятница, 7 февраля 2020 г.

Вывести адрес, под которым машина отправляет запросы к 8.8.8.8 (DNS)

#linux #bash #shell #dns


Задание:

Выведите IP адрес под которым машина отправляет запросы к 8.8.8.8 (учитывать NAT
после выхода пакета из машины не нужно, нужен IP адрес с которым пакет покидает машину).

Нужно написать Bash-скрипт. Как я понимаю, нужно понять где у нас в локальной сети шлюз.

На данный момент готово следующее:

используем tcpdump -
tcpdump -i any 'udp port 53' > whoIP - таким образом результат дампа со всеми запросами
на 53 порт(т.е. DNS) сохраняется в файл.

Файл содержит следующие строки:

15:09:44.365818 IP 192.168.0.102.11996 > 192.168.0.1.domain: 31675+ A? www.google-analytics.com.
(42) 

15:09:44.398267 IP 192.168.0.1.domain > 192.168.0.102.11996: 31675 7/0/0 CNAME www-google-analytics.l.google.com., A

затем при помощи "Адского" выражения:
cat whoIP | grep A? | grep -E -o '>+ ([0-9]{1,3}[.]){3}[0-9]{1,3}'| grep -E -o "([0-9]{1,3}[.]){3}[0-9]{1,3}"

Логика его такова:


cat whoIP |grep A?  - в файле мы ищем строки содержащие запрос на разрешении имени.
tcpdump помечает такие строки A? 
| grep -E -o '>+ ([0-9]{1,3}[.]){3}[0-9]{1,3}' - данное регулярное выражение ищет
IP-адрес в формате ipv4.
в строке мы ищем следующую конструкцию "> "ipv4 адрес"" - в tcpdump ">" означает
от кого к кому идет запрос. Нас интересует к кому.
| grep -E -o "([0-9]{1,3}[.]){3}[0-9]{1,3}" - из найденного фрагмента "к кому" вырезаем
только адрес.Данное выражение от предыдущего отличается лишь отсутствием "<"

В целом мой алгоритм следующий:


1.Запустить tcpdump и перенаправить его вывод в файл whoIP


Послать в другом терминал DNS-запрос при помощи dig
ОСТАНОВИТЬ TCPDUMP ctrl+c
grep файл


Во-первых, проблема в том, что в скрипт данную последовательность я заложить не могу,
т.к. tcpdump работает пока ему не послать сигнал завершения( вот тут и загвоздка).
Если в скрипте ищет запуск tcpdump, то следующие команды не исполняются, т.к. tcpdump
работает бесконечно.

Подскажите, можно ли написать данный скрипт и вообще иду ли я по верному пути, в
чем сомневаюсь очень сильно. Мне кажется, должно быть более простое решение.
    


Ответы

Ответ 1



Если это работает, то значит имеет право на жизнь. А tcpdump можно заставить работать как нужно. для начала нужно его запустить в фоне tcpdump .... параметры .... & символ & важен - он отправляет процесс в фон и можно будет продолжить скрипт дальше. Так как нам нужно будет потом его остановить, то следующей строкой запоминаем его pid. TDPID=$! эта строка должна быть сразу. Иначе она запомнит другой PID. TDPID - это просто переменная. дальше вставляете свой код. Когда нужно будет tcpdump остановить, просто посылаем ему Ctrl+C kill -3 $TDPID P.S. Важный момент, на который все натыкаются и долго не могут понять, что не так. В строке TDPID=$! не должно быть пробелов. Если написать так TDPID = $!, то работать не будет.

Ответ 2



Мне кажется, должно быть более простое решение. совершенно верно. если речь идёт о сетевой подсистеме программы linux версии выше или равной 2.0 (или 2.1? никак не могу запомнить), то можно воспользоваться программой ip: $ ip route get 8.8.8.8 8.8.8.8 via 192.168.0.1 dev eth0 src 192.168.0.2 cache в приведённом примере вывода 192.168.0.2 — это и есть ip-адрес, с которым будут покидать систему пакеты, адресованные к 8.8.8.8. если полный вывод программы не подходит, и этот адрес надо извлечь, то для удобства обработки можно добавить опцию -o (oneline): $ ip -o route get 8.8.8.8 8.8.8.8 via 192.168.0.1 dev eth0 src 192.168.0.2 \ cache и извлечь его с помощью, например, программы sed (кстати, параметры route и get в данном случае можно сократить до одной буквы): $ ip -o r g 8.8.8.8 | sed 's/.*src \([^ ]\+\) .*/\1/' 192.168.0.2 подробности смотрите в документации к программе ip: man ip, man ip-route. кстати, маршрутов может оказаться и более одного (например, при использовании на данной машине динамической маршрутизации). они все будут отображены, и, следовательно, в окончательном выводе будет более одной строчки с ip-адресами.