El comando de búsqueda SSH no reconoce el directorio almacenado en la variable local

El comando de búsqueda SSH no reconoce el directorio almacenado en la variable local

Puedo utilizar SSH e ir al directorio almacenado en una variable local $out_dir. Sin embargo, no puedo entender por qué el find | headcomando no funciona. EnPrueba #2, agregué una segunda barra invertida al final del findcomando:

Pruebe el error número 1: find: `/dir/on/remote/server': No such file or directory

ssh $user@$ip << EOF
    discard=$( find $out_dir -exec basename {} \; | head -n -1 )
    for f in $discard; do
        echo "rm $f"
    done
    logout
EOF

Pruebe el error número 2: syntax error near unexpected token `|'

ssh $user@$ip << EOF
    discard=$( find $out_dir -exec basename {} \\; | head -n -1 )
    for f in $discard; do
        echo "rm $f"
    done
    logout
EOF

Respuesta1

La razón por la que obtiene el mensaje 'No existe tal archivo o directorio' en el intento n.º 1 es que toda la interpolación de variables y comandos para el heredoc (el <<EOF) se realiza localmente, antes de enviarse al host remoto.

Como observa correctamente, $out_dirse está interpolando; verá el directorio que desea ver. Esto sucede en su máquina local antes de realizar la llamada ssh. La sustitución del comando ( $()) también se realiza localmente (pero tiene la intención de hacerlo de forma remota). Dicho de otra manera, la totalidad del hallazgo se procesa localmente antes de enviarlo a la máquina remota; todo lo incluido $()es procesado por el heredoc. Por lo tanto, parece que ${out_dir}no está presente en su máquina local: "No existe tal archivo o directorio".

Si desea ver esto mejor por sí mismo, reduzcamos el ejemplo. Prueba esto:

$ ssh foo@localhost <<EOF
  echo "using account: $(whoami)"
EOF                            
Pseudo-terminal will not be allocated because stdin is not a terminal.
<truncated>
using account: vagrant

Claramente, el contenido de $() se ejecuta localmente, ya que mi cuenta local es "vagabunda"; Si hubiera funcionado correctamente, la cuenta habría sido 'foo' porque lo especifiqué ssh foo@localhost.

° 2 no funciona porque escapó del '\' (en lugar del ';'). Find necesita el -execterminado y ahora no lo es. En cambio, el comando termina abruptamente y tienes un ';' colgando. que finaliza el comando bash. '|' espera enviar alguna información, pero no hay nada especificado. Efectivamente, creaste esto:

$ | cat
-bash: syntax error near unexpected token `|'

Entonces, ¿cuál es una solución que funcione?

Bueno, findpuedo hacer la eliminación por ti:

ssh $user@$ip <<EOF
  find $out_dir -delete
EOF

Es simple y directo. Si le preocupa seleccionar algunos archivos y excluir otros, revise el archivo find.

Le doy el findconsejo de 'revisar' porque en su versión actual, parece que está intentando manejar algunos casos extremos:

  • el uso del nombre base
  • el uso dehead -n -1

Pero no espero que este manejo funcione como pretende.

Primero, la llamada basenameeliminará la mayor parte de la ruta y, en su comando ssh, no hará nada para cambiar el directorio. Su ejecución ssh intentará eliminar todo lo relativo al inicio de ${user}, ¡y al especificar ${out_dir} parece que desea usar un directorio que no sea el de inicio! Nuevamente, deja que findhagamos las eliminaciones por ti.

En segundo lugar, y puede que esté equivocado con este, pero sospecho que lo utiliza head -n -1para deshacerse de ${out_dir} de su lista de archivos de destino.

$ find junk/
junk/
junk/one
junk/two
junk/three

Con razón, no deseas eliminarlo junk/.

Sin embargo, prueba esto:

$ seq 1 4
1
2
3
4
$ seq 1 4 | head -n -1
1
2
3

Esto no conserva la primera línea, sino la última. Prueba la cola:

$ seq 1 4 | tail -n +2
2
3
4

Pero nuevamente, deja que findhagamos las eliminaciones por ti.

Si necesita buscar para eliminar solo archivos, consulte -type f. Pero también hay patrones de exclusión e inclusión para un filtrado más avanzado.

Respuesta2

Prueba esto:

ssh "$user@$ip" "out_dir=$out_dir;" '
    discard=$( find $out_dir -exec basename {} \; | head -n -1 )
    for f in $discard; do
        echo "rm $f"
    done
'

Nota:el comando remoto previsto podría hacerse más simple y seguro, pero criticar eso solo confundirá el punto.

Básicamente, debes pasar las partes quea)deberíatambiénser ampliado en ellocalmáquina y aquellos queb)debiera sersoloser ampliado en elremotomáquina comoseparadoargumentos para ssh, el primero entre comillas dobles y el segundo entre comillas simples.

Ssh unirá todos los argumentos de "comando" con espacios antes de pasarlos como un argumento único a la -copción del shell de inicio de sesión del usuario en la máquina remota.

Si el comando remoto debe contener comillas simples, puede usar una combinación de sustitución de comando + aquí doc. Además, si el valor de la variable "local" puede contener espacios y otros caracteres especiales, debes aplicar escape:

out_dir='/path/to/ * happiness * '
out_dir=$(printf %s "$out_dir" | sed 's/[^a-zA-Z0-9_/.]/\\&/g')
ssh localhost out_dir="$out_dir;" "$(cat <<'EOT'
  echo '<$out_dir>' = "<$out_dir>"
EOT
)"

Observe que las comillas simples en <<'EOF'; si se omite, el $out_dirdocumento aquí se expandirá (en la máquina local).

Las versiones recientes de bash tienen el ${var@Q}formulario, por lo que simplemente podrías usarlo "out_dir=${outdir@Q};"en lugar de ese feo echo | sed.

Pasar el script remoto a través de stdin rara vez (¡o nunca!) es necesario.

información relacionada