попробуй
find... | awk '{$1 = "rm" ; print } ' | bash
это заменит фактическую сумму md5(aaf...
)на rm.
это не будет работать, если в имени файла есть специальный символ, а также если файл защищен от записи (замените rm
наrm -f
).
Bash не настолько гибок, вы можете запускать команды/функции до и после каждой командной строки -, но сбор вывода только в bash не -тривиален.
Некоторые проблемы включают:
ioctl()
, fseek()
, fstat()
), многие программы определяют это (как man
и дажеls
). Вы не захотите использовать это с интерактивным редактором, таким как vi
. time
", конвейеры и даже циклы оболочки могут усложнять решения, а конвейеры изменяют коды возврата alias
только лексически заменяет команду stdout
и stderr
, у вас могут возникнуть проблемы с чередованием .
"(source
)и exit
затронуты аналогичным образом. (В приведенном ниже варианте использования readline Ctrl-j можно использовать для запуска таких команд.)Это (примерно )то, что я использую для аналогичной проблемы, функции, называемые +
, ++
и т. д., поэтому я могу префиксировать только те команды, для которых я хочу захватить/обработать вывод, например к выбору X или буферу обмена:
function +() (
set -f # no double expand
eval "$@" | tee >( tr -s "\n" " " | xclip )
return ${PIPESTATUS[0]} # real rc
)
напр. + find /tmp -name "*.pdf" -mtime -30
Это несовершенно, но в основном делает то, что мне нужно.
Выше @mosvy рекомендует script
,это создает новый терминал tty (), в котором запускается программа, которая обходит все эти проблемы. Он записывает весь вывод на родительский tty, но также записывает его в файл. Он записывает либо весь сеанс, либо одну команду, поэтому у вас все еще есть проблема с тем, как его автоматически вызвать.
Вместо этого используйте screen
(, потому что в отличие от script
можно взаимодействовать и управлять работающим экземпляром )и bash PROMPT_COMMAND
:
if [[ "$TERM" -eq "screen" && -n "$STY" ]]; then
PROMPT_COMMAND=screenlog
SCREENLOG="${HOME}/screenlog"
fi
function screenlog() {
[[ -n "$STY" ]] && {
screen -S "$STY" -X log off
[[ -f "${SCREENLOG:=$HOME/screenlog}" ]] && mv "$SCREENLOG" /tmp/last_command
screen -S "$STY" -X logfile "$SCREENLOG"
screen -S "$STY" -X log on
}
return 0
}
Если указанное выше находится в вашем .bashrc
, вы сможете просто запустить screen
. Вышеупомянутая функция вызывается каждый раз, когда bash отображает новую командную строку. Сначала он проверяет, что он работает под экраном, затем обращается к экрану, чтобы выдать команды для остановки ведения журнала, записывает последний вывод в /tmp/last_command
, а затем приказывает экрану начать ведение журнала.
Одно предостережение заключается в том, что, поскольку PROMPT_COMMAND
вызывается до отображения подсказки, и подсказка, и команда (, включая такие изменения, как возврат ), появятся в начале файла журнала. Один из способов обойти это — эмулировать «pre -exec» с ловушкой DEBUG, чтобы вместо этого запустить вышеуказанное, например:Измените все команды bash через программу перед их выполнением . Если вы запускаете «интерактивные» или терминальные программы (, например. man
илиtop
)вы также получите записанные последовательности управления терминалом, что полезно, если вы хотите воспроизвести вывод (, хотя script
/ scriptreplay
в этом отношении лучше ).
Внутри bash используется readline, что позволяет привязывать ключи к функциям readline, последовательностям других ключей или функциям оболочки (, но не комбинациям трех типов ). Это относительно легко сделать:
bind '"\C-e": end-of-line'
bind '"\C-j": accept-line'
bind '"\C-m": "\C-e | tee /tmp/last_command\C-j"'
Первые две строки просто паранойя, чтобы убедиться, что Ctrl-E и Ctrl-J привязаны как положено (, если у вас есть терминал без accept-line
связан, у вас больше нет терминала... ).Последняя строка заменяет Ctrl-M (, также известную как «Return» ), которая обычно связана с accept-line
, последовательностями:
| tee /tmp/last_command
(буквальный текст)Это видно на экране и в истории по мере изменения самой выданной команды. Недостатком этого простого подхода является то, что он не является «самоосознающим» -и слепо изменяет каждую команду, даже ту, что была в истории, с уже примененным изменением.
Вот более сложная версия, в которой используется функция для изменения команд по мере их отправки:
bind '"\C-e": end-of-line'
bind '"\C-j": accept-line'
bind -x '"\e\C-a": _recorder'
function _record() {
tee /tmp/this_command
[[ -f /tmp/this_command ]] && mv /tmp/this_command /tmp/last_command
}
function _recorder() {
local text="| _record"
[[ -z "${READLINE_LINE}" ]] && return 0
[[ "$READLINE_LINE" =~ "${text}"$ ]] || READLINE_LINE+=" $text"
return 0
}
bind '"\C-m": "\C-e\e\C-a\C-j"'
Шаг 2 заменен bash-функцией _recorder
, привязанной к Esc , Ctrl-a (, которую не нужно набирать, ее просто нужно привязан к нажатию клавиши, потому что макрос может содержать только нажатия клавиш ).
Эта функция может быть сколь угодно умной, здесь она обрабатывает гонку выходных файлов и проверяет, что канал еще не присутствует, перед изменением входной строки. Вы также можете обернуть всю командную строку в подоболочку, если присутствуют другие перенаправления (, хотя вы быстро обнаружите, что синтаксический анализ командных строк bash в bash будет, хм, сложным ).
Можно и дальше (! )усугубить взлом, исправив историю на лету:
function myprompt() {
local _seq _cmdline
local text="| _record"
read _seq _cmdline < <(HISTTIMEFORMAT= history 1) # previous command
[[ "${_cmdline}" =~ (.*)" ${text}"$ ]] && {
_cmdline="${BASH_REMATCH[1]}"
history -d $_seq # delete entry
history -s "$_cmdline" # restore original
}
}
PROMPT_COMMAND=myprompt
Подход readline имеет еще один -нетривиальный недостаток :не-простые команды, которые обычно можно вводить в несколько строк, при этом подходе нарушается, например. (while
, for
).