Bash에서 모든 호출마다 카운트를 늘리고 반환하는 함수를 구현하고 싶습니다. 불행히도 이것은 하위 쉘 내부에서 함수를 호출하기 때문에 사소하지 않은 것처럼 보이므로 결과적으로 상위 쉘의 변수를 수정할 수 없습니다.
내 시도는 다음과 같습니다.
PS_COUNT=0
ps_count_inc() {
let PS_COUNT=PS_COUNT+1
echo $PS_COUNT
}
ps_count_reset() {
let PS_COUNT=0
}
이것은 다음과 같이 사용됩니다 (따라서 하위 쉘에서 함수를 호출해야합니다).
PS1='$(ps_count_reset)> '
PS2='$(ps_count_inc) '
그렇게하면 번호가 매겨진 여러 줄 프롬프트가 나타납니다.
> echo 'this
1 is
2 a
3 test'
귀엽다. 그러나 위에서 언급 한 제한으로 인해 작동하지 않습니다.
작동하지 않는 솔루션은 변수 대신 파일에 개수를 쓰는 것입니다. 그러나 이로 인해 동시에 실행중인 여러 세션간에 충돌이 발생합니다. 물론 쉘의 프로세스 ID를 파일 이름에 추가 할 수 있습니다. 그러나 많은 파일로 시스템을 어지럽히 지 않는 더 나은 솔루션이 있기를 바랍니다.
답변
귀하의 질문에 언급 한 것과 동일한 결과를 얻으려면 필요한 것은 다음과 같습니다.
PS1='${PS2c##*[$((PS2c=0))-9]}- > '
PS2='$((PS2c=PS2c+1)) > '
뒤 틀릴 필요는 없습니다. 이 두 줄은 POSIX 호환성에 가까운 것으로 가장하는 모든 쉘에서 모든 작업을 수행합니다.
- > cat <<HD
1 > line 1
2 > line $((PS2c-1))
3 > HD
line 1
line 2
- > echo $PS2c
0
그러나 나는 이것을 좋아했다. 그리고 나는 이것이 더 잘 작동하는 것의 기본을 보여주고 싶었습니다. 그래서 이것을 조금 편집했습니다. 나는 /tmp
지금 그것을 붙였다. 그러나 나는 나 자신을 위해 그것을 유지할 것이라고 생각한다. 여기 있습니다 :
cat /tmp/prompt
프롬프트 스크립트 :
ps1() { IFS=/
set -- ${PWD%"${last=${PWD##/*/}}"}
printf "${1+%c/}" "$@"
printf "$last > "
}
PS1='$(ps1)${PS2c##*[$((PS2c=0))-9]}'
PS2='$((PS2c=PS2c+1)) > '
참고 : 최근에 yash를 배웠 으므로 어제 만들었습니다. 어떤 이유로 든 %c
문자열을 사용하여 모든 인수의 첫 번째 바이트를 인쇄하지는 않습니다 . 문서는 해당 형식의 와이드 문자 확장과 관련이 있으므로 관련이있을 수 있습니다.%.1s
그게 다야. 두 가지 주요 사항이 있습니다. 그리고 이것은 다음과 같습니다 :
/u/s/m/man3 > cat <<HERE
1 > line 1
2 > line 2
3 > line $((PS2c-1))
4 > HERE
line 1
line 2
line 3
/u/s/m/man3 >
파싱 $PWD
$PS1
평가 될 때마다 구문 분석 및 인쇄$PWD
되어 프롬프트에 추가됩니다. 그러나 나는 전체$PWD
화면이 붐비 는 것을 좋아하지 않기 때문에 현재 경로에있는 모든 이동 경로의 첫 글자 만 현재 디렉토리로 내려 가기를 원합니다. 이처럼 :
/h/mikeserv > cd /etc
/etc > cd /usr/share/man/man3
/u/s/m/man3 > cd /
/ > cd ~
/h/mikeserv >
여기 몇 가지 단계가 있습니다.
IFS=/
우리는 전류를 분리해야
$PWD
하고 가장 신뢰할 수있는 방법은$IFS
split on/
입니다. 이후에는 전혀 신경 쓰지 않아도됩니다. 여기에서 모든 분할$@
은 다음과 같은 다음 명령에서 쉘의 위치 매개 변수 배열에 의해 정의됩니다 .
set -- ${PWD%"${last=${PWD##/*/}}"}
그래서 이것 좀 까다로운하지만 중요한 것은 그 우린 분할이다
$PWD
에/
상징. 또한 매개 변수 확장을 사용$last
하여 가장 왼쪽/
슬래시 와 가장 오른쪽 슬래시 사이에 값이 발생한 후 모든 것에 할당합니다 . 이런 식으로 난 그냥에있어 경우에 알고/
및이 하나가/
후$last
여전히 전체를 동일합니다$PWD
및$1
비어 있습니다. 이것은 중요하다. 또한에 할당하기 전에$last
꼬리 끝에서 벗겨 냅니다.$PWD
$@
printf "${1+%c/}" "$@"
그래서 여기 -만큼
${1+is set}
우리printf
처음%c
각 우리의 쉘의 인수 haracter – 우리가 우리의 현재의 각 디렉토리로 설정 한$PWD
– 덜 최상위 디렉토리 -에 분할/
. 따라서 기본적으로 모든 디렉토리의 첫 번째 문자를$PWD
제외하고 맨 위의 문자 만 인쇄합니다 . 이$1
설정은 전혀 설정되지 않은 경우에만 발생하지만 루트/
또는에서/
와 같이 제거 되지 않은 경우에만 발생합니다/etc
.
printf "$last > "
$last
방금 최상위 디렉토리에 할당 한 변수입니다. 이제 이것이 최상위 디렉토리입니다. 마지막 문장의 인쇄 여부를 인쇄합니다. 그리고>
좋은 측정을 위해서는 깔끔한 약간의 시간이 걸립니다 .
그러나 증가에 대해 무엇입니까?
그리고
$PS2
조건부 문제가 있습니다. 아래에서 여전히 찾을 수있는 방법을 이전에 보여주었습니다. 이것은 근본적으로 범위 문제입니다. 그러나 당신이 많은printf \b
ackspaces 를 시작하고 그들의 캐릭터 수의 균형을 맞추고 싶지 않다면 조금 더 있습니다. 그래서 나는 이것을한다 :
PS1='$(ps1)${PS2c##*[$((PS2c=0))-9]}'
다시,
${parameter##expansion}
하루를 저장합니다. 여기서 조금 이상합니다. 실제로 변수를 제거하는 동안 변수를 설정합니다. 미드 스트립으로 설정 한 새로운 값을 스트립 글로 사용합니다. 알 겠어? 우리는##*
에서 아무것도 할 수있는 마지막 문자에 대한 우리의 증가 변수의 머리에서 모두 제거[$((PS2c=0))-9]
. 우리는 이런 식으로 값을 출력하지 않도록 보장하지만 여전히 값을 할당합니다. 꽤 멋지다-나는 전에 그것을 한 적이 없다. 그러나 POSIX는 이것이 가장 수행하기 쉬운 방법임을 보증합니다.
POSIX 지정 덕분에 ${parameter} $((expansion))
이러한 정의를 평가하는 위치에 관계없이 별도의 하위 쉘로 설정하지 않고도 현재 쉘에 이러한 정의를 유지할 수 있습니다. 그것이 작동 이유입니다 dash
및 sh
단지뿐만 아니라 그것은에서와 같이 bash
와 zsh
. 쉘 / 터미널 종속 이스케이프를 사용하지 않고 변수 자체를 테스트 할 수 있습니다. 그것이 휴대용 코드를 빠르게 만드는 것 입니다.
나머지는 상당히 간단합니다. 매번 카운터를 다시 증가시킬 $PS2
때까지 카운터를 증가시킵니다 $PS1
. 이처럼 :
PS2='$((PS2c=PS2c+1)) > '
그래서 지금 할 수 있습니다 :
대시 데모
ENV=/tmp/prompt dash -i
/h/mikeserv > cd /etc
/etc > cd /usr/share/man/man3
/u/s/m/man3 > cat <<HERE
1 > line 1
2 > line 2
3 > line $((PS2c-1))
4 > HERE
line 1
line 2
line 3
/u/s/m/man3 > printf '\t%s\n' "$PS1" "$PS2" "$PS2c"
$(ps1)${PS2c##*[$((PS2c=0))-9]}
$((PS2c=PS2c+1)) >
0
/u/s/m/man3 > cd ~
/h/mikeserv >
SH 데모
bash
또는 에서 동일하게 작동합니다 sh
.
ENV=/tmp/prompt sh -i
/h/mikeserv > cat <<HEREDOC
1 > $( echo $PS2c )
2 > $( echo $PS1 )
3 > $( echo $PS2 )
4 > HEREDOC
4
$(ps1)${PS2c##*[$((PS2c=0))-9]}
$((PS2c=PS2c+1)) >
/h/mikeserv > echo $PS2c ; cd /
0
/ > cd /usr/share
/u/share > cd ~
/h/mikeserv > exit
위에서 말했듯이 주요 문제는 계산을 수행하는 위치를 고려해야한다는 것입니다. 부모 셸에서 상태를 얻지 못하므로 계산하지 않습니다. 서브 쉘에서 상태를 얻습니다. 그래서 계산하는 곳입니다. 그러나 부모 쉘에서 정의를 수행합니다.
ENV=/dev/fd/3 sh -i 3<<\PROMPT
ps1() { printf '$((PS2c=0)) > ' ; }
ps2() { printf '$((PS2c=PS2c+1)) > ' ; }
PS1=$(ps1)
PS2=$(ps2)
PROMPT
0 > cat <<MULTI_LINE
1 > $(echo this will be line 1)
2 > $(echo and this line 2)
3 > $(echo here is line 3)
4 > MULTI_LINE
this will be line 1
and this line 2
here is line 3
0 >
답변
이 방법 (서브 쉘에서 실행되는 기능)을 사용하면 뒤틀림없이 마스터 셸 프로세스의 상태를 업데이트 할 수 없습니다. 대신, 마스터 프로세스에서 기능이 실행되도록 정렬하십시오.
PROMPT_COMMAND
변수 의 값은 PS1
프롬프트 를 인쇄하기 전에 실행되는 명령으로 해석됩니다 .
에 대해서는 PS2
비교할 것이 없습니다. 그러나 대신 트릭을 사용할 수 있습니다. 원하는 것은 산술 연산이기 때문에 서브 쉘이 포함되지 않은 산술 확장을 사용할 수 있습니다.
PROMPT_COMMAND='PS_COUNT=0'
PS2='$((++PS_COUNT)) '
산술 계산 결과가 프롬프트에 표시됩니다. 숨기려면 존재하지 않는 배열 첨자로 전달할 수 있습니다.
PS1='${nonexistent_array[$((PS_COUNT=0))]}\$ '
답변
약간 I / O 집약적이지만 카운트 값을 유지하려면 임시 파일을 사용해야합니다.
ps_count_inc () {
read ps_count < ~/.prompt_num
echo $((++ps_count)) | tee ~/.prompt_num
}
ps_count_reset () {
echo 0 > ~/.prompt_num
}
쉘 세션마다 별도의 파일이 필요하다면 (사소한 우려로 보입니다. 실제로 두 개의 다른 쉘에 여러 줄 명령을 실제로 입력 할 것입니까?), mktemp
각각에 대해 새 파일을 만드는 데 사용해야 합니다 사용하다.
ps_count_reset () {
rm -f "$prompt_count"
prompt_count=$(mktemp)
echo 0 > "$prompt_count"
}
ps_count_inc () {
read ps_count < "$prompt_count"
echo $((++ps_count)) | tee "$prompt_count"
}
답변
당신은 할 수없는 쉘 변수이 방법을 사용하고 당신은 이미 그 이유를 이해합니다. A는 상속 변수에게 프로세스가 환경을 상속 정확히 같은 방식으로 서브 쉘 : 변경 사항을 적용했다 단지 그것과 자식과되지 않은 조상 프로세스.
다른 답변에 따르면 가장 쉬운 방법은 해당 데이터를 파일에 보관하는 것입니다.
echo $count > file
count=$(<file)
기타.
답변
참고로, 다음은 쉘 파일마다 고유하고 가능한 한 빨리 삭제되는 임시 파일을 사용하는 솔루션입니다 (질문에서 암시되는 것처럼 혼란을 피하기 위해).
# Yes, I actually need this to work across my systems. :-/
_mktemp() {
local tmpfile="${TMPDIR-/tmp}/psfile-$$.XXX"
local bin="$(command -v mktemp || echo echo)"
local file="$($bin "$tmpfile")"
rm -f "$file"
echo "$file"
}
PS_COUNT_FILE="$(_mktemp)"
ps_count_inc() {
local PS_COUNT
if [[ -f "$PS_COUNT_FILE" ]]; then
let PS_COUNT=$(<"$PS_COUNT_FILE")+1
else
PS_COUNT=1
fi
echo $PS_COUNT | tee "$PS_COUNT_FILE"
}
ps_count_reset() {
rm -f "$PS_COUNT_FILE"
}