Bash에서 읽기 전용 변수를 어떻게 설정 해제합니까?
$ readonly PI=3.14
$ unset PI
bash: PI: readonly variable
아니면 불가능합니까?
Bash에서 읽기 전용 변수를 어떻게 설정 해제합니까?
$ readonly PI=3.14
$ unset PI
bash: PI: readonly variable
아니면 불가능합니까?
readonly TMOUT
입니다. 나는 그 줄에 주석을 달고 그 리눅스 시스템에 대한 새로운 연결을 여는 것을 선호합니다.
답변:
실제로 읽기 전용 변수를 설정 해제 할 수 있습니다 . 그러나 나는 이것이 해키 방법이라는 것을 경고해야한다. 이 답변을 추천이 아닌 정보로만 추가하십시오. 자신의 책임하에 사용하십시오. 우분투 13.04, bash 4.2.45에서 테스트되었습니다.
이 방법은 약간의 bash 소스 코드를 아는 것과 관련 이 있으며이 답변 에서 상속됩니다 .
$ readonly PI=3.14
$ unset PI
-bash: unset: PI: cannot unset: readonly variable
$ cat << EOF| sudo gdb
attach $$
call unbind_variable("PI")
detach
EOF
$ echo $PI
$
Oneliner 대답은 F. Hauri의 대답에 제공된대로 배치 모드 및 기타 명령 줄 플래그를 사용하는 것입니다 .
$ sudo gdb -ex 'call unbind_variable("PI")' --pid=$$ --batch
sudo
커널의 ptrace_scope 설정에 따라 필요할 수도 있고 필요하지 않을 수도 있습니다. 자세한 내용은 vip9937의 답변에 대한 의견을 확인하십시오.
cat << EOF| sudo gdb
로 sudo gdb << EOF
. 그것은 할 수 없는 작업의 리디렉션 입력 제공하기 때문에 - bash
로 인해 중단되고 gdb
첨부.
echo -e "attach $$\n call unbind_variable(\"PI\")\n detach" | gdb
TMOUT을 설정 해제 (자동 로그 아웃 비활성화)하고 싶기 때문에 위의 gdb 해킹을 시도했지만 TMOUT이 읽기 전용으로 설정된 컴퓨터에서는 sudo를 사용할 수 없습니다. 하지만 bash 프로세스를 소유하고 있기 때문에 sudo가 필요하지 않습니다. 그러나 구문은 내가 사용하는 컴퓨터에서 제대로 작동하지 않았습니다.
그래도 작동했습니다 (내 .bashrc 파일에 넣었습니다).
# Disable the stupid auto-logout
unset TMOUT > /dev/null 2>&1
if [ $? -ne 0 ]; then
gdb <<EOF > /dev/null 2>&1
attach $$
call unbind_variable("TMOUT")
detach
quit
EOF
fi
-q -n
옵션을 사용하는 것이 좋습니다 . gdb
/proc/sys/kernel/yama/ptrace_scope
. 가장 일반적인 값은 0
이며,이 경우이를 수행 할 수 있으며 1
,이 경우 디버깅 되는 프로세스 의 직접적인 부모가 아닌 것처럼 할 수 없습니다gdb
bash
.
-q
및 -n
도움이되는 (즉, 그들은 -q
) 침묵하지 않는 gdb
소위, /dev/null
리디렉션이 여전히 필요하다. 좋은 제안하지만, @LucasCimon
GDB를 사용하는 것은 매우 느립니다. 대신 ctypes.sh를 시도하십시오. libffi를 사용하여 bash의 unbind_variable ()을 직접 호출하는 방식으로 작동하며, 이는 다른 bash 내장을 사용하는 것만 큼 빠릅니다.
$ readonly PI=3.14
$ unset PI
bash: unset: PI: cannot unset: readonly variable
$ source ctypes.sh
$ dlcall unbind_variable string:PI
$ declare -p PI
bash: declare: PI: not found
먼저 ctypes.sh를 설치해야합니다.
$ git clone https://github.com/taviso/ctypes.sh.git
$ cd ctypes.sh
$ ./autogen.sh
$ ./configure
$ make
$ sudo make install
전체 설명 및 문서는 https://github.com/taviso/ctypes.sh 를 참조 하십시오 .
궁금한 사람을 위해 예, 이것은 bash 내의 모든 함수 또는 bash에 연결된 라이브러리의 모든 함수 또는 원하는 경우 동적으로로드 된 외부 라이브러리를 호출 할 수 있습니다. Bash는 이제 perl만큼 위험합니다 ... ;-)
include ctypes.sh
뜻 source ctypes.sh
나 . ctypes.sh
.
zsh에서
% typeset +r PI
% unset PI
(예, bash라는 질문을 알고 있습니다.하지만 zsh를 위해 Google을 사용하면 bash 질문도 많이받습니다.)
그러나 단순한 구문으로 :
gdb -ex 'call unbind_variable("PI")' --pid=$$ --batch
기능으로 약간의 개선이 있습니다.
destroy
기능 :또는 가변 메타 데이터로 재생하는 방법 . 희귀 한 bashisms의 사용법에 유의하십시오 : local -n VARIABLE=$1
그리고 ${VARIABLE@a}
...
destroy () {
local -n variable=$1
declare -p $1 &>/dev/null || return -1 # Return if variable not exist
local reslne result flags=${variable@a}
[ -z "$flags" ] || [ "${flags//*r*}" ] && {
unset $1 # Don't run gdb if variable is not readonly.
return $?
}
while read resline; do
[ "$resline" ] && [ -z "${resline%\$1 = *}" ] &&
result=${resline##*1 = }
done < <(
gdb 2>&1 -ex 'call unbind_variable("'$1'")' --pid=$$ --batch
)
return $result
}
샘플을 위해 이것을라는 bash 소스 파일에 복사 할 수 destroy.bash
있습니다.
1 destroy () {
2 local -n variable=$1
3 declare -p $1 &>/dev/null || return -1 # Return if variable not exist
4 local reslne result flags=${variable@a}
5 [ -z "$flags" ] || [ "${flags//*r*}" ] && {
6 unset $1 # Don't run gdb if variable is not readonly.
7 return $?
8 }
9 while read resline; do
10 [ "$resline" ] && [ -z "${resline%\$1 = *}" ] &&
11 result=${resline##*1 = }
12 done < <(
13 gdb 2>&1 -ex 'call unbind_variable("'$1'")' --pid=$$ --batch
14 )
15 return $result
16 }
$flags
.unset
대신의 gdb
경우 읽기 전용 플래그 없는while read ... result= ... done
리턴 코드를 얻습니다.call unbind
gdb
gdb
의 사용과 구문 --pid
및 --ex
(참조 gdb --help
).$result
의 call unbind
명령을 사용합니다.source destroy.bash
# 1st with any regular (read-write) variable:
declare PI=$(bc -l <<<'4*a(1)')
echo $PI
3.14159265358979323844
echo ${PI@a} # flags
declare -p PI
declare -- PI="3.14159265358979323844"
destroy PI
echo $?
0
declare -p PI
bash: declare: PI: not found
# now with read only variable:
declare -r PI=$(bc -l <<<'4*a(1)')
declare -p PI
declare -r PI="3.14159265358979323844"
echo ${PI@a} # flags
r
unset PI
bash: unset: PI: cannot unset: readonly variable
destroy PI
echo $?
0
declare -p PI
bash: declare: PI: not found
# and with non existant variable
destroy PI
echo $?
255
특히 TMOUT 변수에 대한 wrt. gdb를 사용할 수없는 경우 또 다른 옵션은 bash를 홈 디렉토리에 복사하고 바이너리의 TMOUT 문자열을 XMOUX와 같은 다른 것으로 패치하는 것입니다. 그런 다음이 추가 셸 레이어를 실행하면 시간이 초과되지 않습니다.
gdb를 사용할 수없는 경우 대안 : 다음을 사용할 수 있습니다. enable
명령을 로드 내장 사용자 지정을 그건 당신이 읽기 전용 특성 설정을 해제하게됩니다. 이를 수행하는 코드의 요점 :
SETVARATTR (find_variable ("TMOUT"), att_readonly, 1);
당연히 당신 TMOUT
은 당신이 관심있는 변수로 대체 할 것 입니다.
직접 내장으로 바꾸고 싶지 않다면 GitHub에서 bash를 포크하고 완전히 작성되고 컴파일 준비가 완료된로드 가능한 내장 내장 readwrite
. 커밋은 https://github.com/josephcsible/bash/commit/bcec716f4ca958e9c55a976050947d2327bcc195에 있습니다. 사용하려면 내 커밋과 함께 Bash 소스를 가져 와서 실행 ./configure && make loadables
하여 빌드 한 다음 enable -f examples/loadables/readwrite readwrite
실행중인 세션에 추가 한 다음 readwrite TMOUT
사용하십시오.
의 매뉴얼 페이지에서 할 수 없습니다 unset
.
각 이름에 대해 해당 변수 또는 함수를 제거하십시오. 옵션이 제공되지 않거나 -v 옵션이 제공되면 각 이름은 쉘 변수를 참조합니다. 읽기 전용 변수는 설정 해제 할 수 없습니다. -f가 지정되면 각 이름은 쉘 기능을 참조하고 기능 정의가 제거됩니다. 설정되지 않은 각 변수 또는 함수는 후속 명령에 전달되는 환경에서 제거됩니다. RANDOM, SECONDS, LINENO, HISTCMD, FUNCNAME, GROUPS 또는 DIRSTACK 중 하나가 설정되지 않은 경우 이후에 재설정 되더라도 특수 속성이 손실됩니다. 이름이 읽기 전용이 아닌 경우 종료 상태는 true입니다.
typeset +r VAR
또한 man 페이지에 따르면, 이후 일을하지 않습니다Using '+' instead of '-' turns off the attribute instead, with the exception that +a may not be used to destroy an array variable.
Bash에서 읽기 전용 변수를 "설정 해제"하는 또 다른 방법은 일회용 컨텍스트에서 해당 변수를 읽기 전용으로 선언하는 것입니다.
foo(){ declare -r PI=3.14; baz; }
bar(){ local PI=3.14; baz; }
baz(){ PI=3.1415927; echo PI=$PI; }
foo;
bash : PI : 읽기 전용 변수
bar;
PI = 3.1415927
이것은 아마도 원래 저자의 의도 인 범위 내에서 "설정 해제"되지는 않지만 확실히 baz () 관점에서 변수를 읽기 전용으로 설정 한 다음 나중에 시점에서 읽기-쓰기로 만드는 것입니다. baz ()보기에서는 약간의 사전 고려를 통해 스크립트를 작성하기 만하면됩니다.
GDB 또는 외부 바이너리가 없는 또 다른 솔루션 (사실 Graham Nicholls 주석 에 대한 강조 )은 exec
.
제 경우에는 성가신 읽기 전용 변수가 /etc/profile.d/xxx
.
bash 매뉴얼 인용 :
"bash가 대화 형 로그인 쉘로 호출되면 [...] 먼저 / etc / profile 파일에서 명령을 읽고 실행합니다."[...]
로그인 셸이 아닌 대화식 셸이 시작되면 bash는 /etc/bash.bashrc [...]에서 명령을 읽고 실행합니다.
내 해결 방법의 요점은 ~/.bash_profile
다음과 같습니다.
if [ -n "$annoying_variable" ]
then exec env annoying_variable='' /bin/bash
# or: then exec env -i /bin/bash
fi
경고 : 재귀 (SSH를 통해서만 계정에 액세스 할 수있는 경우 잠김)를 방지하려면 "성가신 변수"가 bashrc에 의해 자동으로 설정되지 않도록하거나 검사에서 다른 변수를 설정해야합니다. 예를 들어 :
if [ -n "$annoying_variable" ] && [ "${SHLVL:-1}" = 1 ]
then exec env annoying_variable='' SHLVL=$((SHLVL+1)) ${SHELL:-/bin/bash}
fi