Tên tệp có dấu cách ngắt cho vòng lặp, tìm lệnh


34

Tôi có một tập lệnh tìm kiếm tất cả các tập tin trong nhiều thư mục con và lưu trữ để tar. Kịch bản của tôi là

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

Lệnh find cho tôi đầu ra sau

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

Nhưng biến FILE chỉ lưu trữ phần đầu tiên của đường dẫn ./F1/F1-2013-03-19 và sau đó là phần tiếp theo 160413.csv .

Tôi đã thử sử dụng readvới một vòng lặp while,

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

nhưng tôi nhận được lỗi sau

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

Bất cứ ai có thể đề nghị một cách khác?

Cập nhật

Như được đề xuất trong các câu trả lời dưới đây, tôi đã cập nhật các kịch bản

#!/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

Đầu ra mà tôi nhận được là

./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

4
Có vẻ như bạn nên đặt IFS=$'\n'trước vòng lặp `for để phân tích cú pháp theo từng dòng
kiri

Câu trả lời:


36

Sử dụng forvới findlà cách tiếp cận sai ở đây, xem ví dụ bài viết này về hộp giun bạn đang mở.

Cách tiếp cận được đề xuất là sử dụng find, whilereadnhư được mô tả ở đây . Dưới đây là một ví dụ nên làm việc cho bạn:

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

Bằng cách này, bạn phân định tên tệp bằng các \0ký tự null ( ), điều này có nghĩa là sự thay đổi trong không gian và các ký tự đặc biệt khác sẽ không gây ra vấn đề.

Để cập nhật kho lưu trữ với các tệp findđịnh vị, bạn có thể chuyển trực tiếp đầu ra của tệp tới tar:

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

Lưu ý rằng bạn không phải phân biệt giữa kho lưu trữ có tồn tại hay không, tarsẽ xử lý hợp lý. Cũng lưu ý việc sử dụng -printfở đây để tránh bao gồm ./bit trong kho lưu trữ.


Cảm ơn, nó gần như hoạt động. Điều duy nhất là nó lưu trữ ./như tar. ./.tar tar: ./archive.tar: file is the archive; not dumped
Ubuntuser

@Ubuntuser Bạn có thể thêm một kiểm tra đơn giản để xemif [[ "$FILE" == "./" ]]; then continue
kiri

@Ubuntuser: Bạn có thể tránh ./bit với -printfxem câu trả lời được cập nhật. Tuy nhiên, nó không nên tạo ra bất kỳ sự khác biệt nào nếu nó được bao gồm hay không vì nó chỉ tham chiếu thư mục hiện tại. Tôi cũng bao gồm một find/tarsự kết hợp thay thế mà bạn có thể muốn sử dụng.
Thor

Đối với những người bạn muốn sortkết quả trước khi lặp lại chúng, bạn sẽ cần sort -zđến dấu phân cách null.
Adambean

13

Hãy thử trích dẫn forvòng lặp như thế này:

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

Không có dấu ngoặc kép, bash hoàn toàn không xử lý dấu cách và dòng mới ( \n) ...

Cũng thử cài đặt

IFS=$'\n'

1
+1 cho $ IFS. Điều đó làm giảm tính cách của nhân vật.
Ray

1
Đây là giải pháp hiệu quả với tôi. Tôi đã sử dụng commđể so sánh các danh sách tập tin được sắp xếp và tên tệp có khoảng trắng trong đó, mặc dù trích dẫn các biến nó không hoạt động. Sau đó, tôi thấy cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html và giải pháp đặt $ IFS với IFS = $ (echo -en "\ n \ b") làm việc cho tôi.
pbhj

Bổ sung các dấu ngoặc kép, thanh lịch, đơn giản, đẹp - cảm ơn!
Big Rich

8

Điều này hoạt động và đơn giản hơn:

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

Tín dụng cho Rupa ( https://github.com/rupa/z ) cho câu trả lời này.


4

Ngoài trích dẫn thích hợp, bạn có thể yêu findcầu sử dụng dấu phân cách NULL, sau đó đọc và xử lý kết quả trong một whilevòng lặp

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

Điều này sẽ xử lý bất kỳ tên tệp nào tuân thủ POSIX - xem 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.

đây chỉ là giải pháp hiệu quả với tôi Cảm ơn.
tiền mã hóa


1

Tôi đã làm một cái gì đó như thế này để tìm các tập tin có thể chứa không gian.

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

Làm việc như người ở :)


0

Hầu hết các câu trả lời ở đây bị phá vỡ nếu có một ký tự dòng mới trong tên tệp. Tôi sử dụng bash hơn 15 năm, nhưng chỉ tương tác.

Trong Python, bạn có thể sử dụng os.walk (): http://docs.python.org/2/l Library / os.html # os.walk

Và mô-đun tarfile: http://docs.python.org/2/l Library / tarfile.html#tar-examples


0

Tôi nghĩ rằng bạn có thể tốt hơn bằng cách sử dụng findtùy chọn -exec.

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

Tìm sau đó thực thi lệnh bằng cách sử dụng một cuộc gọi hệ thống, để không gian và dòng mới được bảo toàn (chứ không phải là một đường ống, sẽ yêu cầu trích dẫn các ký tự đặc biệt). Lưu ý rằng "tar -c" hoạt động cho dù kho lưu trữ đã tồn tại hay chưa và (ít nhất là với bash) không {} hay + cần phải được trích dẫn.


-1

Như minerz029 đã đề xuất, bạn cần trích dẫn việc mở rộng findlệnh. Bạn cũng cần trích dẫn tất cả các thay thế $FILEtrong vòng lặp của bạn.

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

Lưu ý rằng $()cú pháp nên được ưu tiên sử dụng backticks; thấy U & L câu hỏi này . Tôi cũng đã xóa [[từ khóa và thay thế nó bằng [lệnh vì đó là POSIX.


Về [[[, có vẻ như [[là mới hơn và hỗ trợ nhiều tính năng hơn như kết hợp toàn cầu và regex. Tuy nhiên, [[chỉ trong bash, không phảish
kiri

@ minerz029 Có. Đó là những gì tôi đang nói. Tôi không biết ý của bạn là gì khi [[hỗ trợ Globing. Theo wiki của Greg , không có sự kiện nào xảy ra bên trong [[.
Joseph R.

Hãy thử [ "ab" == a? ] && echo "true"sau đó[[ "ab" == a? ]] && echo "true"
kiri

@ minerz029 Đó không phải là toàn cầu. Đây là những biểu thức chính quy (diễn giải lỏng lẻo). Đây không phải là một quả địa cầu vì a*có nghĩa là "theo sau bởi bất kỳ số lượng ký tự nào" chứ không phải "tất cả các tệp có tên bắt đầu avà có bất kỳ số lượng ký tự nào sau đó". Hãy thử [ ab = a* ] && echo true so với [[ ab == a* ]] && echo true.
Joseph R.

Ah tốt, [[vẫn không biểu hiện thường xuyên trong khi [không. Phải bối rối
kiri
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.