cp con espacios y caracteres especiales funciona bien en la terminal pero no en el script

cp con espacios y caracteres especiales funciona bien en la terminal pero no en el script

Estoy intentando escribir un script para buscar un archivo y copiarlo a una carpeta en particular. El nombre del archivo tiene espacios y otros caracteres especiales.

a=$(ls ~/Downloads/ | grep $1)
a="'$a'"
a="~/Downloads/"$a
echo $a
cp $a ~/Documents/books/.

Cuando cito el nombre del archivo, funciona bien en la terminal. Pero en el guión, las palabras se tratan como argumentos separados. En el script anterior, cuando se repite $a, se muestra algo como:

~/Downloads/'Ian Goodfellow, Yoshua Bengio, Aaron Courville - Deep Learning [pre-pub version] (2016, MIT Press).pdf'

Puede ver que el nombre del archivo está citado. Cuando el argumento anterior se pasa a cp en la terminal, funciona bien pero no en el script. Por favor ayuda.

Respuesta1

Parece que desea copiar un archivo en particular de ~/Downloadsa ~/Documents/books.

Lo primero que hay que tener en cuenta es que ~no se comporta como una variable. En particular, lo hacenoexpanda a la ruta de su directorio de inicio cuando esté citado. Por lo tanto, en los scripts es mejor utilizarlo $HOMEpara hacer referencia al directorio de inicio.

La segunda cosa es que parece agregar comillas simples literales al valor de su variable $a. Esto no es necesario y solo generará el error "No existe tal archivo o directorio" (porque el archivo no tiene comillas simples en el nombre del archivo).

La tercera cosa es que debes intentar evitar su uso lsen scripts. En este caso, por ejemplo, si $1es simplemente el nombre de un archivo, ~/Downloadsno es necesario buscarlo en absoluto. Además, el uso grep $1es problemático por diversas razones:

  • grepse utiliza principalmente en textodocumentos. En teoría, los nombres de archivos pueden contener nuevas líneas, lo que hace que "grepear" los nombres de archivos sólo funcione si uno sabe que los nombres son "agradables".
  • $1no está entre comillas y, por lo tanto, puede resultar confuso grepsi contiene espacios u otros caracteres especiales. Si $1es así *, se expandiría a todos los nombres de archivos en el directorio actual, por ejemplo.
  • Si la cadena $1comienza con un guión, se tomará como opción grep.

Entonces, su guión puede escribirse como

#!/bin/sh

name="$HOME/Downloads/$1"

printf 'The filename is "%s"\n' "$name"

cp "$name" "$HOME/Documents/books"

El printfcomando aquí es mostrar el nombre del archivo que elegimos. Soysalidacomillas dobles alrededor del nombre, pero el nombre nonotener comillas dobles. En cambio, me aseguro de citar $namela llamada cpen la última línea. De hecho, me aseguro de citartodoexpansiones que sé que de otro modo darían como resultado que el shell dividiera el valor de la variable.

Usarías ese script como

./script.sh 'Ian Goodfellow, Yoshua Bengio, Aaron Courville - Deep Learning [pre-pub version] (2016, MIT Press).pdf'

El nombre del archivo debe estar entre comillas ya que contiene espacios y caracteres que de otro modo serían especiales en el shell.

Si desea que su secuencia de comandos busque un archivo que contenga una palabra dada en la línea de comando, de modo que pueda decir

./script.sh Goodfellow

Luego use la cadena dada en un patrón global:

#!/bin/sh

for name in "$HOME/Downloads"/*"$1"*.pdf; do
    printf 'Will copy the file "%s"\n' "$name"
    cp "$name" "$HOME/Documents/books"
done

Ahora el script tomará el primer argumento y recorrerá todos .pdflos archivos ~/Downloadsque contengan la cadena dada en el nombre del archivo. Cada uno de estos archivos se copiará a ~/Documents/books.

Si está en un Unix donde cp -vcopia archivos detalladamente, es posible que el script se reduzca aún más:

#!/bin/sh

cp -v "$HOME/Downloads"/*"$1"*.pdf "$HOME/Documents/books"

Esto le impide formatear la salida usted mismo ("Copiará ...") ya que ya no repasamos los nombres de archivo que coinciden con el patrón dado. En lugar de eso, simplemente confiamos en el hecho de que si le asigna varios nombres de archivo cp(y el patrón global bien puede expandirse a varios nombres de archivo), estos se pueden copiar con un solo comando a algún directorio de destino.


Ver también:

información relacionada