물결표 ~를 변수의 일부로 어떻게 확장할 수 있나요?

물결표 ~를 변수의 일부로 어떻게 확장할 수 있나요?

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 표준단어 확장이 다음 순서로 수행되도록 강제합니다(강조):

  1. 물결표 확장(물결표 확장 참조),매개변수 확장(매개변수 확장 참조), 명령 대체(명령 대체 참조) 및 산술 확장(산술 확장 참조)은 처음부터 끝까지 수행되어야 합니다. 토큰 인식의 항목 5를 참조하세요.

  2. IFS가 비어 있지 않으면 1단계에서 생성된 필드 부분에 대해 필드 분할을 수행해야 합니다(필드 분할 참조).

  3. set -f가 적용되지 않는 한, 경로 이름 확장이 수행되어야 합니다(경로 이름 확장 참조).

  4. 견적 삭제(견적 삭제 참조)는 항상 마지막에 수행되어야 합니다.

우리가 관심을 갖는 유일한 점은 첫 번째 점입니다. 보시다시피 물결표 확장은 인수 확장 전에 처리됩니다.

  1. 쉘은 의 물결표 확장을 시도 echo $x하지만 물결표를 찾지 못하여 계속합니다.
  2. 쉘은 매개변수 확장을 시도하고 echo $x찾아서 $x확장하며 명령줄은 echo ~/someDirectory.
  3. 처리가 계속되고 물결표 확장이 처리되었으며 ~문자는 그대로 유지됩니다.

를 할당할 때 따옴표를 사용하면 $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#"~/"}"확인합니다.

  1. ${HOME}그리고 ${x#"~/"}확장되어야 합니다.
  2. ${HOME}변수의 내용으로 확장됩니다 $HOME.
  3. ${x#"~/"}중첩된 확장을 트리거합니다. "~/"구문 분석되었지만 인용된 경우 리터럴 1 로 처리됩니다 . 여기서 작은따옴표를 사용하면 동일한 결과를 얻을 수 있습니다.
  4. ${x#"~/"}이제 표현식 자체가 확장되어 ~/값에서 접두사가 제거 됩니다 $x.
  5. 위의 결과는 이제 확장 ${HOME}, 리터럴 /, 확장으로 연결됩니다 ${x#"~/"}.
  6. 최종 결과는 큰따옴표로 묶여 단어 분할을 기능적으로 방지합니다. 여기서는 이러한 큰따옴표가 기술적으로 필요하지 않기 때문에 기능적이라고 말합니다(참조여기그리고거기예) 그러나 개인적으로 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에게 감사드립니다.

관련 정보