Pregunta

Tengo una variable en mi escritura del golpe cuyo valor es algo como esto:

~/a/b/c

Tenga en cuenta que es tilde no expandido. Cuando hago ls -lt sobre esta variable (llámese $ VAR), consigo dicho directorio no. Quiero que la fiesta de interpretar / expandir esta variable sin ejecutarlo. En otras palabras, quiero bash para ejecutar eval pero no funciono con el comando evaluado. ¿Es esto posible en bash?

¿Cómo me las arreglo para pasar esto en mi guión sin expansión? Pasé el argumento de que la rodea con comillas dobles.

Trate de este comando para ver lo que quiero decir:

ls -lt "~"

Esta es exactamente la situación en la que estoy. Quiero la tilde se expanda. En otras palabras, lo que debería sustituir a la magia de hacer con estos dos comandos idénticos:

ls -lt ~/abc/def/ghi

y

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

Tenga en cuenta que ~ / abc / def / ghi pueden o no existir.

¿Fue útil?

Solución

Debido a la naturaleza de stackoverflow, que puede no sólo hacer que esta respuesta no aceptada, pero en las que intervienen 5 años desde que he publicado esto no hubiera sido mucho mejores respuestas que mi respuesta ciertamente rudimentario y bastante malo (yo estaba joven, no me mata).

Las otras soluciones en este tema son las soluciones más seguras y mejores. Preferiblemente, me gustaría ir con cualquiera de estos dos:


Respuesta original con fines históricos (pero por favor no utilice este)

Si no me equivoco, "~" no se expandió por una escritura del golpe de esa manera, ya que se trata como una cadena literal "~". Puede forzar la expansión a través de eval como este.

#!/bin/bash

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

Por otra parte, sólo tiene que utilizar ${HOME} si desea que el directorio personal del usuario.

Otros consejos

Si el var variable es de entrada por el usuario, eval debe no ser utilizado para expandir el tilde utilizando

eval var=$var  # Do not use this!

La razón es la siguiente:. El usuario podría accidentalmente (o propósito) Tipo var="$(rm -rf $HOME/)" por ejemplo con posibles consecuencias desastrosas

A mejor (y más seguro) forma es utilizar la expansión de parámetro Bash:

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

una respuesta , para hacer esto con firmeza y sin los riesgos de seguridad asociados con 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%:}"
}

... utilizado como ...

path=$(expandPath '~/hello')

Como alternativa, un enfoque más simple que los usos eval cuidadosamente:

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
}

Una forma segura de usar eval es "$(printf "~/%q" "$dangerous_path")". Tenga en cuenta que es fiesta específica.

#!/bin/bash

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

esta pregunta para obtener más información

Además, nota que bajo zsh esto sería tan tan simple como echo ${~dangerous_path}

¿Qué tal esto:

path=`realpath "$1"`

O:

path=`readlink -f "$1"`

La expansión (sin doble sentido) en birryree de respuestas y de halloleo: El enfoque general es utilizar eval, pero viene con algunas advertencias importantes, a saber espacios y redirección de la salida (>) en la variable. El siguiente parece que funciona para mí:

mypath="$1"

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

Pruebe con cada uno de los siguientes argumentos:

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

Explicación

  • El ${mypath//>} excluye los caracteres > que podría Clobber un archivo durante la eval.
  • El eval echo ... es lo que hace la expansión de tilde real
  • Las comillas dobles alrededor del argumento -e son para el soporte de nombres de archivo con espacios.

Tal vez hay una solución más elegante, pero esto es lo que yo era capaz de llegar a.

Creo que esto es lo que estás buscando

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

Ejemplo de uso:

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

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

Aquí está mi solución:

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

Sólo tiene que utilizar correctamente eval:. Con la validación

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

Esta es la función equivalente de POSIX Bash de Håkon Hægland respuesta

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

12/10/2017 edición:. '%s' complemento por @CharlesDuffy en los comentarios

Sólo para extender birryree 's respuesta para caminos con espacios: No se puede utilizar el comando eval como es debido a que separa evaluación por espacios. Una solución consiste en sustituir los espacios temporalmente para el comando 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

Este ejemplo se basa, por supuesto, en el supuesto de que mypath nunca contiene la secuencia "_spc_" Char.

Se puede encontrarlo más fácil de hacer en pitón.

(1) Desde la línea de comandos de UNIX:

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

Los resultados en:

/Users/someone/fred

(2) Dentro de un script bash como un hecho aislado - Guardar como test.sh:

#!/usr/bin/env bash

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

echo $thepath

Running resultados bash ./test.sh en:

/Users/someone/fred

(3) Como una utilidad - guardar esto como un lugar expanduser en su camino, con permisos de ejecución:

#!/usr/bin/env python

import sys
import os

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

Esto podría entonces ser utilizado en la línea de comandos:

expanduser ~/fred

o en un script:

#!/usr/bin/env bash

thepath=$(expanduser $1)

echo $thepath

más simple . Reemplace 'mágica' con 'eval echo'

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

Problema: Vas a que tenga problemas con otras variables eval porque es malo. Por ejemplo:

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

Tenga en cuenta que el tema de la inyección no sucede en la primera expansión. Así que si tuviera que sustituir simplemente magic con eval echo, usted debe estar bien. Pero si lo hace echo $(eval echo ~), que sería susceptible a la inyección.

Del mismo modo, si lo hace eval echo ~ en lugar de eval echo "~", que contaría como dos veces ampliado y, por tanto, sería posible la inyección de inmediato.

he hecho esto con la sustitución de parámetros variable después de la lectura en la ruta utilizando -e lectura (entre otros). Por lo que el usuario puede, por tabuladores completar el camino, y si el usuario entra en un camino ~ que se ordenan.

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

El beneficio adicional es que si no hay una tilde no pasa nada a la variable, y si hay una tilde pero no en la primera posición también es ignorada.

(incluyo el -i para lectura desde que uso este en un bucle de manera que el usuario puede fijar la ruta de acceso si hay un problema.)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top