ฉันจะทำงานซ้ำ ๆ ผ่านไดเรคทอรีต้นไม้และรันคำสั่งเฉพาะในแต่ละไฟล์และออกพา ธ , ชื่อไฟล์, นามสกุล, ขนาดไฟล์และข้อความเฉพาะอื่น ๆ ไปยังไฟล์เดียวในไฟล์ทุบตี
ฉันจะทำงานซ้ำ ๆ ผ่านไดเรคทอรีต้นไม้และรันคำสั่งเฉพาะในแต่ละไฟล์และออกพา ธ , ชื่อไฟล์, นามสกุล, ขนาดไฟล์และข้อความเฉพาะอื่น ๆ ไปยังไฟล์เดียวในไฟล์ทุบตี
คำตอบ:
ในขณะที่การfind
แก้ปัญหานั้นง่ายและมีประสิทธิภาพฉันตัดสินใจที่จะสร้างโซลูชันที่ซับซ้อนมากขึ้นซึ่งขึ้นอยู่กับฟังก์ชั่นที่น่าสนใจนี้ซึ่งฉันเห็นเมื่อไม่กี่วันที่ผ่านมา
1.สร้างไฟล์สคริปต์ที่เรียกทำงานได้ซึ่งเรียกใช้walk
ซึ่งอยู่ใน/usr/local/bin
เพื่อให้สามารถเข้าถึงได้เป็นคำสั่งเชลล์:
sudo touch /usr/local/bin/walk
sudo chmod +x /usr/local/bin/walk
sudo nano /usr/local/bin/walk
nano
: Shift+ Insertเพื่อวาง; Ctrl+ OและEnterสำหรับการบันทึก; Ctrl+ Xสำหรับทางออก2.เนื้อหาของสคริปต์walk
คือ:
#!/bin/bash
# Colourise the output
RED='\033[0;31m' # Red
GRE='\033[0;32m' # Green
YEL='\033[1;33m' # Yellow
NCL='\033[0m' # No Color
file_specification() {
FILE_NAME="$(basename "${entry}")"
DIR="$(dirname "${entry}")"
NAME="${FILE_NAME%.*}"
EXT="${FILE_NAME##*.}"
SIZE="$(du -sh "${entry}" | cut -f1)"
printf "%*s${GRE}%s${NCL}\n" $((indent+4)) '' "${entry}"
printf "%*s\tFile name:\t${YEL}%s${NCL}\n" $((indent+4)) '' "$FILE_NAME"
printf "%*s\tDirectory:\t${YEL}%s${NCL}\n" $((indent+4)) '' "$DIR"
printf "%*s\tName only:\t${YEL}%s${NCL}\n" $((indent+4)) '' "$NAME"
printf "%*s\tExtension:\t${YEL}%s${NCL}\n" $((indent+4)) '' "$EXT"
printf "%*s\tFile size:\t${YEL}%s${NCL}\n" $((indent+4)) '' "$SIZE"
}
walk() {
local indent="${2:-0}"
printf "\n%*s${RED}%s${NCL}\n\n" "$indent" '' "$1"
# If the entry is a file do some operations
for entry in "$1"/*; do [[ -f "$entry" ]] && file_specification; done
# If the entry is a directory call walk() == create recursion
for entry in "$1"/*; do [[ -d "$entry" ]] && walk "$entry" $((indent+4)); done
}
# If the path is empty use the current, otherwise convert relative to absolute; Exec walk()
[[ -z "${1}" ]] && ABS_PATH="${PWD}" || cd "${1}" && ABS_PATH="${PWD}"
walk "${ABS_PATH}"
echo
3.คำอธิบาย:
กลไกหลักของwalk()
ฟังก์ชั่นได้อธิบายไว้สวยดีโดย Zanna ในตัวเธอคำตอบ ดังนั้นฉันจะอธิบายเฉพาะส่วนใหม่
ภายในwalk()
ฟังก์ชั่นฉันได้เพิ่มการวนซ้ำนี้:
for entry in "$1"/*; do [[ -f "$entry" ]] && file_specification; done
นั่นหมายความว่าแต่ละที่เป็นไฟล์ที่จะดำเนินการฟังก์ชั่น$entry
file_specification()
ฟังก์ชั่นfile_specification()
นี้มีสองส่วน ส่วนแรกได้รับข้อมูลที่เกี่ยวข้องกับไฟล์ - ชื่อเส้นทางขนาด ฯลฯ ส่วนที่สองส่งข้อมูลในรูปแบบที่ดี printf
การจัดรูปแบบข้อมูลที่มีการใช้คำสั่ง และถ้าคุณต้องการที่จะปรับแต่งสคริปต์ที่คุณควรอ่านเกี่ยวกับคำสั่งนี้ - ตัวอย่างเช่นบทความนี้
ฟังก์ชั่นfile_specification()
เป็นสถานที่ที่ดีที่คุณสามารถใส่คำสั่งที่ระบุว่าควรจะดำเนินการสำหรับแต่ละไฟล์ ใช้รูปแบบนี้:
คำสั่ง "$ {รายการ}"
หรือคุณสามารถบันทึกผลลัพธ์ของคำสั่งเป็นตัวแปรและจากนั้นprintf
ตัวแปรนี้ ฯลฯ :
MY_VAR = "$ ( คำสั่ง " $ {entry} ")" พิมพ์ "% * s \ t ขนาดไฟล์: \ t $ {YEL}% s $ {NCL} \ n" $ ((เยื้อง + 4)) '' "$ MY_VAR"
หรือprintf
ส่งออกคำสั่งโดยตรง:
printf "% * s \ t ขนาดไฟล์: \ t $ {YEL}% s $ {NCL} \ n" $ ((เยื้อง + 4)) '' "$ ( คำสั่ง " $ {รายการ} ")"
ส่วนของการขอทานเรียกว่าColourise the output
เริ่มต้นตัวแปรน้อยที่ใช้ภายในprintf
คำสั่งเพื่อให้สีออก เพิ่มเติมเกี่ยวกับเรื่องนี้คุณอาจพบว่าที่นี่
ที่ด้านล่างของใบเพิ่มเงื่อนไขเพิ่มเติมที่เกี่ยวข้องกับเส้นทางที่แน่นอนและสัมพันธ์
4.ตัวอย่างการใช้งาน:
วิธีเรียกใช้walk
สำหรับไดเรกทอรีปัจจุบัน:
walk # You shouldn't use any argument,
walk ./ # but you can use also this format
วิธีเรียกใช้walk
สำหรับไดเรกทอรีลูกใด ๆ :
walk <directory name>
walk ./<directory name>
walk <directory name>/<sub directory>
วิธีเรียกใช้walk
สำหรับไดเรกทอรีอื่น ๆ :
walk /full/path/to/<directory name>
ในการสร้างไฟล์ข้อความตามwalk
เอาต์พุต:
walk > output.file
ในการสร้างไฟล์เอาต์พุตที่ไม่มีรหัสสี ( แหล่งที่มา ):
walk | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" > output.file
5. การสาธิตการใช้งาน:
ฉันงุนงงเล็กน้อยว่าทำไมไม่มีใครโพสต์เลย แต่bash
มีความสามารถแบบเรียกซ้ำถ้าคุณเปิดใช้งานglobstar
ตัวเลือกและใช้**
glob ด้วยเหตุนี้คุณสามารถเขียนbash
สคริปต์บริสุทธิ์ (เกือบ) ที่ใช้ globstar แบบวนซ้ำได้ดังนี้:
#!/usr/bin/env bash
shopt -s globstar
for i in ./**/*
do
if [ -f "$i" ];
then
printf "Path: %s\n" "${i%/*}" # shortest suffix removal
printf "Filename: %s\n" "${i##*/}" # longest prefix removal
printf "Extension: %s\n" "${i##*.}"
printf "Filesize: %s\n" "$(du -b "$i" | awk '{print $1}')"
# some other command can go here
printf "\n\n"
fi
done
ขอให้สังเกตว่าที่นี่เราใช้การขยายตัวพารามิเตอร์ที่จะได้รับชิ้นส่วนของชื่อไฟล์ที่เราต้องการและเราไม่ได้อาศัยคำสั่งภายนอกยกเว้นสำหรับการขนาดไฟล์ที่มีและการทำความสะอาดการส่งออกด้วยdu
awk
และเมื่อมันผ่านทรีไดเรกทอรีของคุณผลลัพธ์ของคุณควรเป็นดังนี้:
Path: ./glibc/glibc-2.23/benchtests
Filename: sprintf-source.c
Extension: c
Filesize: 326
กฎระเบียบมาตรฐานของการใช้งานสคริปต์ใช้: ให้แน่ใจว่าเป็นปฏิบัติการด้วยchmod +x ./myscript.sh
และเรียกใช้จากไดเรกทอรีปัจจุบันผ่าน./myscript.sh
หรือวางไว้ในและเรียกใช้~/bin
source ~/.profile
"$(file "$i")"
(ในสคริปต์ด้านบนเป็นส่วนที่สองของ printf) จะกลับมา?
output the path, filename, extension, filesize
ดังนั้นคำตอบที่ตรงกับสิ่งที่ถาม :)
คุณสามารถใช้find
ในการทำงาน
find /path/ -type f -exec ls -alh {} \;
วิธีนี้จะช่วยคุณหากคุณต้องการแสดงรายการไฟล์ทั้งหมดที่มีขนาด
-exec
จะช่วยให้คุณสามารถรันคำสั่งหรือสคริปต์ที่กำหนดเองสำหรับแต่ละไฟล์ที่
\;
ใช้ในการวิเคราะห์ไฟล์ทีละไฟล์คุณสามารถใช้+;
หากคุณต้องการเชื่อมต่อไฟล์เหล่านั้น (หมายถึงชื่อไฟล์)
ด้วยfind
เท่านั้น
find /path/ -type f -printf "path:%h fileName:%f size:%kKB Some Text\n" > to_single_file
หรือคุณสามารถใช้ด้านล่างแทน:
find -type f -not -name "to_single_file" -execdir sh -c '
printf "%s %s %s %s Some Text\n" "$PWD" "${1#./}" "${1##*.}" $(stat -c %s "$1")
' _ {} \; > to_single_file
find -printf
) +1
*
ถ้าคุณรู้ว่าลึกต้นไม้เป็นวิธีที่ง่ายที่สุดจะใช้สัญลักษณ์แทน
เขียนทุกสิ่งที่คุณต้องการทำเป็นเชลล์สคริปต์หรือฟังก์ชัน
function thing() { ... }
จากนั้นเรียกใช้for i in *; do thing "$i"; done
, for i in */*; do thing "$i"; done
... ฯลฯ
ในฟังก์ชั่น / สคริปต์ของคุณคุณสามารถใช้การทดสอบอย่างง่าย ๆเพื่อแยกไฟล์ที่คุณต้องการใช้งานและทำสิ่งที่คุณต้องการ
$i
แทน
for i in */*
ทำงาน ทดสอบที่นี่:for i in */*; do printf "|%s|\n" "$i"; done
find
สามารถทำได้:
find ./ -type f -printf 'Size:%s\nPath:%H\nName:%f\n'
ลองดูman find
คุณสมบัติของไฟล์อื่น ๆ
หากคุณต้องการส่วนขยายจริงๆคุณสามารถเพิ่มสิ่งนี้:
find ./ -type f -printf 'Size:%s\nPath:%H\nName:%f\nExtension:' -exec sh -c 'echo "${0##*.}\n"' {} \;