그래서 내가 실행하는 모든 명령이 다른 명령으로 파이프되기를 원하며 .bashrc 함수를 사용해 보았습니다.
* () {
${@} | [my command]
}
예를 들어, 제가 정말로 달성하고 싶은 것은 마지막으로 실행한 명령의 표준 출력을 다른 명령으로 호출할 수 있는 임시 파일에 저장하는 방법입니다.
* () {
${@} | tee /tmp/last_command
}
내가 달성하고 싶은 것을 설명하는 또 다른 방법은 다음과 같습니다.ALIAS [any_command]='[any_command] | tee /tmp/last_command'
다음은 이 예제를 통해 달성한 실제 예입니다 tee
.
$ tail -5 /etc/passwd
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
명령을 실행하면 실제로 어떤 일이 발생합니까(그러나 매번 작성할 필요는 없습니다):
$ tail -5 /etc/passwd | tee /tmp/last_command
완전한 예는 다음과 같습니다.
$ tail -5 /etc/passwd
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
$ cat /tmp/last_command
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
somethingsomethingsomethingsomething
$ date
Tue Jan 14 13:26:04 EET 2020
$ cat /etc/last_command
Tue Jan 14 13:26:04 EET 2020
답변1
Bash는 유연성이 떨어지므로 각 명령줄 전후에 명령/함수를 실행할 수 있습니다. 하지만 Bash에서 출력을 캡처하는 것만으로는 간단하지 않습니다.
몇 가지 질문은 다음과 같습니다:
- 파이프에 쓰는 것은 터미널이나 파일(예: , )에 쓰는 것과 같지 않으며
ioctl()
많은 프로그램에서 이를 감지합니다(예: 심지어 ). 대화형 편집기와 함께 사용하고 싶지 않을 것입니다.fseek()
fstat()
man
ls
vi
time
" ", 파이프 또는 쉘 루프와 같은 특수 명령은 솔루션을 복잡하게 만들 수 있으며 파이프는 반환 코드를 변경할 수 있습니다.alias
별칭을 사용하여 가능한 모든 명령을 쉽게 "하이재킹"할 수는 없습니다. 이러한 별칭 은 명령줄 옵션/인수를 손상시킬 수 있습니다.어휘적으로바꾸기 명령- 단일 파일에 (겹쳐) 쓸 수는 없습니다. 즉, 마지막 출력을 확인하기 위해 명령을 실행할 때 경합이 있습니까?
- 캡처를 원
stdout
하지만stderr
인터리빙 문제가 있을 수 있는 경우 - 파이프를 사용하는 경우 파이프 및 기타 특정 리디렉션이 있을 때 bash가 하위 쉘을 생성하고 직접적인 부작용은 변수 할당이라는 점을 기억하십시오. 이러한 변수는 하위 쉘에 할당된 다음 현재 쉘에서 삭제되며 변경되지 않은 상태로 유지됩니다. "
.
" (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
때문에 ) 및 bash를 사용하세요 .script
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가 새 명령 프롬프트를 표시할 때마다 호출됩니다. 먼저 screen에서 실행 중인지 확인한 다음 screen과 대화하여 로깅을 중지하라는 명령을 내리고 마지막 출력을 쓴 /tmp/last_command
다음 screen에 로깅을 시작하라고 지시합니다.
프롬프트가 표시되기 전에 s가 호출 되므로 PROMPT_COMMAND
프롬프트와 명령(백스페이스 등의 편집 내용 포함)이 모두 로그 파일 시작 부분에 나타납니다. 이 문제를 해결하는 한 가지 방법은 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("Return"이라고도 함)을 다음 시퀀스로 대체합니다.Maccept-line
- Ctrl- E(줄 끝)
| tee /tmp/last_command
(단어)- Ctrl- J(라인 수락)
발행된 명령 자체가 변경되면 화면과 이력에 표시됩니다. 단점은 이 간단한 접근 방식이 "자기 인식"이 아니며 모든 명령을 맹목적으로 변경한다는 것입니다. 심지어 역사적으로 변경 사항이 적용된 명령도 마찬가지입니다.
다음은 전송된 명령을 수정하는 함수를 사용하는 더 복잡한 버전입니다.
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단계는 , - _recorder
에 바인딩된 bash 함수로 대체됩니다 (매크로는 키 입력만 포함할 수 있으므로 입력할 필요가 없으며 키 입력에만 바인딩하면 됩니다).EscCtrla
이 함수는 임의로 스마트할 수 있습니다. 여기서는 출력 파일 경합을 처리하고 입력 행을 변경하기 전에 파이프가 이미 존재하는지 확인합니다. 다른 리디렉션이 있는 경우 전체 명령줄을 하위 쉘로 래핑할 수도 있습니다(비록 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
))이 이 방법으로 인해 손상됩니다.
답변2
- "각 명령 뒤" 대신 - "다음 프롬프트 전"이면 충분할까요? 생각 bash
)
PROMPT_COMMAND If set, the value is executed as a command prior to issuing each primary prompt.
( man bash
) 돕다?