Я пытаюсь написать скрипт для поиска файла и копирования его в определенную папку. Имя файла содержит пробелы и другие специальные символы.
a=$(ls ~/Downloads/ | grep $1)
a="'$a'"
a="~/Downloads/"$a
echo $a
cp $a ~/Documents/books/.
Когда я цитирую имя файла, это нормально работает на терминале. Но в скрипте слова обрабатываются как отдельные аргументы. В приведенном выше скрипте, когда $a выводится, выдается что-то вроде:
~/Downloads/'Ian Goodfellow, Yoshua Bengio, Aaron Courville - Deep Learning [pre-pub version] (2016, MIT Press).pdf'
Вы видите, что имя файла заключено в кавычки. Когда указанный выше аргумент передается в cp на терминале, он работает нормально, но не в скрипте. Пожалуйста, помогите.
решение1
Кажется, вы хотите скопировать определенный файл из ~/Downloads
в ~/Documents/books
.
Первое, что следует отметить, это то, что ~
не ведет себя как переменная. В частности, она ведет себянетрасширяется до пути к вашему домашнему каталогу, когда он заключен в кавычки. Поэтому в скриптах лучше использовать $HOME
для ссылки на домашний каталог.
Во-вторых, вы, похоже, добавляете буквальные одинарные кавычки к значению вашей переменной $a
. Это не обязательно и приведет только к ошибке «Нет такого файла или каталога» (потому что в имени файла нет одинарных кавычек).
Третье, что вам следует попытаться избежать использования ls
в скриптах. В этом случае, например, если $1
это просто имя файла, ~/Downloads
вам вообще не нужно его искать. Кроме того, использование grep $1
проблематично по ряду причин:
grep
в основном используется в текстедокументы. Имена файлов теоретически могут содержать новые строки, что делает «grepping» имен файлов работоспособным только в том случае, если известно, что имена «хорошие».$1
не заключено в кавычки и может поэтому сбивать с толкуgrep
, если содержит пробелы или другие специальные символы. Если$1
это*
, то оно будет расширяться до всех имен файлов в текущем каталоге, например.- Если строка
$1
начинается с дефиса, она будет воспринята как вариантgrep
.
Итак, ваш сценарий может быть написан как
#!/bin/sh
name="$HOME/Downloads/$1"
printf 'The filename is "%s"\n' "$name"
cp "$name" "$HOME/Documents/books"
Команда printf
здесь выводит имя файла, который мы выбрали. ЯвыводИмя заключено в двойные кавычки, но само имянетесть двойные кавычки. Вместо этого я обязательно цитирую $name
вызов cp
в последней строке. На самом деле, я обязательно цитируювсерасширения, которые, как я знаю, в противном случае привели бы к тому, что оболочка разделила бы значение переменной.
Вы бы использовали этот сценарий как
./script.sh 'Ian Goodfellow, Yoshua Bengio, Aaron Courville - Deep Learning [pre-pub version] (2016, MIT Press).pdf'
Имя файла необходимо заключить в кавычки, поскольку оно содержит пробелы и символы, которые в оболочке являются специальными.
Если вы хотите, чтобы ваш скрипт искал файл, содержащий слово, указанное в командной строке, так, чтобы вы могли сказать
./script.sh Goodfellow
Затем используйте данную строку в шаблоне подстановки:
#!/bin/sh
for name in "$HOME/Downloads"/*"$1"*.pdf; do
printf 'Will copy the file "%s"\n' "$name"
cp "$name" "$HOME/Documents/books"
done
Теперь скрипт возьмет первый аргумент и переберет все .pdf
файлы, ~/Downloads
содержащие указанную строку в имени файла. Каждый такой файл будет скопирован в ~/Documents/books
.
Если вы работаете в Unix, где cp -v
требуется подробное копирование файлов, скрипт можно сократить еще больше:
#!/bin/sh
cp -v "$HOME/Downloads"/*"$1"*.pdf "$HOME/Documents/books"
Это не позволяет вам форматировать вывод самостоятельно ("Will copy ..."), поскольку мы больше не перебираем имена файлов, соответствующие заданному шаблону. Вместо этого мы просто полагаемся на тот факт, что если вы дадите несколько имен файлов cp
(а шаблон подстановки может расшириться до нескольких имен файлов), то их можно будет скопировать одной командой в некоторый целевой каталог.
Смотрите также: