source

bash에서 특수 변수(예: ~ 타일드)를 수동으로 확장하는 방법

factcode 2023. 5. 24. 22:28
반응형

bash에서 특수 변수(예: ~ 타일드)를 수동으로 확장하는 방법

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

~/a/b/c

확장되지 않은 타일입니다.이 변수($VAR이라고 함)에 대해 -lt를 수행하면 해당 디렉터리가 표시되지 않습니다.bash가 이 변수를 실행하지 않고 해석/확장할 수 있도록 합니다.다시 말해, 나는 bash가 eval을 실행하고 평가된 명령을 실행하지 않기를 원합니다.이것이 bash에서 가능합니까?

확장하지 않고 어떻게 스크립트에 전달할 수 있었을까요?저는 그 주장을 이중따옴표로 둘러싸서 통과시켰습니다.

이 명령을 사용하여 의미를 확인합니다.

ls -lt "~"

이것이 바로 제가 처한 상황입니다.저는 타일이 확장되기를 원합니다.즉, 이 두 명령을 동일하게 만들려면 마법을 무엇으로 대체해야 합니까?

ls -lt ~/abc/def/ghi

그리고.

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

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

수가변인 var한 것입니다.eval를 사용하여 타일을 확장하는 데 사용해서는 안 됩니다.

eval var=$var  # Do not use this!

로 (할 수 입니다.var="$(rm -rf $HOME/)"비참한 결과를 초래할 수도 있습니다.

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

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

StackOverflow의 특성상 이 답변을 수락하지 않도록 할 수는 없지만, 이 글을 올린 이후 5년 동안 제가 인정하는 초보적이고 꽤 나쁜 답변(저는 젊었습니다, 저를 죽이지 마세요)보다 훨씬 더 나은 답변이 있었습니다.

이 스레드의 다른 솔루션은 더 안전하고 더 나은 솔루션입니다.다음 두 가지 중 하나를 사용하는 것이 좋습니다.


역사적인 목적을 위한 원본 답변(단, 이를 사용하지 마십시오.

틀리지 , 내가틀않면다았지리,면."~"bash 스크립트는 리터럴 문자열로 처리되므로 이러한 방식으로 확장되지 않습니다."~"다음을 통해 강제로 확장할 수 있습니다.eval이것처럼.

#!/bin/bash

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

또는 사용하기만 하면 됩니다.${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
}

이거 어때:

path=`realpath "$1"`

또는:

path=`readlink -f "$1"`

eval을 안전하게 사용하는 방법은 다음과 같습니다."$(printf "~/%q" "$dangerous_path")"이는 bash별로 다릅니다.

#!/bin/bash

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

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

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

여기에 터무니없는 해결책이 있습니다.

$ echo "echo $var" | bash

이 명령의 기능에 대한 설명:

  1. bash의 새합니다. bash,calling 르의해새로의운스부다만기.bash;
  2. 현을잡라는 합니다."echo $var" 및대품으로 합니다.$var변수 값(대체 후 문자열에 타일이 포함됨);
  3. 합니다. 서는 2단계에서 생성된 bash 인스턴스를 1단계 bash 인스턴스라고 . 여기서 호출합니다.echo그리고 그것의 출력을 파이프로 연결합니다.|성격.

으로 현재되며 "bash" 합니다."echo ~..."우리를 위하여

Birryree와 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인수는 공백이 있는 파일 이름을 지원합니다.

어쩌면 더 우아한 해결책이 있을지도 모르지만, 이것이 제가 생각해 낼 수 있었던 것입니다.

gentent와 함께 사용자의 홈 디렉토리를 얻는 것에 대해 직접 탐구하는 것은 어떻습니까?

$ getent passwd mike | cut -d: -f6
/users/mike

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

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

다음은 Hókon Hägland의 Bash 답변에 해당하는 POSIX 함수입니다.

expand_tilde() {
    tilde_less="${1#\~/}"
    [ "$1" != "$tilde_less" ] && tilde_less="$HOME/$tilde_less"
    printf '%s' "$tilde_less"
}

2017-12-10 추가: 추가'%s'@Charles Duffy 댓글따.

제 솔루션은 다음과 같습니다.

#!/bin/bash


expandTilde()
{
    local tilde_re='^(~[A-Za-z0-9_.-]*)(.*)'
    local path="$*"
    local pathSuffix=

    if [[ $path =~ $tilde_re ]]
    then
        # only use eval on the ~username portion !
        path=$(eval echo ${BASH_REMATCH[1]})
        pathSuffix=${BASH_REMATCH[2]}
    fi

    echo "${path}${pathSuffix}"
}



result=$(expandTilde "$1")

echo "Result = $result"

가장 간단합니다. '매직'을 '평가 에코'로 대체합니다.

$ eval echo "~"
/whatever/the/f/the/home/directory/is

문제:평가는 사악하기 때문에 다른 변수와 문제가 발생할 것입니다.예를 들어:

$ # home is /Users/Hacker$(s)
$ s="echo SCARY COMMAND"
$ eval echo $(eval echo "~")
/Users/HackerSCARY COMMAND

첫 번째 확장에서는 주입 문제가 발생하지 않습니다.그래서 만약 당신이 단순히 대체한다면,magic와 함께eval echo당신은 괜찮을 겁니다.하지만 만약 당신이echo $(eval echo ~)주사에 취약할 수 있습니다.

마찬가지로, 만약 당신이 한다면,eval echo ~eval echo "~"두 배로 확장되었기 때문에 즉시 주입이 가능할 것입니다.

누구나 참고할 수 있도록 python의 os.path.expanduser() 동작을 모방하는 함수(평가 사용 없음):

# _expand_homedir_tilde ~/.vim
/root/.vim
# _expand_homedir_tilde ~myuser/.vim
/home/myuser/.vim
# _expand_homedir_tilde ~nonexistent/.vim
~nonexistent/.vim
# _expand_homedir_tilde /full/path
/full/path

그리고 기능:

function _expand_homedir_tilde {
    (
    set -e
    set -u
    p="$1"
    if [[ "$p" =~ ^~ ]]; then
        u=`echo "$p" | sed 's|^~\([a-z0-9_-]*\)/.*|\1|'`
        if [ -z "$u" ]; then
            u=`whoami`
        fi

        h=$(set -o pipefail; getent passwd "$u" | cut -d: -f6) || exit 1
        p=`echo "$p" | sed "s|^~[a-z0-9_-]*/|${h}/|"`
    fi
    echo $p
    ) || echo $1
}

공백이 있는 경로에 대한 Birryree의 답변을 확장하기 위해:사용할 수 없습니다.eval공간별로 평가를 구분하기 때문에 그대로 명령합니다.한 가지 해결책은 eval 명령에 대한 공백을 임시로 바꾸는 것입니다.

mypath="~/a/b/c/Something With Spaces"
expandedpath=${mypath// /_spc_}    # replace spaces 
eval expandedpath=${expandedpath}  # put spaces back
expandedpath=${expandedpath//_spc_/ }
echo "$expandedpath"    # prints e.g. /Users/fred/a/b/c/Something With Spaces"
ls -lt "$expandedpath"  # outputs dir content

이 예는 물론 다음과 같은 가정에 의존합니다.mypath 시퀀스 문자시를포않지다습니하함스가 되어 있지 않습니다."_spc_".

이것은 파이썬에서 더 쉽게 찾을 수 있습니다.

unix 명령줄에서:

python -c 'import os; import sys; print os.path.expanduser(sys.argv[1])' ~/fred

결과:

/Users/someone/fred

- bash로 합니다.test.sh:

#!/usr/bin/env bash

thepath=$(python -c 'import os; import sys; print os.path.expanduser(sys.argv[1])' $1)

echo $thepath

중입니다.bash ./test.sh결과:

/Users/someone/fred

- 유리티로 - 장저로 expanduser실행 권한이 있는 경로의 어딘가:

#!/usr/bin/env python

import sys
import os

print os.path.expanduser(sys.argv[1])

다음 명령행에서 사용할 수 있습니다.

expanduser ~/fred

또는 스크립트에서:

#!/usr/bin/env bash

thepath=$(expanduser $1)

echo $thepath

그냥 사용하기eval정확하게: 유효성 검사 포함.

case $1${1%%/*} in
([!~]*|"$1"?*[!-+_.[:alnum:]]*|"") ! :;;
(*/*)  set "${1%%/*}" "${1#*/}"       ;;
(*)    set "$1" 
esac&& eval "printf '%s\n' $1${2+/\"\$2\"}"

나는 (다른 것들 중에서) read -e를 사용하여 경로에서 읽은 후 변수 매개변수 대체를 사용하여 이 작업을 수행했습니다.따라서 사용자는 경로를 탭 완료할 수 있으며 ~ 경로를 입력하면 정렬됩니다.

read -rep "Enter a path:  " -i "${testpath}" testpath 
testpath="${testpath/#~/${HOME}}" 
ls -al "${testpath}" 

추가적인 이점은 타일이 없으면 변수에 아무 일도 일어나지 않으며 타일이 있지만 첫 번째 위치에 있지 않으면 무시된다는 것입니다.

(저는 루프에서 사용하기 때문에 문제가 있을 때 경로를 수정할 수 있도록 -i를 읽기 위해 포함합니다.)

어떤 이유로 문자열이 이미 인용되었을 때 Perl만 하루를 저장합니다.

  #val="${val/#\~/$HOME}" # for some reason does not work !!
  val=$(echo $val|perl -ne 's|~|'$HOME'|g;print')

나는 생각합니다.

thepath=( ~/abc/def/ghi )

다른 모든 솔루션보다 쉽습니다.아니면 내가 뭔가를 놓쳤나요?경로가 실제로 존재하지 않더라도 작동합니다.

언급URL : https://stackoverflow.com/questions/3963716/how-to-manually-expand-a-special-variable-ex-tilde-in-bash

반응형