¿~ Siempre es igual a $ HOME

40

Sé que esto probablemente se haya preguntado antes, pero no pude encontrarlo con Google.

Dado

  • Kernel de Linux
  • No hay configuraciones que cambien $ HOME
  • golpetazo

Será ~ == $HOMEverdad?

PythonNut
fuente
3
Creo que sí, pero este no es un problema específico de Linux. Más bien, creo ~que será equivalente a $HOMEcualquier entorno POSIX; Pero podría estar equivocado.
HalosGhost
44
A pesar de las respuestas a continuación ... no siempre. Compara echo "~"y echo "$HOME".
Sparhawk
@Sparhawk Espera, ¿entonces 1 + 1 no es 2? Es una conspiración! ¡El gobierno nos está ocultando algo! : P
Pomo de la puerta
1
@Sparhawk, notará que el OP en realidad no citó ni el ~o $HOME. : P
HalosGhost
2
@HalosGhost Ah, buen punto. (Además, más tarde me di cuenta de que la respuesta de Michael Homer menciona "un solo sin comillas ~".) De todos modos, algo a tener en cuenta para el lector casual, como yo.
Sparhawk

Respuestas:

46

Lo que es importante entender es que la ~expansión es una característica del shell (de algunos shells), no es un personaje mágico que significa su directorio de inicio donde sea que se use.

Se expande (mediante el shell, que es una aplicación utilizada para interpretar líneas de comando), al igual que $varse expande a su valor en algunas condiciones cuando se usa en una línea de comando del shell antes de ejecutar el comando.

Esa característica apareció por primera vez en el C-shell a fines de la década de 1970 (el shell Bourne no lo tenía, ni su predecesor, el shell Thompson), luego se agregó al shell Korn (un shell más nuevo construido sobre el shell Bourne en el 80s). Eventualmente fue estandarizado por POSIX y ahora está disponible en la mayoría de los shells, incluidos los que no son POSIX fish.

Debido a su uso tan extendido en shells, algunas aplicaciones que no son de shell también lo reconocen como el directorio de inicio. Ese es el caso de muchas aplicaciones en sus archivos de configuración o de su propia línea de comandos ( mutt, slrn, vim...).

bashespecíficamente (que es el shell del proyecto GNU y ampliamente utilizado en muchos sistemas operativos basados ​​en Linux), cuando se invoca como sh, en su mayoría sigue las reglas POSIX sobre ~expansión, y en áreas no especificadas por POSIX, se comporta principalmente como el shell Korn (de que es un clon parcial).

Si bien $varse expande en la mayoría de los lugares (excepto dentro de comillas simples), la ~expansión, una idea de último momento solo se expande en unas pocas condiciones específicas.

Se expande cuando está en su propio argumento en contextos de lista, en contextos donde se espera una cadena.

Aquí hay algunos ejemplos de dónde se expande bash:

  • cmd arg ~ other arg
  • var=~
  • var=x:~:x(requerido por POSIX, usado para variables como PATH, MANPATH...)
  • for i in ~
  • [[ ~ = text ]]
  • [[ text = ~ ]](la expansión de ~ser tomado como un patrón en AT&T kshpero no bashdesde 4.0).
  • case ~ in ~) ...
  • ${var#~} (aunque no en otras conchas)
  • cmd foo=~(aunque no cuando se invoca como sh, y solo cuando lo que está a la izquierda =tiene la forma de un bashnombre de variable sin comillas )
  • cmd ~/x (requerido por POSIX obviamente)
  • cmd ~:x(pero no x:~:xo x-~-x)
  • a[~]=foo; echo "${a[~]} $((a[~]))" (no en otras conchas)

Aquí hay algunos ejemplos donde no se expande:

  • echo "~" '~'
  • echo ~@ ~~(también tenga en cuenta que ~uestá destinado a expandirse al directorio de inicio del usuario u).
  • echo @~
  • (( HOME == ~ )), $(( var + ~ ))
  • con extglob: case $var in @(~|other))...(aunque case $var in ~|other)está bien).
  • ./configure --prefix=~(como --prefixno es un nombre de variable válido)
  • cmd "foo"=~(en bash, debido a las comillas).
  • Cuando se invoca como sh: export "foo"=~, env JAVA_HOME=~ cmd...

En cuanto a lo que se expande: ~solo se expande al contenido de la HOMEvariable, o cuando no está configurado, al directorio de inicio del usuario actual en la base de datos de la cuenta (como una extensión ya que POSIX deja ese comportamiento indefinido).

Cabe señalar que en ksh88 y bashversiones anteriores a la 4.0, la expansión de tilde experimentó un bloqueo (generación de nombre de archivo) en contextos de lista:

$ bash -c 'echo "$HOME"'
/home/***stephane***
$ bash -c 'echo ~'
/home/***stephane*** /home/stephane
$ bash -c 'echo "~"'
~

Eso no debería ser un problema en los casos habituales.

Tenga en cuenta que debido a que está expandido, se aplica la misma advertencia que otras formas de expansión.

cd ~

No funciona si $HOMEcomienza con -o contiene ..componentes. Entonces, aunque es muy poco probable que alguna vez haga alguna diferencia, estrictamente hablando, uno debería escribir:

cd -P -- ~

O incluso:

case ~ in
  (/*) cd -P ~;;
  (*) d=~; cd -P "./$d";;
esac

(para cubrir valores de me $HOMEgusta -, +2...) o simplemente:

cd

(como lo cdlleva a su directorio de inicio sin ningún argumento)

Otros proyectiles tienen ~expansiones más avanzadas . Por ejemplo, en zsh, tenemos:

  • ~4, ~-, ~-2(Con la terminación) que se utiliza para expandir los directorios en su pila de directorios (los lugares que ha cdde antes).
  • directorios con nombre dinámico . Puede definir su propio mecanismo para decidir cómo ~somethingse está expandiendo.
Stéphane Chazelas
fuente
25

En cualquier versión de Bash en cualquier sistema, sí . ~como un término en sí mismo se define para expandirse a:

El valor de $ HOME

así que siempre será lo mismo que lo que sea $HOMEpara el shell actual. Hay varias otras expansiones de tilde, como el directorio de inicio de ~userfor user, pero una sola sin comillas ~siempre se expandirá a "$HOME".

Tenga en cuenta que el comportamiento de ~y $HOMEpuede ser diferente en algunos casos: en particular, si $HOMEcontiene espacios (u otros IFS caracteres), luego $HOME(sin comillas) se expandirá a varias palabras, mientras que ~es siempre una única palabra. ~se expande de manera equivalente a "$HOME"(citado).

Con respecto a su pregunta específica:

[[ $HOME == ~ ]]

siempre es cierto, porque [[ suprime la división de palabras. [[ ~ == $HOME ]puede no serlo si HOMEtiene caracteres de coincidencia de patrones , pero [[ ~ == "$HOME" ]](es decir, entre comillas "$HOME") siempre es cierto. Usarlo entre paréntesis puede ser un error de sintaxis para valores de HOMEespacios o caracteres especiales. Para cualquier configuración de directorio de inicio sensible ~y "$HOME"son iguales y se comparan como iguales.


Stéphane Chazelas ha notado un caso en los comentarios donde ~y $HOMEda diferentes valores: si usted unset HOME, cuando use ~Bash, lo llamará getpwuidpara leer un valor de la base de datos de contraseñas. Este caso está excluido por su condición de no cambiar la configuración $HOME, pero lo mencionaré aquí para completarlo.

Michael Homer
fuente
1
Sin embargo, /bin/shpuede que no sea bash. No estoy seguro de que la shespecificación de Posix diga sobre~
Basile Starynkevitch
1
POSIX especifica ~. ~no estaba en el shell Thomson o Bourne (que en su momento estaban disponibles como /bin/sh). No está ni en rcsus derivados (donde se usa para otra cosa)
Stéphane Chazelas
55
En algunos shells que incluyen bash, si no HOMEestá configurado, se ~expande al directorio de inicio del usuario desde la base de datos passwd. Entonces ese es un caso en el que ~puede no expandirse al valor de $HOME.
Stéphane Chazelas
1
Tenga en cuenta que bashantes de bash4 solía realizar globbing tras la expansión de tilde (intente HOME='/*' bash -c 'echo /*'). Entonces HOME=/*; [ "$HOME" = ~ ]devolvería un error allí.
Stéphane Chazelas
44
@cuonglm Revisé el código fuente. En última instancia , llama get_current_user_info , que se usa getpwuiden todas las plataformas excepto Tandem .
Michael Homer