Скопируйте несколько файлов через cp из строки
Я пытаюсь скопировать несколько файлов в каталог из шеллскрипта. Эти файлы содержат всевозможные "уродливые" символы, такие как пробелы, скобки и тому подобное. Тем не менее, я застрял, когда дело доходит до побега bash
а также cp
кажется, справиться с этим очень странно.
Вот сценарий:
При выдаче этой команды из моей оболочки она работает как шарм:cp /somedir/a\ file.png /somedir/another\ file.png /someotherdir/
Однако при чтении файлов для копирования из строки это неожиданно становится странным:var="/somedir/a\ file.png /somedir/another\ file.png"
cp "$var" /someotherdir/
Результаты в cp: cannot stat 'a\\ file.png another\\ file.png': No such file or directory
Я считаю, что это связано с тем, что я даю переменную в виде строки и cp
считает, что это один файл, хотя это несколько файлов. При выдаче той же команды без помещения переменной в кавычки (var="/somedir/a\ file.png /somedir/another\ file.png"; cp $var /someotherdir/
) Я получаю еще более странную ошибку:cp: cannot stat 'a\\ file.png another\\ file.png': No such file or directory
cp: cannot stat 'file.png': No such file or directory
cp: cannot stat 'another\\': No such file or directory
cp: cannot stat 'file.png': No such file or directory
Кажется, это полностью игнорирует мое спасение. Что я делаю неправильно?
РЕДАКТИРОВАТЬ:// Кажется, что список копирования файлов имеет ответ с xargs
, но я все еще удивляюсь, почему bash ведет себя так странно.
3 ответа
Доктор, мне больно, когда я делаю это...
Тогда не делай этого!
Серьезно, почему вы пытаетесь присвоить одной переменной разделенный пробелами список имен файлов, которые содержат пробелы? Это рецепт катастрофы.
Чтобы ответить на ваш подразумеваемый вопрос: я согласен, что отображаемые вами сообщения об ошибках не имеют смысла для команд, которые, как вы говорите, вы использовали.
Поскольку вы еще не сказали, как определить, какие файлы копировать, трудно понять, какое решение будет работать для вас, а какое - нет. Вот возможный подход:
︙ f1 = "/ somedir / a file.png" ︙ f2 = "/ somedir / another file.png" ︙ cp "$f1" "$f2" "$f3" … /someotherdir
(Вам не нужно /
в конце /someotherdir/
, но это тоже не повредит.) Это ограничено фактами, что вам нужно знать заранее максимальное количество файлов, которое вам нужно будет скопировать, и вам нужен какой-то способ выяснить, что f
переменная для назначения каждого.
Это близко к тому, что вы делаете сейчас:
var = "/ somedir / a \ file.png / somedir / another \ file.png…" echo "$ var" | пока читаю f1 f2 f3 … делать cp "$f1" "$f2" "$f3" … /someotherdir сделанный
read
команда сломает $var
разделяйте на равнинах и убирайте обратные слеши, оставляя \
как раз
(пространство). Но вам все равно нужно заранее знать максимальное количество файлов, которое вам нужно будет скопировать.
(Вам нужно while
конструкция цикла, хотя этот цикл будет повторяться только один раз.)
Возможно, этот становится ближе:
Filelist=/ TMP /my_filelist $$. ︙ echo "/somedir/a file.png" >> "$filelist" ︙ echo "/somedir/another file.png" >> "$filelist" ︙ пока читаешь имя файла делать cp "$filename" /someotherdir сделано<"$ filelist"
Другой вариант заключается в использовании массива переменных. Вы можете узнать, как это сделать, в bash
справочная страница.
Когда bash анализирует командную строку, он сначала интерпретирует кавычки, экранированные символы и т. Д., А затем заменяет переменные. Если в значениях переменных содержатся экранированные символы, кавычки и т. Д., Они никогда не вступают в силу, поскольку к тому времени, когда они включены в команду, для них уже слишком поздно достичь желаемого эффекта.
Как правило, лучший способ справиться с такой проблемой - хранить имена файлов в массиве (один элемент на имя файла), а не в строковой переменной; затем используйте "${array[@]}"
чтобы расширить массив, каждый элемент рассматривается как отдельное "слово":
files=(/somedir/a\ file.png /somedir/another\ file.png)
cp "${files[@]}" /someotherdir/
Обновление: если вам нужно построить список файлов динамически, это легко с массивом:
files=() # Start with an empty list
for newfile in somethingorother; do
files+=("$newfile")
done
Если вы используете bash, вы можете заключить в кавычки имя файла, чтобы избежать использования escape-символов:
cp /old_location/This\ is\ an\ \[ugly\] \file.tmp /new_location/This\ is\ an\ \[ugly\] \file.tmp
используйте это вместо этого при использовании #!/bin/bash
в вашем шеллскрипте:
cp "/old_location/This is an [ugly] file with extras $$$.tmp" "/new_location/Ugly file with extras $$$.tmp"
В вашем случае вы используете значения внутри переменной, но передаете переменную неправильно. Если переменная имеет пробелы, вы должны заключить ее в двойные кавычки:
var="/somedir/a\ file.png /somedir/another\ file.png"; cp "$var" /someotherdir/
Если вы не заключите его в двойные кавычки, это будет похоже на использование нескольких переменных, разделенных пробелами.