Domanda

Ho una variabile nel mio script bash il cui valore è qualcosa di simile a questo:

~/a/b/c

Si noti che è tilde non espanso. Quando faccio ls -LT su questa variabile (lo chiamano $ VAR), ottengo tale directory. Voglio lasciare bash interpretare / espandere questa variabile senza eseguirlo. In altre parole, voglio bash per eval periodo, ma non eseguire il comando valutata. Questo è possibile in bash?

Come ho fatto a passare questo nel mio script senza espansione? Ho superato l'argomento in circondandolo con virgolette doppie.

Prova questo comando per capire cosa intendo:

ls -lt "~"

Questa è esattamente la situazione in cui mi trovo. Io voglio la tilde per essere ampliato. In altre parole, che cosa devo sostituire la magia con di rendere questi due comandi identici:

ls -lt ~/abc/def/ghi

e

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

Si noti che ~ / abc / def / ghi può o non può esistere.

È stato utile?

Soluzione

A causa della natura di StackOverflow, non posso solo fare questa risposta non accettato, ma nei successivi 5 anni da quando ho postato questo ci sono state risposte molto meglio di mia risposta certamente rudimentale e piuttosto male (ero giovane, non mi uccide).

Le altre soluzioni in questa discussione sono le soluzioni più sicure e migliori. Preferibilmente, mi piacerebbe andare con uno di questi due:


risposta originale per scopi storici (ma per favore non usare questo)

Se non mi sbaglio, "~" non sarà ampliata con uno script bash in quel modo, perché è trattata come un "~" stringa letterale. È possibile forzare espansione tramite eval come questo.

#!/bin/bash

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

In alternativa, basta usare ${HOME} se si desidera che la directory home dell'utente.

Altri suggerimenti

Se la var variabile è inserita dall'utente, eval dovrebbe non essere utilizzato per espandere la tilde utilizzando

eval var=$var  # Do not use this!

Il motivo è:. L'utente potrebbe accidentalmente (o scopo) tipo per esempio var="$(rm -rf $HOME/)" con possibili conseguenze disastrose

Una migliore (e più sicuro) modo è quello di utilizzare l'espansione di parametro Bash:

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

una precedente risposta , per fare questo robusto senza rischi per la sicurezza associati a 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%:}"
}

... usato come ...

path=$(expandPath '~/hello')

In alternativa, un approccio più semplice che usi eval con attenzione:

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 modo sicuro per l'uso eval è "$(printf "~/%q" "$dangerous_path")". Si noti che è specifico bash.

#!/bin/bash

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

questo domanda per i dettagli

Si noti inoltre che in zsh questo sarebbe come semplice come echo ${~dangerous_path}

Che ne dite di questo:

path=`realpath "$1"`

o

path=`readlink -f "$1"`

L'espansione (no pun intended) sulle risposte di halloleo birryree di e: L'approccio generale è quello di utilizzare eval, ma si tratta con alcuni avvertimenti importanti, vale a dire spazi e reindirizzamento uscita (>) nella variabile. Quanto segue sembra funzionare per me:

mypath="$1"

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

Prova con ciascuno dei i seguenti argomenti:

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

Spiegazione

  • Il ${mypath//>} strisce fuori personaggi > che potrebbe massacravano un file durante la eval.
  • Il eval echo ... è ciò che fa l'espansione della tilde reale
  • Le virgolette intorno l'argomento -e sono per il supporto di nomi di file con spazi.

Forse c'è una soluzione più elegante, ma questo è quello che sono riuscito a trovare.

Credo che questo sia quello che stai cercando per

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

Esempio di utilizzo:

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

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

Ecco la mia soluzione:

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

Basta usare eval correttamente:. Con la convalida

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

Questa è la funzione equivalente POSIX di di Håkon Hægland Bash risposta

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

2017/12/10 edit:. Add '%s' per @CharlesDuffy nei commenti

Proprio per estendere birryree 's risposta per i percorsi con gli spazi: non è possibile utilizzare il comando eval come è perché separa la valutazione da spazi. Una soluzione è quella di sostituire gli spazi temporaneamente il 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

Questo esempio si basa naturalmente sul presupposto che mypath non contiene mai il "_spc_" sequenza di char.

Si potrebbe trovare questo più facile da fare in python.

(1) Dalla riga di comando UNIX:

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

Risultati in:

/Users/someone/fred

(2) All'interno di uno script bash come un one-off - Salva questo come test.sh:

#!/usr/bin/env bash

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

echo $thepath

Esecuzione risultati bash ./test.sh in:

/Users/someone/fred

(3) Come un programma di utilità - Salva questo come expanduser da qualche parte sul vostro percorso, con i permessi di esecuzione:

#!/usr/bin/env python

import sys
import os

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

Questo potrebbe quindi essere utilizzato sulla riga di comando:

expanduser ~/fred

o in uno script:

#!/usr/bin/env bash

thepath=$(expanduser $1)

echo $thepath

più semplice :. Replace 'magia' con 'eval echo'

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

Problema: Hai intenzione di incorrere in problemi con le altre variabili, perché eval è il male. Per esempio:

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

Si noti che il problema dell'iniezione non avviene sulla prima espansione. Quindi, se si dovesse sostituire semplicemente magic con eval echo, si dovrebbe essere a posto. Ma se lo fai echo $(eval echo ~), che sarebbe suscettibile di iniezione.

Allo stesso modo, se si fa eval echo ~ invece di eval echo "~", che conterebbe come due volte ampliato e, pertanto, l'iniezione sarebbe possibile fin da subito.

Ho fatto questo con sostituzione di parametro variabile dopo aver letto nel percorso utilizzando -e di lettura (tra gli altri). Così l'utente può, scheda-completare il percorso, e se l'utente inserisce un percorso ~ si ottiene allineati.

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

Il vantaggio è che se non c'è tilde non succede nulla alla variabile, e se c'è una tilde, ma non nella prima posizione è anche ignorato.

(includo la -i per la lettura da quando uso questo in un ciclo così l'utente può risolvere il percorso se c'è un problema.)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top