배쉬 매뉴얼

배쉬 매뉴얼

.bash_historyPROMPT_COMMAND다음 설정을 사용하여 모든 터미널 탭과 창에서 명령 기록을 활성화 하고 싶습니다 .profile.

export PROMPT_COMMAND="history -a; history -c; history -r;$PROMPT_COMMAND"

그러나 이 환경 변수가 설정되어 있는지 확인하면 다음과 같은 결과가 나타납니다.

echo $PROMPT_COMMAND
printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/\~}"

PROMPT_COMMAND이렇게 내보내면 기존 목록을 덮어쓰게 됩니까 , 아니면 내보내기 전에 값 앞에 접두사를 붙여야 $PROMPT_COMMAND합니까 ?PROMPT_COMMAND:

답변1

Google 검색 시 상위 결과 중 하나인 "PROMPT_COMMAND seperator"이 답변은 지금까지 제공된 것보다 더 많은 노력을 기울일 가치가 있다고 생각합니다.

노트:이 게시물을 준비하면서 Bash 버전 3.2, 4.4, 5.0 및 5.1을 테스트했습니다.도커) 동일한 버전의 소스 코드를 확인했습니다.gnu.org)

시작하자...


배쉬 매뉴얼

배쉬 <= v5.0

PROMPT_COMMAND 변수의 값은 이전에 확인됩니다.
Bash는 각 주요 프롬프트를 인쇄합니다. PROMPT_COMMAND가 설정되어 있고
null이 아닌 값이 있으면 마치 null이 아닌 것처럼 값을 실행합니다.
이미 명령줄에 입력되었습니다.

무슨 뜻이에요?

즉, bash는 다음과 거의 동일한 작업을 수행합니다.

eval "${PROMPT_COMMAND:-}"

따라서 이 목적에 적합한 명령의 연결/순서는 eval허용됩니다.

배쉬 >= v5.1

Bash는 배열 변수 PROMPT_COMMAND의 값을 확인합니다.
각 주요 팁을 인쇄하기 직전.

PROMPT_COMMAND의 요소가 설정되어 있고 비어 있지 않으면 Bash
이미 실행된 것처럼 각 값을 번호순으로 실행합니다.
명령줄에 입력하세요.
...
이 변수가 설정되고 배열인 경우 각 변수의 값은
set 요소는 실행될 명령으로 해석됩니다.
메인 프롬프트($PS1)를 인쇄하기 전.

설정되었지만 배열 변수가 아닌 경우 해당 값이 사용됩니다.
실행될 명령으로.

무슨 뜻이에요?

즉, bash는 다음과 거의 동일한 작업을 수행합니다.

if [[ ${#PROMPT_COMMAND[@]} -gt 1 ]]; then
    for cmd in "${PROMPT_COMMAND[@]}"; do eval "${cmd:-}"; done
else
    eval "${PROMPT_COMMAND:-}"
fi

즉, 배열의 각 요소는 eval독립적 입니다.

커뮤니티는 무엇을 하고 있나요?

수색"PROMPT_COMMAND"github에 표시됨:

  • 일반적으로 커뮤니티에서는 ;구분 기호로 사용합니다.
  • 경우에 따라 $'\n'구분 기호 로 사용됨

내가 구체적으로 본 것은

몇 가지 일화 참고 사항:

실제로 작동하는 모습을 봅시다

먼저 새로운 Bash v3.2 환경을 구축해 보겠습니다.

docker run -it --rm bash:3.2
bash-3.2$ 

세미콜론으로 구분된 문자열

-로 구분된 문자열을 사용하여 테스트하면 ;다음이 생성됩니다.

bash-3.2$ PROMPT_COMMAND='printf 1;printf 2;printf 3;printf ":\n"'
123:
bash-3.2$ 

줄로 구분된 문자열

-로 구분된 문자열을 사용하여 이 결과를 테스트합니다 \n.

bash-3.2$ PROMPT_COMMAND=$'printf 1\nprintf 2\nprintf 3\nprintf ":\n"'
123:
bash-3.2$ 

둘 다 섞는다

두 구분 기호를 모두 혼합할 수 있나요? 옙! :

bash-3.2$ PROMPT_COMMAND=$'printf 1;printf 2\nprintf 3;printf ":\n"'
123:
bash-3.2$ 

배열은 어떻습니까?

우리가 가는 곳에서는 Bash v5.1이 필요합니다.

docker run -it --rm bash:5.1
bash-5.1$ 

리터럴 배열

리터럴 배열을 사용하여 이 결과를 테스트합니다.

bash-5.1$ PROMPT_COMMAND=( "printf 1" "printf 2" "printf 3" "printf ':\n'" )
123:
bash-5.1$ 
세 가지 모두 사용해보세요!

모두 섞으면 어떻게 될까요?

bash-5.1$ PROMPT_COMMAND=( "printf 1" $'printf 2\nprintf 3' 'printf 4;printf ":\n"' )
1234:
bash-5.1$ 

어느 것을 사용할 것인가?

${PROMPT_COMMAND}배열인 경우 배열에 명령을 추가/추가해야 합니다 .

참고 원하는 경우 명령 그룹(예: 단일 ;또는 $'\n'구분된 명령 문자열)을 배열에 개별 항목으로 추가할 수 있습니다.

${PROMPT_COMMAND}배열이 아닌 경우 다음을 수행해야 합니다.아마도;좀 더 공식적인 구분 기호처럼 느껴지므로 명령을 추가할 때 사용하세요 . 하지만 추가하는 명령은 $'\n'.'으로 구분된 단계를 포함할 수 있는 더 복잡한 스크립트일 수 있다는 점에 유의하세요.

씹기 기능을 사용해 볼까요?

틀림없이!

##
# pc_munge munges PROMPT_COMMAND.
# Tries to accommodate when PROMPT_COMMAND is an array (supported in Bash v5.1+).
# If ${#PROMPT_COMMAND[@]} has 2+ elements, then we treat as an array, otherwise
# we defensively treat it as a string.
# By default, uses ';' as separator.
#
# NOTE: Does NOT check if command is already present
#
# Parms:
#  $1 command to add
#  $2 before | after (default: after)
#  $3 separator (default: ';')
#
pc_munge() {
    [[ -n "$1" ]] || return
    local fs="${3:-;}" # null | '' => default
    case "${2:-after}" in
        before)
            [[ ${#PROMPT_COMMAND[@]} -gt 1 ]] && PROMPT_COMMAND=( "$1" "${PROMPT_COMMAND[@]}" ) || PROMPT_COMMAND="${1}${PROMPT_COMMAND:+${fs}$PROMPT_COMMAND}"
            ;;
        *) # after
            [[ ${#PROMPT_COMMAND[@]} -gt 1 ]] && PROMPT_COMMAND+=( "$1" ) || PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND${fs}}${1}"
            ;;
    esac
}

[편집: 철자 오류 "PROMP_" => "PROMPT_"]

[편집: 오타 "Som" => "일부"]

[편집: "노래하다" 철자 오류 => "싱글"]

답변2

$PROMPT_COMMAND는 콜론으로 구분된 목록입니까?

테스트하기 쉽습니다.

$ PROMPT_COMMAND='true:true' bash 
bash: true:true: command not found
$ exit

따라서 대답은 "아니요"입니다.

하지만 당신은 그것을 다음과 같이 생각할 수 있습니다세미콜론다른 쉘 코드 행과 마찬가지로 구분된 명령 시퀀스:

$ PROMPT_COMMAND='echo x;echo y' bash 
x
y
$ exit

귀하의 질문에 있는 작업에는 세미콜론으로 구분된 여러 명령이 있고 PROMPT_COMMAND끝에 이전 값이 추가되어 있습니다.

물론 PROMPT_COMMAND여러 명령을 실행하는 또 다른 방법은 함수를 만들고 거기에서 호출하는 것입니다.

즉, 두 가지 이유로 printf시퀀스가 ​​실제 프롬프트에 더 잘 배치되어 보일 수 있습니다. PROMPT_COMMAND첫째, 개행 문자로 끝나지 않기 때문에 종료하기 전에 불완전한 줄을 출력하는 다른 명령처럼 커서 위치에 대한 Bash의 이해를 망칠 수 있습니다. 둘째, 탭 완성을 통해 쉘에 프롬프트를 다시 인쇄하도록 요청하면 PS1프롬프트가 다시 표시되지만 PROMPT_COMMAND다시 실행되지는 않습니다.

답변3

bash $PROMPT_COMMAND(또는 @DavidFarrell이 이미 지적한 대로 bash 5.1+의 배열인 경우 각 요소)는 쉘 코드로 해석되므로 해당 내용을 쉘 스크립트처럼 작성할 수 있습니다. 쉘 스크립트에서 명령은 ;, newline, |, &, &&등 으로 구분할 수 있습니다. ||각 명령은 고유한 의미를 갖습니다.

예를 들면 다음과 같습니다.

PROMPT_COMMAND='
  cmd1 && cmd2 &
  cmd3; cmd4
  for cmd in cmd5 cmd6; do
    "$cmd"
  done
  cmd7 << "EOF"
foo bar
EOF'

등.

이미 설정된 명령에 명령을 추가 $PROMPT_COMMAND하고 이전 명령과 독립적으로 실행되도록 하려면 다음을 선택 ;하고새로운 팀, 그러나 처음에 비어 있거나 이 문서 구분 기호(위의 것과 같은)로 끝나는 특수한 경우에는 작동하지 않습니다 ;.$PROMPT_COMMANDEOF

PROMPT_COMMAND+='
  your extra command here'

(또는 이전 버전의 경우:

PROMPT_COMMAND=$PROMPT_COMMAND'
  your extra command here'

)

선호됩니다. 또는:

PROMPT_COMMAND='your extra command here
'$PROMPT_COMMAND

먼저 실행되도록 미리 추가하세요.

Bash 5.1+에서는 배열 요소를 추가할 수도 있습니다.

PROMPT_COMMAND+=(
  'your extra command here'
)

아니면 앞에 추가하세요:

PROMPT_COMMAND=(
  'your extra command here'
  "${PROMPT_COMMAND[@]}"
)

이는 잠재적인 문제를 남깁니다. errexit( -e) 옵션이 활성화되어 있고 이러한 스크립트의 명령이 실패하면 처리가 중지되므로 추가 명령이 실행되지 않을 수 있습니다. 하지만 이는 아마도 병적인 사례로 보아야 할 것입니다. 아마도 대화형 쉘에서 사용하는 것은 errexit좋지 않은 생각일 것입니다 (스크립트에서 사용될 때 이미 논란의 여지가 있습니다).

비교를 위해 에서는 zsh대신 $PROMPT_COMMAND유사한 이름에서 영감을 받은(및 개선된) 다른 지점에서 호출되는 후크 함수가 있습니다.특별한 별칭존재하다 tcsh. precmd()각 프롬프트 전에 호출되도록 정의하려는 함수입니다. 에서는 호출할 추가 함수가 포함된 특수 배열을 설정할 zsh수도 있습니다 ( 처리에도 영향을 미침).precmd_functionserrexit


nounset¹처리 중에 구문 오류나 치명적인 오류가 발생하는 경우 이 옵션에서도 동일한 현상이 발생할 수 있습니다.

관련 정보