Как скопировать имя файла / путь, содержащий пробелы в Unix? - PullRequest
0 голосов
/ 25 сентября 2019

Моя цель - найти файлы, содержащие шаблон, и скопировать его в папку.Файлы могут быть доступны во многих папках из папки (скажем / home / abc / logs / ....).Некоторые имена файлов и папок содержат специальные символы, включая пробелы.Я пытаюсь присвоить найденный результат переменной и скопировать его.Но результат «Список аргументов слишком длинный»

Сервер Linux

pattern="somesttring" ;
LOG_FILE_PATH="/path/to/logs/"
LOGS_TO_COPY=$(grep -Hrn "$pattern" "$LOG_FILE_PATH" | cut -d: -f1)
cp $LOGS_TO_COPY temp #temp is folder to which i have to copy 

Я получаю следующую ошибку:

cp $LOGS_TO_COPY temp/
-bash: /bin/cp: Argument list too long

1 Ответ

0 голосов
/ 25 сентября 2019

Не храните его в переменной, по крайней мере, в обычной строковой переменной.Строковая переменная не может сохранить разницу между пробелом внутри каждого аргумента и пробелом, разделяющим их.

grep -lr "$pattern" "$LOG_FILE_PATH" | while IFS= read -r file; do cp "$file" temp/; done

Также обратите внимание, что вам не нужно вырезать результат grep, так как *Флаг 1004 * дает вам только имена файлов, именно то, что вы хотели.-H - это значение по умолчанию, поэтому оно не нужно, а -n не нужно, если вы только собираетесь его отключить.

Это не будет работать, если у вас есть файлы, содержащие в своих именах символ новой строки.В качестве решения вы можете указать grepread) использовать NUL для разделителя.

grep -lr --null "$pattern" "$LOG_FILE_PATH" | while IFS= read -d $'\0' -r file; do cp "$file" temp/; done

В качестве альтернативы, вы можете использовать xargs вместо while + read:

grep -lr --null "$pattern" "$LOG_FILE_PATH" | xargs --null -I {} cp {} temp/

Если вам нужно сохранить его в переменной, используйте тот же шаблон, что и выше, чтобы сохранить его в переменной array .Таким образом, пробел не требуется в качестве разделителя аргументов.

declare -a files
while IFS= read -d $'\0' -r file; do files+=("$file"); done < <(grep -lr --null "$pattern" "$LOG_FILE_PATH")
cp "${files[@]}" temp/

Вот еще один способ сделать это (хотя это не будет работать, если имена файлов включают символы новой строки, так как mapfile не поддерживает изменение разделителя):

mapfile -t files < <(grep -lr "$pattern" "$LOG_FILE_PATH")
cp "${files[@]}" temp/

Если количество копируемых файлов очень велико, решения while и xargs все равно будут работать, но те, которые просто объединяют весь массив в одну команду cp, могут не работать.

...