Имена файлов с пробелами для цикла, команда find

У меня есть скрипт, который ищет все файлы в нескольких подпапках и архивах в tar. Мой скрипт

for FILE in `find . -type f -name '*.*'` do if [[ ! -f archive.tar ]]; then tar -cpf archive.tar $FILE else tar -upf archive.tar $FILE fi done 

Команда find дает мне следующий результат

 find . -type f -iname '*.*' ./F1/F1-2013-03-19 160413.csv ./F1/F1-2013-03-19 164411.csv ./F1-FAILED/F2/F1-2013-03-19 154412.csv ./F1-FAILED/F3/F1-2011-10-02 212910.csv ./F1-ARCHIVE/F1-2012-06-30 004408.csv ./F1-ARCHIVE/F1-2012-05-08 190408.csv 

Но переменная FILE сохраняет только первую часть пути ./F1/F1-2013-03-19, а затем следующую часть 160413.csv .

Я пытался использовать read с циклом while,

 while read `find . -type f -iname '*.*'`; do ls $REPLY; done 

но я получаю следующую ошибку

 bash: read: `./F1/F1-2013-03-19': not a valid identifier 

Может ли кто-нибудь предложить альтернативный путь?

Обновить

Как было предложено в ответах ниже, я обновил сценарии

 #!/bin/bash INPUT_DIR=/usr/local/F1 cd $INPUT_DIR for FILE in "$(find . -type f -iname '*.*')" do archive=archive.tar if [ -f $archive ]; then tar uvf $archive "$FILE" else tar -cvf $archive "$FILE" fi done 

Результат, который я получаю, это

 ./test.sh tar: ./F1/F1-2013-03-19 160413.csv\n./F1/F1-2013-03-19 164411.csv\n./F1/F1-2013-03-19 153413.csv\n./F1/F1-2013-03-19 154412.csv\n./F1/F1-2012-09-10 113409.csv\n./F1/F1-2013-03-19 152411.csv\n./.tar\n./F1-FAILED/F3/F1-2013-03-19 154412.csv\n./F1-FAILED/F3/F1-2013-03-19 170411.csv\n./F1-FAILED/F3/F1-2012-09-10 113409.csv\n./F1-FAILED/F2/F1-2011-10-03 113911.csv\n./F1-FAILED/F2/F1-2011-10-02 165908.csv\n./F1-FAILED/F2/F1-2011-10-02 212910.csv\n./F1-ARCHIVE/F1-2012-06-30 004408.csv\n./F1-ARCHIVE/F1-2011-08-17 133905.csv\n./F1-ARCHIVE/F1-2012-10-21 154410.csv\n./F1-ARCHIVE/F1-2012-05-08 190408.csv: Cannot stat: No such file or directory tar: Exiting with failure status due to previous errors 

Использование с помощью find – это неправильный подход здесь, см., Например, эту запись о банке червей, которую вы открываете.

Рекомендуемый подход – использовать find , while и read как описано здесь . Ниже приведен пример, который должен работать на вас:

 find . -type f -name '*.*' -print0 | while IFS= read -r -d '' file; do printf '%s\n' "$file" done 

Таким образом, вы делите имена файлов с нулевыми ( \0 ) символами, это означает, что изменение в пространстве и других специальных символах не вызовет проблем.

Чтобы обновить архив с файлами, которые find локации, вы можете передать его вывод непосредственно в tar :

 find . -type f -name '*.*' -printf '%p\0' | tar --null -uf archive.tar -T - 

Обратите внимание, что вам не нужно различать, существует ли архив или нет, tar будет обрабатывать его разумно. Также обратите внимание на использование -printf здесь, чтобы не включать бит ./ в архив.

Попробуйте процитировать цикл for следующим образом:

 for FILE in "`find . -type f -name '*.*'`" # note the quotation marks 

Без кавычек bash не обрабатывает пробелы и символы новой строки ( \n ) вообще …

Также попробуйте установить

 IFS=$'\n' 

Это работает и проще:

 find . -name '<pattern>' | while read LINE; do echo "$LINE" ; done 

Благодарим Рупу ( https://github.com/rupa/z ) за этот ответ.

В дополнение к правильному цитированию вы можете сказать, что find использовать разделитель NULL, а затем читать и обрабатывать результаты в цикле while

 while read -rd $'\0' file; do something with "$file" done < <(find . -type f -name '*.*' -print0) 

Это должно обрабатывать любые имена файлов, совместимые с POSIX, – см. man find

  -print0 True; print the full file name on the standard output, followed by a null character (instead of the newline character that -print uses). This allows file names that contain newlines or other types of white space to be correctly interpreted by programs that process the find output. This option corresponds to the -0 option of xargs. 
 find . <find arguments> -print0 | xargs -0 grep <pattern> 

Я сделал что-то подобное, чтобы найти файлы, которые могут содержать пробелы.

 IFS=$'\n' for FILE in `/usr/bin/find $DST/shared -name *.nsf | grep -v bookmark.nsf | grep -v names.nsf`; do file $FILE | tee -a $LOG done 

Работал как шарм 🙂

Большинство ответов здесь разбиваются, если в имени файла есть символ новой строки. Я использую bash более 15 лет, но только интерактивный.

В Python вы можете os.walk (): http://docs.python.org/2/library/os.html#os.walk

И модуль tarfile: http://docs.python.org/2/library/tarfile.html#tar-examples

Я думаю, вам может быть лучше использовать опцию find -s -exec.

 find . -type f -name '*.*' -exec tar -cpf archive.tar {} + 

Затем команда выполняет команду с использованием системного вызова, так что сохраняются пробелы и символы новой строки (скорее, это канал, для которого требуется котирование специальных символов). Обратите внимание, что «tar -c» работает независимо от того, существует ли архив уже и что (по крайней мере, с bash) ни {}, ни + не нужно указывать.

Как предложил minerz029, вам нужно указать расширение команды find . Вам также нужно процитировать все подстановки $FILE в вашем цикле.

 for FILE in "$(find . -type f -name '*.*')" do if [ ! -f archive.tar ]; then tar -cpf archive.tar "$FILE" else tar -upf archive.tar "$FILE" fi done 

Обратите внимание, что синтаксис $() должен быть предпочтительнее использования обратных циклов; см. этот вопрос U & L. Я также удалил [[ ключевое слово и заменил его на [ команда, потому что это POSIX.