bash lặp lại danh sách tập tin, trừ khi trống


33

Tôi nghĩ rằng điều này sẽ đơn giản - nhưng nó đang chứng minh phức tạp hơn tôi mong đợi.

Tôi muốn lặp qua tất cả các tệp của một loại cụ thể trong một thư mục, vì vậy tôi viết điều này:

#!/bin/bash

for fname in *.zip ; do
   echo current file is ${fname}
done

Điều này hoạt động miễn là có ít nhất một tệp phù hợp trong thư mục. Tuy nhiên nếu không có tệp phù hợp, tôi nhận được điều này:

current file is *.zip

Sau đó tôi đã thử:

#!/bin/bash

FILES=`ls *.zip`
for fname in "${FILES}" ; do
    echo current file is ${fname}
done

Mặc dù phần thân của vòng lặp không thực thi khi không có tệp, tôi gặp lỗi từ ls:

ls: *.zip: No such file or directory

Làm thế nào để tôi viết một vòng lặp xử lý sạch không có tệp phù hợp?


7
Thêm shopt -s nullglobtrước khi chạy vòng lặp for.
cuonglm

@cuolnglm: spookily kết quả này trong ls trả lại tên của kịch bản thực hiện chứ không phải là một danh sách trống trên hộp RHEL5 này (bash 3.2.25) nếu tôi làm FILES=ls * .zip ; for fname in "${FILES}"...nhưng nó làm việc như mong đợi vớifor fname in *.zip ; do....
symcbean

3
Sử dụng for file in *.zip, không `ls ...`. Đề xuất của @ cuonglm là để *.zipmở rộng thành không có gì khi mẫu không khớp với bất kỳ tệp nào. lskhông có đối số liệt kê các thư mục hiện tại.
Stéphane Chazelas

1
Câu hỏi này thảo luận về lý do tại sao phân tích cú pháp đầu ra lsthường được tránh: Tại sao không phân tích cú pháp ls? ; cũng thấy liên kết gần đầu trang đó với bài viết ParsingLs của BashGuide .
PM 2Ring

Câu trả lời:


34

Trong bash, bạn có thể đặt nullglobtùy chọn sao cho mẫu không khớp với "không biến mất", thay vì được coi là một chuỗi ký tự:

shopt -s nullglob
for fname in *.zip ; do
   echo "current file is ${fname}"
done

Trong tập lệnh shell POSIX, bạn chỉ cần xác minh fnametồn tại (và đồng thời với [ -f ], kiểm tra xem đó có phải là tệp thông thường (hoặc symlink đến tệp thông thường) không chứ không phải các loại khác như thư mục / fifo / thiết bị ...):

for fname in *.zip; do
    [ -f "$fname" ] || continue
    printf '%s\n' "current file is $fname"
done

Thay thế [ -f "$fname" ]bằng [ -e "$fname" ] || [ -L "$fname ]nếu bạn muốn lặp qua tất cả các tệp (không ẩn) có tên kết thúc .zipbất kể loại của chúng.

Thay thế *.zipbằng .*.zip .zip *.zipnếu bạn cũng muốn xem xét các tập tin ẩn có tên kết thúc .zip.


1
shopt -s nullglobkhông hoạt động với tôi trên Ubuntu 17.04, nhưng [ -f "$fname" ] || continuehoạt động tốt.
koppor

@koppor Có vẻ như bạn không thực sự sử dụng bash.
chepner

2
set ./*                               #set the arg array to glob results
${2+":"} [ -e "$1" ] &&               #if more than one result skip the stat "$1"
printf "current file is %s\n" "$@"    #print the whole array at once

###or###

${2+":"} [ -e "$1" ] &&               #same kind of test
for    fname                          #iterate singly on $fname var for array
do     printf "file is %s\n" "$fname" #print each $fname for each iteration
done                                  

Trong một bình luận ở đây, bạn đề cập đến việc gọi một chức năng ...

file_fn()
    if     [ -e "$1" ] ||               #check if first argument exists
           [ -L "$1" ]                  #or else if it is at least a broken link
    then   for  f                       #if so iterate on "$f"
           do : something w/ "$f"
           done
    else   command <"${1-/dev/null}"    #only fail w/ error if at least one arg
    fi

 file_fn *

2

Sử dụng tìm

export -f myshellfunc
find . -mindepth 1 -maxdepth 1 -type f -name '*.zip' -exec bash -c 'myshellfunc "$0"' {} \;

Bạn PHẢI xuất hàm shell của bạn export -fđể làm việc này. Bây giờ findthực bashthi chức năng shell của bạn và chỉ ở mức dir hiện tại.


Mà đệ quy thông qua các thư mục con và tôi muốn gọi một hàm bash (không phải tập lệnh) cho các kết quả khớp.
symcbean

@symcbean Tôi đã chỉnh sửa để giới hạn các thư mục đơn và xử lý các hàm bash
Dani_l

-3

Thay vì:

FILES=`ls *.zip`

Thử:

FILES=`ls * | grep *.zip`

Bằng cách này nếu ls thất bại (trong trường hợp của bạn), nó sẽ grep đầu ra không thành công và trả về dưới dạng một biến trống.

current file is      <---Blank Here

Bạn có thể thêm một số logic vào đây để làm cho nó trở lại "Không tìm thấy tệp"

#!/bin/bash

FILES=`ls * | grep *.zip`
if [[ $? == "0" ]]; then
    for fname in "$FILES" ; do
        echo current file is $fname
    done
else
    echo "No Files Found"
fi

Bằng cách này nếu lệnh trước thành công (thoát với giá trị 0) thì nó sẽ in tệp hiện tại, nếu không nó sẽ in "Không tìm thấy tệp"


1
Tôi nghĩ rằng nên thêm một quy trình ( grep) thay vì cố gắng khắc phục sự cố bằng cách sử dụng một công cụ tốt hơn ( find) hoặc thay đổi cài đặt có liên quan cho giải pháp hiện tại (với shopt -s nullglob)
Thomas Baruchel

1
Theo nhận xét của OP về bài viết gốc của họ, shopt -s nullglobnó không hoạt động. Tôi đã cố gắng findtrong khi xác minh câu trả lời của tôi và nó tiếp tục thất bại. Tôi nghĩ vì những điều xuất khẩu mà Dani nói.
Kip K
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.