Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Lập Trình Bash

Dễ dàng liệt kê file hoặc thư mục trên hệ thống file lớn với script Bash này

Bạn có bao giờ muốn liệt kê tất cả file trong thư mục, nhưng chỉ file, không có gì khác? Hay chỉ thư mục? Nếu có, script sau, mã nguồn mở theo GPLv3, có thể là thứ bạn tìm kiếm.

Tất nhiên, bạn có thể dùng lệnh find:

find . -maxdepth 1 -type f -print

Nhưng gõ phiền, output không thân thiện, và thiếu tinh tế của lệnh ls. Bạn cũng có thể kết hợp ls và grep:

ls -F . | grep -v /

Nhưng cũng cồng kềnh. Script này cung cấp giải pháp đơn giản.

Sử dụng

Script cung cấp bốn hàm chính, phụ thuộc vào tên bạn gọi: lsf liệt kê file, lsd liệt kê thư mục, lsx liệt kê file thực thi, và lsl liệt kê link.

Không cần cài nhiều bản sao script, vì symbolic link hoạt động. Tiết kiệm dung lượng và cập nhật script dễ dàng.

Script dùng lệnh find tìm kiếm, rồi chạy ls trên mỗi mục tìm được. Điều hay là mọi tham số cho script được truyền cho lệnh ls. Ví dụ, liệt kê tất cả file, kể cả file bắt đầu bằng dấu chấm:

lsf -a

Để liệt kê thư mục định dạng dài, dùng lsd:

lsd -l

Bạn có thể đưa nhiều tham số, và cả đường dẫn file/thư mục.

Lệnh này liệt kê phân loại chi tiết tất cả file trong thư mục cha của thư mục hiện tại, và thư mục /usr/bin:

lsf -F -l .. /usr/bin

Một việc script chưa xử lý là đệ quy. Lệnh này chỉ liệt kê file trong thư mục hiện tại:

lsf -R

Script không đi vào thư mục con. Có thể sửa sau.

Bên trong

Script viết theo kiểu top-down với hàm khởi tạo ở đầu và phần làm việc chính ở cuối. Chỉ có hai hàm thực sự quan trọng. Hàm parse_args() duyệt dòng lệnh, tách tùy chọn và tên đường dẫn, và tách tùy chọn riêng của script từ tùy chọn dòng lệnh ls.

Hàm list_things_in_dir() nhận tên thư mục làm tham số và chạy find trên nó. Mỗi mục tìm được truyền cho ls để hiển thị.

Kết luận

Đây là script đơn giản thực hiện chức năng đơn giản. Tiết kiệm thời gian và có thể hữu ích bất ngờ khi làm việc với hệ thống file lớn.

Script

#!/bin/bash

# Script liệt kê:
# thư mục (nếu gọi "lsd")
# file (nếu gọi "lsf")
# link (nếu gọi "lsl")
# hoặc file thực thi (nếu gọi "lsx")
# không phải loại đối tượng filesystem khác.
# FIXME: thêm lsp (liệt kê pipe)
#
# Cách dùng:
# <tên_lệnh> [tùy chọn hợp lệ cho ls] [tên_thư_mục...]
#
# Hoạt động với tên chứa khoảng trắng và bắt đầu bằng dấu gạch ngang.
#
# Tác giả: Nick Clifton.
# Phiên bản 1.4
# Bản quyền (c) 2006, 2007 Red Hat.
#
# Phần mềm tự do; bạn có thể phân phối lại và/hoặc sửa đổi
# theo Giấy phép Công cộng GNU phiên bản 3, hoặc (tùy chọn)
# bất kỳ phiên bản sau.

# It is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.

# ToDo:
# Xử lý đệ quy, vd: lsl -R
# Xử lý switch có tham số, vd --block-size
# Xử lý --almost-all, --ignore-backups, --format và --ignore

main ()
{
init

parse_args ${1+"$@"}

list_objects

exit 0
}

report ()
{
echo $prog": " ${1+"$@"}
}

fail ()
{
report " Internal error: " ${1+"$@"}
exit 1
}

# Khởi tạo biến toàn cục.
init ()
{
# Mặc định liệt kê thứ trong thư mục hiện tại.
dirs[0]=".";

# num_dirs là số thư mục sẽ liệt kê trừ một.
# Vì index mảng dirs[] từ không.
num_dirs=0;

# Mặc định bỏ qua thứ bắt đầu bằng dấu chấm.
no_dots=1

# Lưu ý - biến toàn cục 'type' và 'opts' khởi tạo trong
# hàm parse_args.
}

# Phân tích dòng lệnh
parse_args ()
{
local no_more_args

no_more_args=0 ;

prog=`basename $0` ;

# Quyết định liệt kê file hay thư mục.
case $prog in
lsf | lsf.sh)
type=f
opts="";
;;
lsd | lsd.sh)
type=d
# Switch -d cho "ls" được giả định khi liệt kê thư mục.
opts="-d";
;;
lsl | lsl.sh)
type=l
# Dùng -d để ngăn link được follow.
opts="-d";
;;
lsx | lsx.sh)
type=f
find_extras="-perm /111"
;;
*)
fail "Tên chương trình không nhận diện: '$prog', kỳ vọng 'lsd', 'lsf', 'lsl' hoặc 'lsx'"
;;
esac

# Tìm thêm switch dòng lệnh cho ls và gom lại.
# Tương tự gom non-switch vào danh sách thư mục.
while [ $# -gt 0 ]
do
case "$1" in
# FIXME: Xử lý switch có tham số, vd --block-size
# FIXME: Xử lý đúng --almost-all, --ignore-backups, --format
# FIXME: và --ignore
# FIXME: Xử lý đúng --recursive
-a | -A | --all | --almost-all)
no_dots=0;
;;
--version)
report "version 1.2"
exit 0
;;
--help)
case $type in
d) report "phiên bản 'ls' chỉ liệt kê thư mục" ;;
l) report "phiên bản 'ls' chỉ liệt kê link" ;;
f) if [ "x$find_extras" = "x" ] ; then
report "phiên bản 'ls' chỉ liệt kê file" ;
else
report "phiên bản 'ls' chỉ liệt kê file thực thi";
fi ;;
esac
exit 0
;;
--)
# Switch nói rằng tất cả mục tiếp theo là tham số không phải switch.
no_more_args=1 ;
;;
-*)
if [ "x$no_more_args" = "x1" ] ;
then
dirs[$num_dirs]="$1";
let "num_dirs++"
else
# Kiểm tra switch chỉ dùng một gạch ngang, không phải hai.
# Thực tế có thể nhiều switch gom thành một từ, vd "lsd -alF".
# Quét tìm switch -a.
# XXX: FIXME: =~ yêu cầu bash v3.0+.
if [[ "x${1:1:1}" != "x-" && "x$1" =~ "x-.*a.*" ]] ;
then
no_dots=0;
fi
opts="$opts $1";
fi
;;
*)
dirs[$num_dirs]="$1";
let "num_dirs++"
;;
esac
shift
done

# Nhớ đếm từ không không phải một.
if [ $num_dirs -gt 0 ] ;
then
let "num_dirs--"
fi
}

list_things_in_dir ()
{
local dir

# Kiểm tra paranoia - user không bao giờ gặp.
if test "x$1" = "x" ;
then
fail "list_things_in_dir gọi không có tham số"
fi

if test "x$2" != "x" ;
then
fail "list_things_in_dir gọi với quá nhiều tham số"
fi

# Dùng ngoặc khi truy cập $dir để giữ
# khoảng trắng có thể có trong tên thư mục.
dir="${dirs[$1]}";

# Bắt tên thư mục bắt đầu bằng gạch ngang - chúng
# làm nhầm pushd.
if test "x${dir:0:1}" = "x-" ;
then
dir="./$dir"
fi

if [ -d "$dir" ]
then
if [ $num_dirs -gt 0 ]
then
echo " $dir:"
fi

# Dùng pushd thay vì truyền tên thư mục cho find để
# tên find truyền cho xargs không có đường dẫn tiền tố.
pushd "$dir" > /dev/null
if [ $no_dots -ne 0 ] ; then
find . -maxdepth 1 -type $type $find_extras -not -name ".*" -printf "%f\000" \
| xargs --null --no-run-if-empty ls $opts -- ;
else
find . -maxdepth 1 -type $type $find_extras -printf "%f\000" \
| xargs --null --no-run-if-empty ls $opts -- ;
fi
popd > /dev/null
else
report "thư mục '$dir' không tìm thấy"
fi
}

list_objects ()
{
local i

i=0;
while [ $i -le $num_dirs ]
do
list_things_in_dir i
let "i++"
done
}

# Gọi main
main ${1+"$@"}