Question

J'ai une variable dans mon script bash dont la valeur est quelque chose comme ceci:

~/a/b/c

Notez qu'il est tilde non dilatée. Quand je fais ls -LT sur cette variable (appeler $ VAR), je reçois pas de répertoire. Je veux laisser bash interpréter / étendre cette variable sans l'exécuter. En d'autres termes, je veux bash à courir eval mais pas courir la commande évaluée. Est-ce possible en bash?

Comment ai-je réussi à passer ceci dans mon script sans extension? J'ai passé l'argument qui l'entoure avec des guillemets doubles.

Essayez cette commande pour voir ce que je veux dire:

ls -lt "~"

Ceci est exactement la situation que je suis. Je veux que le tilde soit élargi. En d'autres termes, que dois-je remplacer la magie avec pour rendre ces deux commandes identiques:

ls -lt ~/abc/def/ghi

et

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

Notez que ~ / abc / def / ghi peut ou peut ne pas exister.

Était-ce utile?

La solution

En raison de la nature de StackOverflow, je ne peux pas faire cette réponse unaccepted, mais dans les 5 années écoulées depuis que je posté il y a eu des réponses beaucoup mieux que ma réponse, certes rudimentaire et assez mauvais (j'étais jeune, ne me tuez pas).

Les autres solutions dans ce fil sont plus sûrs et de meilleures solutions. De préférence, je partirais avec l'une de ces deux:


Réponse originale à des fins historiques (mais s'il vous plaît ne pas utiliser)

Si je ne me trompe pas, "~" ne sera pas étendu par un script bash de cette manière parce qu'elle est traitée comme une "~" chaîne littérale. Vous pouvez forcer l'expansion via eval comme celui-ci.

#!/bin/bash

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

Sinon, il suffit d'utiliser ${HOME} si vous voulez que le répertoire personnel de l'utilisateur.

Autres conseils

Si la var variable est entrée par l'utilisateur, eval devrait pas être utilisé pour étendre le tilde en utilisant

eval var=$var  # Do not use this!

La raison en est. L'utilisateur pourrait par hasard (ou en fin) par exemple de type var="$(rm -rf $HOME/)" avec des conséquences désastreuses possibles

Une meilleure (et plus sûr) consiste à utiliser l'expansion du paramètre bash:

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

Plagarizing moi-même de de réponse avant, de le faire sans la sécurité avec vigueur les risques associés à 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%:}"
}

... utilisé comme ...

path=$(expandPath '~/hello')

Alternativement, une approche plus simple que les utilisations eval soigneusement:

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
}

Un moyen sûr d'utiliser eval est "$(printf "~/%q" "$dangerous_path")". Notez que spécifique bash.

#!/bin/bash

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

Voir cette question pour plus de détails

En outre, notez que sous zsh ce serait comme aussi simple que echo ${~dangerous_path}

Que diriez-vous ceci:

path=`realpath "$1"`

Ou:

path=`readlink -f "$1"`

L'expansion (sans jeu de mots) sur les réponses de birryree et de halloleo: L'approche générale est d'utiliser eval, mais il est livré avec quelques mises en garde importantes, à savoir les espaces et la redirection de sortie (>) dans la variable. Ce qui suit semble fonctionner pour moi:

mypath="$1"

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

Essayez avec chacun des arguments suivants:

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

Explication

  • Les bandes ${mypath//>} les caractères de > qui pourrait CLOBBER un fichier au cours de la eval.
  • Le eval echo ... est ce que fait l'expansion du tilde réelle
  • Les guillemets doubles autour de l'argument -e sont pour le soutien des noms de fichiers avec des espaces.

Peut-être il y a une solution plus élégante, mais ce que je pouvais trouver.

Je crois que c'est ce que vous cherchez

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.$$
}

Exemple d'utilisation:

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

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

Voici ma solution:

#!/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"

Il suffit d'utiliser eval correctement. Avec la validation

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

Voici la fonction POSIX équivalent de Bash Håkon Hægland réponse

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

10/12/2017 modifier:. Ajouter '%s' par @CharlesDuffy dans les commentaires

Juste pour étendre birryree de réponse pour les chemins avec des espaces: Vous ne pouvez pas utiliser la commande eval comme cela est parce qu'il seperates évaluation par des espaces. Une solution consiste à remplacer les espaces temporairement pour la commande 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

Cet exemple repose bien sûr sur l'hypothèse que mypath ne contient jamais la séquence char "_spc_".

Vous trouverez peut-être ce plus facile à faire en python.

(1) A partir de la ligne de commande unix:

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

Résultats dans:

/Users/someone/fred

(2) Dans un script bash comme unique - enregistrer comme test.sh:

#!/usr/bin/env bash

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

echo $thepath

Exécution des résultats de bash ./test.sh dans:

/Users/someone/fred

(3) En tant que service public - enregistrer comme un endroit de expanduser sur votre chemin, avec des autorisations d'exécution:

#!/usr/bin/env python

import sys
import os

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

Cela pourrait alors être utilisé sur la ligne de commande:

expanduser ~/fred

Ou dans un script:

#!/usr/bin/env bash

thepath=$(expanduser $1)

echo $thepath

Simplest :. Remplacer 'magique' par 'eval echo'

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

Problème: Vous allez rencontrer des problèmes avec d'autres variables parce eval est mal. Par exemple:

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

Notez que la question de l'injection ne se produit pas sur la première extension. Donc, si vous deviez simplement remplacer magic avec eval echo, vous devriez être bien. Mais si vous faites echo $(eval echo ~), ce serait sensible à l'injection.

De même, si vous le faites eval echo ~ au lieu de eval echo "~", qui compterait jusqu'à deux fois élargi et donc l'injection serait juste possible loin.

Je l'ai fait avec une substitution de paramètre variable après avoir lu dans le chemin à l'aide -e lecture (entre autres). Ainsi, la boîte utilisateur tabulations compléter le chemin, et si l'utilisateur entre un chemin ~ il obtient triée.

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

L'avantage est que s'il n'y a pas de rien tilde arrive à la variable, et s'il y a un tilde, mais pas dans la première position, il est également ignoré.

(I comprennent les -i pour lire depuis que je l'utiliser dans une boucle afin que l'utilisateur peut fixer le chemin s'il y a un problème.)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top