[bash] bash에서 특수 변수를 수동으로 확장하는 방법 (예 : ~ 물결표)

내 bash 스크립트에 값이 다음과 같은 변수가 있습니다.

~/a/b/c

확장되지 않은 물결표입니다. 이 변수에 대해 ls -lt를 수행하면 ($ VAR이라고 함) 그러한 디렉토리가 없습니다. bash 가이 변수를 실행하지 않고 해석 / 확장하도록하고 싶습니다. 즉, bash가 eval을 실행하고 평가 된 명령은 실행하지 않기를 원합니다. bash에서 가능합니까?

확장하지 않고 스크립트에 어떻게 전달 했습니까? 나는 큰 따옴표로 둘러싼 논쟁을 통과시켰다.

이 명령을 사용하여 무슨 뜻인지 확인하십시오.

ls -lt "~"

이것이 바로 내가 처한 상황입니다. 물결표가 확장되기를 원합니다. 다시 말해,이 두 명령을 동일하게하기 위해 마법을 대체해야하는 것은 무엇입니까?

ls -lt ~/abc/def/ghi

ls -lt $(magic "~/abc/def/ghi")

~ / abc / def / ghi가 존재할 수도 있고 존재하지 않을 수도 있습니다.



답변

StackOverflow의 특성으로 인해이 답변을 받아 들일 수는 없지만이 게시물을 게시 한 후 5 년 동안 저의 초보적이고 꽤 나쁜 답변보다 훨씬 더 나은 답변이있었습니다 (어려서 죽이지 마십시오) 나를).

이 스레드의 다른 솔루션은 더 안전하고 더 나은 솔루션입니다. 바람직하게는 다음 두 가지 중 하나를 선택합니다.


역사적인 목적을위한 독창적 인 답변 (그러나 이것을 사용하지 마십시오)

내가 실수 "~"하지 않으면 리터럴 문자열로 취급되기 때문에 bash 스크립트로 확장되지 않습니다 "~". eval이런 식으로 확장을 강제 할 수 있습니다 .

#!/bin/bash

homedir=~
eval homedir=$homedir
echo $homedir # prints home path

또는 ${HOME}사용자의 홈 디렉토리를 원할 경우 사용 하십시오.


답변

var사용자가 변수 를 입력 한 경우 다음을 사용하여 물결표를 확장하는 데 사용 eval해서는 안됩니다 .

eval var=$var  # Do not use this!

그 이유는 다음과 같습니다. 사용자는 우연히 (또는 목적에 따라) 유형으로 예를 들어 var="$(rm -rf $HOME/)"재앙이 발생할 수 있습니다.

더 좋고 안전한 방법은 Bash 매개 변수 확장을 사용하는 것입니다.

var="${var/#\~/$HOME}"


답변

다음 과 관련된 보안 위험없이이를 강력하게 수행 하기 위해 사전 답변 에서 나 자신을 표명합니다 eval.

expandPath() {
  local path
  local -a pathElements resultPathElements
  IFS=':' read -r -a pathElements <<<"$1"
  : "${pathElements[@]}"
  for path in "${pathElements[@]}"; do
    : "$path"
    case $path in
      "~+"/*)
        path=$PWD/${path#"~+/"}
        ;;
      "~-"/*)
        path=$OLDPWD/${path#"~-/"}
        ;;
      "~"/*)
        path=$HOME/${path#"~/"}
        ;;
      "~"*)
        username=${path%%/*}
        username=${username#"~"}
        IFS=: read -r _ _ _ _ _ homedir _ < <(getent passwd "$username")
        if [[ $path = */* ]]; then
          path=${homedir}/${path#*/}
        else
          path=$homedir
        fi
        ;;
    esac
    resultPathElements+=( "$path" )
  done
  local result
  printf -v result '%s:' "${resultPathElements[@]}"
  printf '%s\n' "${result%:}"
}

…로 사용 …

path=$(expandPath '~/hello')

또는 eval신중하게 사용하는 간단한 접근 방식 :

expandPath() {
  case $1 in
    ~[+-]*)
      local content content_q
      printf -v content_q '%q' "${1:2}"
      eval "content=${1:0:2}${content_q}"
      printf '%s\n' "$content"
      ;;
    ~*)
      local content content_q
      printf -v content_q '%q' "${1:1}"
      eval "content=~${content_q}"
      printf '%s\n' "$content"
      ;;
    *)
      printf '%s\n' "$1"
      ;;
  esac
}


답변

eval을 사용하는 안전한 방법은 "$(printf "~/%q" "$dangerous_path")"입니다. 배쉬 전용입니다.

#!/bin/bash

relativepath=a/b/c
eval homedir="$(printf "~/%q" "$relativepath")"
echo $homedir # prints home path

자세한 내용은 이 질문 을 참조하십시오

또한 zsh에서 이것은 다음과 같이 간단합니다. echo ${~dangerous_path}


답변

이건 어때요:

path=`realpath "$1"`

또는:

path=`readlink -f "$1"`


답변

Birryrees와 Halloleo의 답변에 대한 확장 (장어 의도 없음) : 일반적인 접근 방식은을 사용하는 eval것이지만 >변수에 공백과 출력 리디렉션 ( ) 과 같은 몇 가지주의 사항 이 있습니다. 다음은 저에게 효과적입니다.

mypath="$1"

if [ -e "`eval echo ${mypath//>}`" ]; then
    echo "FOUND $mypath"
else
    echo "$mypath NOT FOUND"
fi

다음 각 인수로 시도하십시오.

'~'
'~/existing_file'
'~/existing file with spaces'
'~/nonexistant_file'
'~/nonexistant file with spaces'
'~/string containing > redirection'
'~/string containing > redirection > again and >> again'

설명

  • ${mypath//>}떼어 내고 >동안 파일을 소지품 수있는 자 eval.
  • eval echo ...실제 틸드 확장을하는 일이다
  • -e인수 주위의 큰 따옴표 는 공백이있는 파일 이름을 지원하기위한 것입니다.

아마도 더 우아한 해결책이 있지만 이것이 내가 생각해 낸 것입니다.


답변

나는 이것이 당신이 찾고있는 것이라고 믿습니다.

magic() { # returns unexpanded tilde express on invalid user
    local _safe_path; printf -v _safe_path "%q" "$1"
    eval "ln -sf ${_safe_path#\\} /tmp/realpath.$$"
    readlink /tmp/realpath.$$
    rm -f /tmp/realpath.$$
}

사용법 예 :

$ magic ~nobody/would/look/here
/var/empty/would/look/here

$ magic ~invalid/this/will/not/expand
~invalid/this/will/not/expand