บทนำ
เคยอยากแสดงรายการไฟล์ทั้งหมดในไดเรกทอรี แต่แค่ไฟล์อย่างเดียว ไม่ใช่สิ่งอื่น? หรือแค่ไดเรกทอรี? หากเคย สคริปต์ต่อไปนี้ซึ่งเป็นโอเพนซอร์สภายใต้ GPLv3 อาจเป็นสิ่งที่คุณหา
แน่นอน คุณสามารถใช้คำสั่ง find:
find . -maxdepth 1 -type f -print
แต่มันพิมพ์ยุ่งยาก ให้เอาต์พุทที่ไม่เป็นมิตร และขาดความละเอียดของคำสั่ง ls คุณอาจรวม ls และ grep เพื่อผลลัพธ์เดียวกัน:
ls -F . | grep -v /
แต่มันก็ยุ่งยากเช่นกัน สคริปต์นี้ให้ทางเลือกที่เรียบง่าย
การใช้งาน
สคริปต์ให้ฟังก์ชันหลัก 4 อย่าง ขึ้นอยู่กับชื่อที่คุณเรียก: lsf แสดงไฟล์ lsd แสดงไดเรกทอรี lsx แสดงไฟล์ executable และ lsl แสดง links
ไม่ต้องติดตั้งสคริปต์หลายๆ ไฟล์ เพราะ symbolic links ทำงานได้ ประหยัดพื้นที่และทำให้อัปเดตสคริปต์ง่ายขึ้น
สคริปต์ทำงานโดยใช้คำสั่ง find ค้นหา แล้วรัน ls บนแต่ละไอเท็มที่พบ สิ่งดีคืออาร์กิวเมนต์ที่ส่งให้สคริปต์จะส่งต่อให้คำสั่ง ls ดังนั้น เช่น นี้แสดงไฟล์ทั้งหมด รวมถึงที่ขึ้นต้นด้วยจุด:
lsf -a
เพื่อแสดงไดเรกทอรีในรูปแบบ long format ใช้คำสั่ง lsd:
lsd -l
คุณสามารถระบุอาร์กิวเมนต์หลายตัว และเส้นทางไฟล์/ไดเรกทอรีได้
คำสั่งนี้ให้ long classified listing ของไฟล์ทั้งหมดในไดเรกทอรี parent ของปัจจุบัน และใน /usr/bin:
lsf -F -l .. /usr/bin
หนึ่งอย่างที่สคริปต์ไม่จัดการตอนนี้คือ recursion คำสั่งนี้แสดงแค่ไฟล์ในไดเรกทอรีปัจจุบัน:
lsf -R
สคริปต์ไม่ลงไปใน subdirectories นี่อาจแก้ไขในอนาคต
ภายในสคริปต์
สคริปต์เขียนในรูปแบบ top-down พร้อมฟังก์ชันเริ่มต้นที่ต้น และส่วนทำงานจริงอยู่ท้าย มีเพียงสองฟังก์ชันที่สำคัญจริงๆ ฟังก์ชัน parse_args() แยกวิเคราะห์บรรทัดคำสั่ง แยก options จาก pathnames และ options เฉพาะของสคริปต์จากตัวเลือกของ ls
ฟังก์ชัน list_things_in_dir() รับชื่อไดเรกทอรีเป็นอาร์กิวเมนต์ และรันคำสั่ง find บนมัน แต่ละไอเท็มที่พบส่งให้ ls แสดง
สรุป
นี่คือสคริปต์เรียบง่ายสำหรับฟังก์ชันเรียบง่าย มันประหยัดเวลาและมีประโยชน์น่าประทับใจเมื่อทำงานกับระบบไฟล์ขนาดใหญ่
สคริปต์
#!/bin/bash
# สคริปต์สำหรับแสดงรายการ:
# ไดเรกทอรี (หากเรียกว่า "lsd")
# ไฟล์ (หากเรียกว่า "lsf")
# links (หากเรียกว่า "lsl")
# หรือ executables (หากเรียกว่า "lsx")
# แต่ไม่ใช่ประเภท filesystem object อื่นๆ
# FIXME: add lsp (list pipes)
#
# การใช้งาน:
# <command_name> [switches valid for ls command] [dirname...]
#
# ทำงานกับชื่อที่มี space และขึ้นต้นด้วย hyphen
#
# สร้างโดย Nick Clifton
# Version 1.4
# Copyright (c) 2006, 2007 Red Hat.
#
# นี่เป็นซอฟต์แวร์เสรี คุณสามารถกระจายซ้ำและ/หรือปรับเปลี่ยนภายใต้
# ข้อกำหนดของ GNU General Public License ตามที่เผยแพร่
# โดย Free Software Foundation; ไม่ว่าจะเป็น version 3 หรือ (ตาม
# ตัวเลือกของคุณ) เวอร์ชันใดๆ ที่ออกมาในภายหลัง
#
# เผยแพร่ใน希望ว่าจะมีประโยชน์ แต่ไม่มีการรับประกันใดๆ
# แม้แต่การรับประกันโดยนัยของความสามารถในการขายหรือความเหมาะสม
# สำหรับวัตถุประสงค์เฉพาะ ดู GNU General Public License
# เพื่อรายละเอียดเพิ่มเติม
# ToDo:
# Handle recursion, eg: lsl -R
# Handle switches that take arguments, eg --block-size
# Handle --almost-all, --ignore-backups, --format and --ignore
main ()
{
init
parse_args ${1+"$@"}
list_objects
exit 0
}
report ()
{
echo $prog": " ${1+"$@"}
}
fail ()
{
report " Internal error: " ${1+"$@"}
exit 1
}
# Initialise global variables.
init ()
{
# Default to listing things in the current directory.
dirs[0]=".";
# num_dirs is the number of directories to be listed minus one.
# This is because we are indexing the dirs[] array from zero.
num_dirs=0;
# Default to ignoring things that start with a period.
no_dots=1
# Note - the global variables 'type' and 'opts' are initialised in
# parse_args function.
}
# Parse our command line
parse_args ()
{
local no_more_args
no_more_args=0 ;
prog=`basename $0` ;
# Decide if we are listing files or directories.
case $prog in
lsf | lsf.sh)
type=f
opts="";
;;
lsd | lsd.sh)
type=d
# The -d switch to "ls" is presumed when listing directories.
opts="-d";
;;
lsl | lsl.sh)
type=l
# Use -d to prevent the listed links from being followed.
opts="-d";
;;
lsx | lsx.sh)
type=f
find_extras="-perm /111"
;;
*)
fail "Unrecognised program name: '$prog', expected either 'lsd', 'lsf', 'lsl' or 'lsx'"
;;
esac
# Locate any additional command line switches for ls and accumulate them.
# Likewise accumulate non-switches to the directories list.
while [ $# -gt 0 ]
do
case "$1" in
# FIXME: Handle switches that take arguments, eg --block-size
# FIXME: Properly handle --almost-all, --ignore-backups, --format
# FIXME: and --ignore
# FIXME: Properly handle --recursive
-a | -A | --all | --almost-all)
no_dots=0;
;;
--version)
report "version 1.2"
exit 0
;;
--help)
case $type in
d) report "a version of 'ls' that lists only directories" ;;
l) report "a version of 'ls' that lists only links" ;;
f) if [ "x$find_extras" = "x" ] ; then
report "a version of 'ls' that lists only files" ;
else
report "a version of 'ls' that lists only executables";
fi ;;
esac
exit 0
;;
--)
# A switch to say that all further items on the command line are
# arguments and not switches.
no_more_args=1 ;
;;
-*)
if [ "x$no_more_args" = "x1" ] ;
then
dirs[$num_dirs]="$1";
let "num_dirs++"
else
# Check for a switch that just uses a single dash, not a double
# dash. This could actually be multiple switches combined into
# one word, eg "lsd -alF". In this case, scan for the -a switch.
# XXX: FIXME: The use of =~ requires 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
# Remember that we are counting from zero not one.
if [ $num_dirs -gt 0 ] ;
then
let "num_dirs--"
fi
}
list_things_in_dir ()
{
local dir
# Paranoia checks - the user should never encounter these.
if test "x$1" = "x" ;
then
fail "list_things_in_dir called without an argument"
fi
if test "x$2" != "x" ;
then
fail "list_things_in_dir called with too many arguments"
fi
# Use quotes when accessing $dir in order to preserve
# any spaces that might be in the directory name.
dir="${dirs[$1]}";
# Catch directory names that start with a dash - they
# confuse 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
# Use pushd rather passing the directory name to find so that the
# names that find passes on to xargs do not have any paths prepended.
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 "directory '$dir' could not be found"
fi
}
list_objects ()
{
local i
i=0;
while [ $i -le $num_dirs ]
do
list_things_in_dir i
let "i++"
done
}
# Invoke main
main ${1+"$@"}