cp com espaços e caracteres especiais funciona bem no terminal, mas não no script

cp com espaços e caracteres especiais funciona bem no terminal, mas não no script

Estou tentando escrever um script para localizar um arquivo e copiá-lo para uma pasta específica. O nome do arquivo contém espaços e outros caracteres especiais.

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

Quando cito o nome do arquivo, ele funciona bem no terminal. Mas no roteiro, as palavras são tratadas como argumentos separados. No script acima, quando $a é ecoado, ele fornece algo como:

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

Você pode ver que o nome do arquivo está entre aspas. Quando o argumento acima é passado para cp no terminal, ele funciona bem, mas não no script. Por favor ajude.

Responder1

Você parece querer copiar um arquivo específico de ~/Downloadspara ~/Documents/books.

A primeira coisa a notar é que ~não se comporta como uma variável. Em particular, faznãoexpanda para o caminho do seu diretório inicial quando estiver entre aspas. Em scripts, portanto, é melhor usar $HOMEpara se referir ao diretório inicial.

A segunda coisa é que você parece adicionar aspas simples literais ao valor da sua variável $a. Isso não é necessário e resultará apenas em um erro "Esse arquivo ou diretório não existe" (porque o arquivo não possui aspas simples no nome do arquivo).

A terceira coisa é que você deve tentar evitar o uso lsem scripts. Neste caso, por exemplo, se $1é simplesmente o nome de um arquivo, ~/Downloadsvocê não precisa procurá-lo. Além disso, o uso grep $1é problemático por vários motivos:

  • grepé usado principalmente em textodocumentos. Os nomes de arquivos podem, teoricamente, conter novas linhas, o que faz com que os nomes de arquivos "grepping" só funcionem se alguém souber os nomes ou "nice".
  • $1não está entre aspas e pode, portanto, confundir grepse contiver espaços ou outros caracteres especiais. Se $1for *, seria expandido para todos os nomes de arquivos no diretório atual, por exemplo.
  • Se a string $1começar com um travessão, ela será considerada uma opção para grep.

Então, seu script pode ser escrito como

#!/bin/sh

name="$HOME/Downloads/$1"

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

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

O printfcomando aqui é gerar o nome do arquivo que escolhemos. Eu sousaídaaspas duplas ao redor do nome, mas o nome nãonãotem aspas duplas. Em vez disso, certifico-me de citar $namea chamada cpna última linha. Na verdade, estou me certificando de citartodosexpansões que eu sei que de outra forma resultariam na divisão do shell pelo valor da variável.

Você usaria esse script como

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

O nome do arquivo deve ser colocado entre aspas, pois contém espaços e caracteres que são especiais no shell.

Se você quiser que seu script procure um arquivo que contenha uma palavra dada na linha de comando, para que você possa dizer

./script.sh Goodfellow

Em seguida, use a string fornecida em um padrão 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

Agora o script pegará o primeiro argumento e fará um loop sobre todos .pdfos arquivos ~/Downloadsque contêm a string fornecida no nome do arquivo. Cada um desses arquivos será copiado para ~/Documents/books.

Se você estiver em um Unix onde cp -vcopiará arquivos detalhadamente, o script poderá ser reduzido ainda mais:

#!/bin/sh

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

Isso evita que você mesmo formate a saída ("Copiará ..."), pois não estamos mais repetindo os nomes de arquivos que correspondem ao padrão fornecido. Em vez disso, confiamos apenas no fato de que, se você fornecer vários nomes de arquivos cp(e o padrão globbing pode se expandir para vários nomes de arquivos), eles poderão ser copiados com um único comando para algum diretório de destino.


Veja também:

informação relacionada