Как извлечь названия программ из команды в bash?

Предположим, есть файл истории команд наподобие такого:
sudo ls  | awk '!($NF ~ /\.[a-z]+$/)'
for i in ~/*; do echo $(locate -c -r $i) $i; done | pv | sort -nr | bcat -t
for f in *.zip; do unzip $f; done
RACK_HANDLER=falcon rails s

Необходимо произвести разбор этих строк, чтобы смог извлечь имена используемых программ (желательно, не встроенных в shell). Из примера выше:
sudo ls awk
pv sort bcat
unzip
rails

Я нашел простой модуль Shell::Parser на Перле, который вроде бы то, что мне нужно. Есть какой-то gem с похожей функциональностью?
  • Вопрос задан
  • 151 просмотр
Пригласить эксперта
Ответы на вопрос 1
xotkot
@xotkot
хорошо есть и хорошо весьма
это вполне легко можно реализовать на самом баше в несколько строк, если нужен именно руби то можете просто взять за основу как отправную точку для своей реализации

список всех программ расположенных в $PATH
$ ls ${PATH//:/ } |awk 'NF && !/:$/'

список, а точнее описание, встроенных(builtin) команд в оболочку bash можно посмотреть с помощью команды help
$ help

но думаю правильнее будет использовать type для определения типа команды
$ type -t rg
file
$ type -t cd
builtin

$ type --help
...
-t	output a single word which is one of `alias', `keyword',
	`function', `builtin', `file' or `', if NAME is an alias,
	shell reserved word, shell function, shell builtin, disk file,
	or not found, respectively


тип file по сути это чисто внешние программы, а builtin - внутреннии, на счет остальных могут быть нюансы, то есть при проверке через type оставляем тока те утилиты которые возвращают file.

В итоге мы можем сгенерировать список(file.txt) чисто внешних программ из тех что лежат в $PATH:
$ for i in $(ls ${PATH//:/ } |awk 'NF && !/:$/');do [[ $(type -t $i) == "file" ]] && echo $i;done > file.txt


Допустим у нас есть файл test.txt с историей команд:
sudo ls  | awk '!($NF ~ /\.[a-z]+$/)'
for i in ~/*; do echo $(locate -c -r $i) $i; done | pv | sort -nr | bcat -t
for f in *.zip; do unzip $f; done
RACK_HANDLER=falcon rails s


тогда:
$ grep -o -w -n -f file.txt test.txt
1:sudo
1:awk
2:pv
2:sort
3:zip
3:unzip


единственный недостаток это каждое совпадение пишет с новой строки, исправляем с помощью awk:
$ grep -o -w -n -f file.txt test.txt |awk -F: '{if($1!=i){printf $2" "}else{print $2};i=$1}'
sudo awk
pv sort
zip unzip


если сравнивать с вашим шаблоном:
sudo ls awk
pv sort bcat
unzip
rails


то мы видим что нету ls это из за того что, например, у меня это alias основанный на exa - внешней программе, хотя с таким же успехом это могла быть внутренняя программа, та же ls с подобранными ключами, но её бы тоже здесь бы не отобразило, в общем если алиасы включать в вывод то по идее их тоже нужно будет анализировать в отдельном обработчике.

также естественно что иногда будут происходить косяки как например тут с zip при анализе строки
for f in *.zip; do unzip $f; done
zip тут не выступает в роли программы, но это всё же слово которое совпало с нашим списком утилит.
bcat и rails не вывело так как данных программ не установленно.

в общем если кратко то всё это реализовывается в два однострочника на bash:
$ for i in $(ls ${PATH//:/ } |awk 'NF && !/:$/');do [[ $(type -t $i) == "file" ]] && echo $i;done > file.txt
$ grep -o -w -n -f file.txt test.txt |awk -F: '{if($1!=i){printf $2" "}else{print $2};i=$1}'
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы