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 ~/Downloads
para ~/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 $HOME
para 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 ls
em scripts. Neste caso, por exemplo, se $1
é simplesmente o nome de um arquivo, ~/Downloads
você 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".$1
não está entre aspas e pode, portanto, confundirgrep
se contiver espaços ou outros caracteres especiais. Se$1
for*
, seria expandido para todos os nomes de arquivos no diretório atual, por exemplo.- Se a string
$1
começar com um travessão, ela será considerada uma opção paragrep
.
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 printf
comando 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 $name
a chamada cp
na ú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 .pdf
os arquivos ~/Downloads
que 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 -v
copiará 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: