출력이 터미널에 맞지 않으면 자동으로 호출기를 통해 파이프되도록 명령을 래핑할 수 있기를 원합니다.
현재 저는 다음 쉘 기능을 사용하고 있습니다(Arch Linux의 zsh에서):
export LESS="-R"
RET="$($@)"
RET_LINES="$(echo "${RET}" | wc -l)"
if [[ $RET_LINES -ge $LINES ]]; then
echo "${RET}" | ${PAGER:="less"}
else
echo "${RET}"
fi
그러나 그것은 나에게 별로 설득력이 없습니다. 내가 원하는 것을 달성할 수 있는 더 나은 방법(견고성과 오버헤드 측면에서)이 있습니까? 작업이 잘 수행된다면 zsh 특정 코드에도 열려 있습니다.
고쳐 쓰다:이 질문을 하고 나서 알게 된 사실은답변$LINES
less
출력 을 모두 캐싱하는 대신 로 전송하기 전에 대부분의 라인을 버퍼링하는 더 나은(더 복잡한 경우) 솔루션을 제공합니다 . 불행하게도 어느 솔루션도 길고 복잡한 문제를 해결하지 못하기 때문에 이 역시 만족스럽지 않습니다. 예를 들어, 위의 코드가 다음과 같은 함수에 저장되어 pager_wrap
있다면
pager_wrap echo {1..10000}
호출기를 통해 파이프하는 대신 긴 줄을 표준 출력으로 인쇄합니다.
답변1
POSIX 셸 준수를 위해 작성된 솔루션이 있지만 bash에서만 테스트했기 때문에 이식 가능한지 확실하지 않습니다. 그리고 저는 zsh를 모르기 때문에 zsh 친화적으로 만들려고 노력하지 않았습니다. 명령을 다른 명령에 인수로 전달하는 것은 잘못된 설계 입니다 .
물론 이 문제를 해결하려면 터미널에 있는 행과 열 수를 알아야 합니다. 아래 코드에서는 종속성 LINES
과 COLUMNS
환경 변수가 있다고 가정합니다( less
참조). 보다 신뢰할 수 있는 방법은 다음과 같습니다.
rows="${LINES:=$(tput lines)}"
다음 과cols="${COLUMNS:=$(tput cols)}"
같이 사용하세요 .AP 추천, 또는- 출력을 봅니다
stty size
. 이 명령은 터미널을 표준 입력으로 사용해야 하므로 스크립트에 있고 스크립트에 파이프하는 경우stty size <&1
(bash에서) 또는 이라고 말해야 합니다stty size < /dev/tty
. 출력을 캡처하는 것은 훨씬 더 복잡합니다.
비밀 요소: 이 fold
명령은 화면에서처럼 긴 줄을 나누어 스크립트가 긴 줄을 올바르게 처리할 수 있도록 합니다.
#!/bin/sh
buffer=$(mktemp)
rows="$LINES"
cols="$COLUMNS"
while true
do
IFS= read -r some_data
e=$? # 1 if EOF, 0 if normal, successful read.
printf "%s" "$some_data" >> "$buffer"
if [ "$e" = 0 ]
then
printf "\n" >> "$buffer"
fi
if [ $(fold -w"$cols" "$buffer" | wc -l) -lt "$rows" ]
then
if [ "$e" != 0 ]
then
cat "$buffer"
else
continue
fi
else
if [ "$e" != 0 ]
then
"${PAGER:="less"}" < "$buffer"
# The above is equivalent to
# cat "$buffer" | "${PAGER:="less"}"
# … but that’s a UUOC.
else
cat "$buffer" - | "${PAGER:="less"}"
fi
fi
break
done
rm "$buffer"
이것을 사용하려면:
- 위의 내용을 파일에 넣으세요
mypager
. - (선택 사항) 이를 검색 경로의 디렉터리에 배치합니다
$HOME/bin
. - 를 입력하여 실행 가능하게 만듭니다
chmod +x mypager
. ps ax | mypager
또는 같은 명령에 사용하세요ls -la | mypager
.
두 번째 단계(검색 경로의 디렉터리에 스크립트 넣기)를 건너뛴 경우 이 작업을 수행해야 합니다.ps ax | path_to_mypager/mypager
path_to_mypager
"와 같은 상대 경로일 수 있습니다..
".
* 하나의 명령을 다른 명령의 인수로 전달하는 것이 잘못된 설계인 이유는 무엇입니까?
1. 미학/전통의 고수/유닉스 철학
유닉스에는 철학이 있다~의뭔가를하고 잘해라. 예를 들어, 프로그램이 특정 방식(페이저처럼)으로 데이터를 표시하는 경우 데이터를 생성하는 메커니즘을 호출해서는 안 됩니다. 이것이 바로 파이프의 목적입니다.
사용자가 지정한 명령이나 프로시저를 실행하는 Unix 프로그램은 많지 않습니다. 이를 수행하는 일부를 살펴보겠습니다.
- Shell은 OK와 마찬가지로 사용자가 지정한 명령을 실행하는 쉘입니다.
sh -c "command"
일하다;이것은한 가지쉘이 하는 일. (물론 쉘이 단순한 프로그램이라는 뜻은 아닙니다.) env
,nice
,nohup
,setsid
,su
, 그리고sudo
. 이들 프로그램에는 공통점이 있습니다. 즉, 모두 수정된 실행 환경 에서 프로그램을 실행하기 위해 존재한다는 것입니다1 . Unix는 일반적으로 다른 프로세스의 실행 환경을 변경하는 것을 허용하지 않기 때문에 자신이 하는 방식으로 작동해야 합니다. 그런 다음fork
및/또는exec
.
_______
1 나는 이 표현을 사용하고 있습니다실행 환경 광범위하게 말하면 환경 변수를 의미할 뿐만 아니라 "nice
" 값, UID 및 GID, 프로세스 그룹, 세션 ID, 제어 터미널, 열린 파일, 작업 디렉터리,umask
값,ulimit
s, 신호 처리,alarm
타이머 및 기타 프로세스 속성도 포함합니다. 등.- "쉘 탈출"을 허용하는 프로그램. 마음속에 떠오르는 유일한 예는
vi
/ 입니다vim
. 물론 다른 예도 있을 거라 확신합니다. 역사적인 유물들입니다. 이는 윈도우 시스템이나 작업 제어보다 앞서 있습니다. 파일을 편집하고 다른 작업(디렉토리 목록 보기 등)을 수행하려면 셸로 돌아가기 전에 파일을 저장하고 편집기를 종료해야 합니다. 이제 다른 창으로 전환하거나 Ctrl+ Z(또는 입력:suspend
)를 사용하여 편집기를 활성 상태로 유지하면서 셸로 돌아갈 수 있으므로 셸 이스케이프는 더 이상 사용되지 않습니다.
복제하는 대신 기능을 활용하기 위해 다른(하드코드된) 프로그램을 실행하는 프로그램은 포함하지 않습니다. 예를 들어, 일부 프로그램은 diff
또는 sort
. (예 를 들어 초기 버전을 사용하여 문서에 사용된 단어 목록을 spell
가져온 다음 해당 목록을 사전 단어 목록과 비교하고 문서의 어떤 단어가 사전에 없는지 식별했다는 이야기가 있습니다. .)sort -u
diff
comm
2. 시간 문제
스크립트 작성 방법에 따라 해당 RET="$($@)"
줄은 호출된 명령이 완료될 때까지 완료되지 않습니다. 따라서 데이터를 생성한 명령이 완료될 때까지 스크립트는 데이터 표시를 시작할 수 없습니다. 아마도 이 문제를 해결하는 가장 쉬운 방법은 데이터 생성 명령을 데이터 표시 프로그램에서 분리하는 것입니다(다른 방법도 있지만).
삼. 명령 기록
출력이 표시 필터에 의해 처리되는 일부 명령을 실행하고 출력을 확인한 후 해당 출력을 파일에 저장하기로 결정했다고 가정합니다. 입력한 경우(가설의 예)
ps ax | mypager
그러면 들어갈 수 있어요
!:1 > myfile
또는 ↑해당 줄을 누르고 편집하세요. 이제 입력하면
mypager "ps ax"
여전히 돌아가서 명령을 편집할 수 있지만
ps ax > myfile
그렇게 간단하지는 않습니다.아니면 달리기를 다음 단계로 결정했다고 가정해 보겠습니다
ps uax
. 이미 입력했다면ps ax | mypager
다음을 수행할 수 있습니다.!:0 u!:*
다시 말하지만,
mypager "ps ax"
여전히 실행 가능하지만 틀림없이 더 어렵습니다.ps ax | mypager
또한 두 가지 명령인 및 를 살펴보십시오mypager "ps ax"
.history
한 시간 후에 목록을 실행한다고 가정해 보겠습니다 . ISTM의mypager "ps ax"
경우 어떤 명령이 실행되고 있는지 주의 깊게 살펴봐야 합니다 .
4. 복잡한 명령/참조
echo {1..10000}
분명히 예제 명령일 뿐이며ps ax
그다지 좋지는 않습니다. 그냥 하고 싶은 게 있으면 어떡하지?작은좀 더 현실적이죠ps ax | grep oracle
? 당신이 입력하면mypager ps ax | grep oracle
실행되고
mypager ps ax
출력이 파이프됩니다grep oracle
. 따라서 from 의 출력 길이ps ax
가 30줄이면 from 의 출력 길이가 3줄에 불과하더라도 호출됩니다mypager
. 이것이 더 극적인 방식으로 실패하는 경우가 있을 수 있습니다.less
ps ax | grep oracle
따라서 이전에 보여드린 대로 수행해야 합니다.
mypager "ps ax | grep oracle"
그러나
RET="$($@)"
이것을 처리할 수 없습니다. 물론 이런 일을 처리할 수 있는 방법도 있지만 권장되지 않습니다.캡처하려는 출력의 명령줄이 더 복잡하다면 어떻게 될까요? 예를 들어,
명령 1 "아르기닌1” | 명령 2 '아르기닌 2'$'아르기닌 3'
매개변수에는 공백, 탭, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , ,
$
, , , , , , , , , , , , , , , , , , , , , , , , , , , , 및 아마도 의 혼란스러운 조합일 수도 있습니다. 이와 같은 명령은 셸에 직접 올바르게 입력하기 어려울 수 있습니다. 이제 겪어야 할 악몽을 상상해 보세요|
\
<
>
*
;
&
[
]
(
)
`
'
"
인용하다에 매개변수로 전달합니다mypager
.
답변2
해당 옵션도 사용해야 하지만 이것이 바로 -F
옵션 의 목적입니다 . 그렇지 않으면 해당 옵션이 있는 터미널의 대체 화면에 텍스트가 인쇄됩니다(종료 후에는 언제든지 옵션을 사용할 수 없음을 의미 ). 앞으로는 바뀔 수도 있기 때문에less
-X
less
현재 텍스트가 한 화면에 들어갈 때 -X를 암시하는 개선 요청이 있습니다 -F
(303).그리고RedHat 시스템에는 2008년부터 이에 대한 패치가 있었던 것으로 보입니다.(아직 업스트림은 아니지만 (2017-09-14 기준으로 방금 이메일을 보냈습니다.)[이메일 보호됨]그것에 대해)).
그래서:
cmd | less -RXF
출력이 너무 길어도 백업 화면을 계속 사용하려면 약간의 트릭을 수행해야 합니다(위의 RedHat 패치가 없는 시스템에서).
page() {
L=${LINES:-$(tput lines)} C=${COLUMNS:-$(tput cols)} \
perl -Mopen=locale -MText::Tabs -MText::CharWidth=mbswidth -e '
while(<STDIN>) {
if ($pager) {
print $pager $_;
} else {
chomp(my $line = $_);
$line =~ s/\e\[[\d;]*m//g;
$l += 1 + int(mbswidth(expand($line)) / $ENV{C});
$buf .= $_;
if ($l > $ENV{L}) {
open $pager, "|-", "less", "-R", @ARGV or die "pager: $!";
print $pager $buf;
}
}
}
print $buf unless $pager;' -- "$@"
}
다음과 같이 사용됩니다:
cmd | page
또는
page < file
page -S < file...
(아니요 page file
, 표준 입력 페이징을 위한 것입니다.)
주어진 텍스트 줄을 표시하는 터미널 줄 수를 결정할 수 있도록 색상 이스케이프 시퀀스를 제거하고 탭을 확장하고 너비를 계산하여 출력 길이를 추측하려고 합니다.
출력에 다른 이스케이프 시퀀스나 잘못 제어/인코딩된 문자가 없으면 작동합니다.
또한 RedHat 패치와 한 가지 중요한 차이점이 있습니다. 단일 화면 출력의 경우 출력은 less
후처리(예: 반전된 비디오의 제어 문자 렌더링, ^X
빈 줄 압축 등)를 거치지 않습니다. -s
이는 여기서 요구되는 수준에 더 가깝지만 실제로는 이상적이지 않을 수 있습니다.
표준 모듈 중 하나가 아닌 Text::CharWidth 모듈을 설치해야 할 수도 있습니다( libtext-charwidth-perl
Debian의 패키지).