bash 프롬프트를 열고 다음을 입력하면:
$ set -o xtrace
$ x='~/someDirectory'
+ x='~/someDirectory'
$ echo $x
+ echo '~/someDirectory'
~/someDirectory
위의 다섯번째 줄이 사라졌으면 좋겠습니다 + echo /home/myUsername/someDirectory
. 이를 수행할 수 있는 방법이 있습니까? 원래 Bash 스크립트에서 변수 x는 실제로 다음과 같은 루프를 통해 입력 파일의 데이터로 채워집니다.
while IFS= read line
do
params=($line)
echo ${params[0]}
done <"./someInputFile.txt"
그래도 echo '~/someDirectory'
대신 echo /home/myUsername/someDirectory
.
답변1
이것POSIX 표준단어 확장이 다음 순서로 수행되도록 강제합니다(강조):
물결표 확장(물결표 확장 참조),매개변수 확장(매개변수 확장 참조), 명령 대체(명령 대체 참조) 및 산술 확장(산술 확장 참조)은 처음부터 끝까지 수행되어야 합니다. 토큰 인식의 항목 5를 참조하세요.
IFS가 비어 있지 않으면 1단계에서 생성된 필드 부분에 대해 필드 분할을 수행해야 합니다(필드 분할 참조).
set -f가 적용되지 않는 한, 경로 이름 확장이 수행되어야 합니다(경로 이름 확장 참조).
견적 삭제(견적 삭제 참조)는 항상 마지막에 수행되어야 합니다.
우리가 관심을 갖는 유일한 점은 첫 번째 점입니다. 보시다시피 물결표 확장은 인수 확장 전에 처리됩니다.
- 쉘은 의 물결표 확장을 시도
echo $x
하지만 물결표를 찾지 못하여 계속합니다. - 쉘은 매개변수 확장을 시도하고
echo $x
찾아서$x
확장하며 명령줄은echo ~/someDirectory
. - 처리가 계속되고 물결표 확장이 처리되었으며
~
문자는 그대로 유지됩니다.
를 할당할 때 따옴표를 사용하면 $x
물결표가 확장되지 않고 일반 문자로 처리되지 않도록 명시적으로 요청하는 것입니다. 종종 간과되는 한 가지는 쉘 명령에서 전체 문자열을 인용할 필요가 없으므로 변수 할당 중에 확장할 수 있다는 것입니다.
user@host:~$ set -o xtrace
user@host:~$ x=~/'someDirectory'
+ x=/home/user/someDirectory
user@host:~$ echo $x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
또한 확장이 echo
발생하는 한 명령줄에서 확장이 발생하도록 할 수도 있습니다.앞으로매개변수 확장:
user@host:~$ x='someDirectory'
+ x=someDirectory
user@host:~$ echo ~/$x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
어떤 이유로 $x
확장 없이 변수의 물결표에 영향을 주고 명령에서 확장할 수 있어야 하는 경우 변수가 두 번 확장되도록 echo
강제로 두 번 수행해야 합니다 .$x
user@host:~$ x='~/someDirectory'
+ x='~/someDirectory'
user@host:~$ echo "$( eval echo $x )"
++ eval echo '~/someDirectory'
+++ echo /home/user/someDirectory
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
그러나 이러한 구문이 사용되는 상황에 따라 원치 않는 부작용이 있을 수 있다는 점에 유의하세요. 경험상 eval
다른 방법이 있을 때 이를 필요로 하는 항목을 사용하지 않는 것이 가장 좋습니다.
다른 종류의 확장이 아닌 물결표 문제를 구체적으로 해결하려는 경우 이 구조가 더 안전하고 이식성이 뛰어납니다.
user@host:~$ x='~/someDirectory'
+ x='~/someDirectory'
user@host:~$ case "$x" in "~/"*)
> x="${HOME}/${x#"~/"}"
> esac
+ case "$x" in
+ x=/home/user/someDirectory
user@host:~$ echo $x
+ echo /home/user/someDirectory
/home/user/someDirectory
user@host:~$
이 구조는 선행 디렉터리가 있는지 명시적으로 확인하고, ~
발견되면 이를 사용자의 홈 디렉터리로 바꿉니다.
귀하의 의견에 따르면 x="${HOME}/${x#"~/"}"
이는 쉘에서 프로그래밍하지 않은 사람들에게는 실제로 놀라운 일이지만 실제로는 위에서 인용한 것과 동일한 POSIX 규칙과 관련이 있습니다.
POSIX 표준에 따르면 인용 제거는 마지막에 발생하고 매개변수 확장은 일찍 발생합니다. 따라서 ${#"~"}
외부 제안을 평가하기 훨씬 전에 평가 및 확장됩니다. 차례로 정의된 대로매개변수 확장규칙:
단어 값이 필요한 모든 경우(아래 설명된 인수 상태에 따라) 단어는 물결표 확장, 매개변수 확장, 명령 대체 및 산술 확장을 거쳐야 합니다.
#
따라서 물결표 확장을 방지하려면 연산자의 오른쪽 부분을 올바르게 인용하거나 이스케이프해야 합니다.
즉, 쉘 인터프리터가 이를 볼 때 다음을 x="${HOME}/${x#"~/"}"
확인합니다.
${HOME}
그리고${x#"~/"}
확장되어야 합니다.${HOME}
변수의 내용으로 확장됩니다$HOME
.${x#"~/"}
중첩된 확장을 트리거합니다."~/"
구문 분석되었지만 인용된 경우 리터럴 1 로 처리됩니다 . 여기서 작은따옴표를 사용하면 동일한 결과를 얻을 수 있습니다.${x#"~/"}
이제 표현식 자체가 확장되어~/
값에서 접두사가 제거 됩니다$x
.- 위의 결과는 이제 확장
${HOME}
, 리터럴/
, 확장으로 연결됩니다${x#"~/"}
. - 최종 결과는 큰따옴표로 묶여 단어 분할을 기능적으로 방지합니다. 여기서는 이러한 큰따옴표가 기술적으로 필요하지 않기 때문에 기능적이라고 말합니다(참조여기그리고거기예) 그러나 개인적으로
a=$b
과제가 범위를 벗어나면 큰따옴표를 추가하는 것이 더 명확하다는 것을 알았습니다.
그런데 구문을 더 자세히 살펴보면 그 구성이 위에서 설명한 것과 동일한 개념에 의존한다는 case
것을 알 수 있습니다 (여기에서도 큰따옴표와 작은따옴표를 서로 바꿔서 사용할 수 있습니다)."~/"*
x=~/'someDirectory'
이러한 내용이 언뜻 보기에 모호해 보이더라도 걱정하지 마세요(아마도 두 번째 또는 그 이후에 볼 수도 있습니다!). 내 생각에는 매개변수 확장과 서브쉘은 쉘 언어로 프로그래밍할 때 마스터해야 할 가장 복잡한 개념 중 하나입니다.
일부 사람들은 크게 동의하지 않을 수도 있다는 것을 알고 있습니다. 하지만 쉘 프로그래밍에 대해 더 자세히 알고 싶다면 다음 내용을 읽어보시기 바랍니다.고급 Bash 스크립팅 가이드: Bash 스크립팅을 가르치기 때문에 POSIX 쉘 스크립팅에 비해 확장과 추가 기능이 많지만, 실용적인 예제도 많고 잘 작성되어 있는 것 같습니다. 일단 그렇게 하고 나면 필요할 때 POSIX 기능으로 제한하기가 쉬우며 개인적으로 POSIX 영역으로 바로 뛰어드는 것은 초보자에게 불필요하게 가파른 학습 곡선이라고 생각합니다(내 POSIX 물결표 교체를 변환(@m0dular의 정규식과 비교). -Bash와 유사) 무슨 뜻인지 알 수 있는 것과 동일합니다 ;)! ).
1 : 이로 인해 물결표 확장이 올바르게 구현되지 않은 Dash의 버그를 발견하게 되었습니다( x='~/foo'; echo "${x#~/}"
. 매개변수 확장은 사용자와 쉘 개발자 모두에게 복잡한 영역입니다!
답변2
가능한 대답 중 하나:
eval echo "$x"
파일에서 입력을 읽고 있으므로 이 작업을 수행하지 않습니다.
다음과 같이 ~를 $HOME 값으로 검색하고 바꿀 수 있습니다.
x='~/.config'
x=${x/#\~/${HOME}}
echo "$x"
나에게주세요:
/home/adrian/.config
편집: ${x/#\~/${HOME}}
제안 ~
에 대해 @user137369에게 감사드립니다.